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 "ogrlibjsonutils.h"
18 : #include "ogrsqliteutility.h"
19 : #include "cpl_md5.h"
20 : #include "cpl_multiproc.h" // CPLSleep()
21 : #include "cpl_time.h"
22 : #include "ogr_p.h"
23 : #include "sqlite_rtree_bulk_load/wrapper.h"
24 : #include "gdal_priv_templates.hpp"
25 :
26 : #include <algorithm>
27 : #include <cassert>
28 : #include <cerrno>
29 : #include <cinttypes>
30 : #include <climits>
31 : #include <cmath>
32 : #include <limits>
33 : #include <mutex>
34 :
35 : #undef SQLITE_STATIC
36 : #define SQLITE_STATIC static_cast<sqlite3_destructor_type>(nullptr)
37 : #undef SQLITE_TRANSIENT
38 : #define SQLITE_TRANSIENT reinterpret_cast<sqlite3_destructor_type>(-1)
39 :
40 : static const char UNSUPPORTED_OP_READ_ONLY[] =
41 : "%s : unsupported operation on a read-only datasource.";
42 :
43 : //----------------------------------------------------------------------
44 : // SaveExtent()
45 : //
46 : // Write the current contents of the layer envelope down to the
47 : // gpkg_contents metadata table.
48 : //
49 5956 : OGRErr OGRGeoPackageTableLayer::SaveExtent()
50 : {
51 5956 : if (!m_poDS->GetUpdate() || !m_bExtentChanged || !m_poExtent)
52 5300 : return OGRERR_NONE;
53 :
54 656 : sqlite3 *poDb = m_poDS->GetDB();
55 :
56 656 : if (!poDb)
57 0 : return OGRERR_FAILURE;
58 :
59 : char *pszSQL =
60 656 : sqlite3_mprintf("UPDATE gpkg_contents SET "
61 : "min_x = %.17g, min_y = %.17g, "
62 : "max_x = %.17g, max_y = %.17g "
63 : "WHERE lower(table_name) = lower('%q') AND "
64 : "Lower(data_type) = 'features'",
65 656 : m_poExtent->MinX, m_poExtent->MinY, m_poExtent->MaxX,
66 656 : m_poExtent->MaxY, m_pszTableName);
67 :
68 656 : OGRErr err = SQLCommand(poDb, pszSQL);
69 656 : sqlite3_free(pszSQL);
70 656 : m_bExtentChanged = false;
71 :
72 656 : return err;
73 : }
74 :
75 : //----------------------------------------------------------------------
76 : // SaveTimestamp()
77 : //
78 : // Update the last_change column of the gpkg_contents metadata table.
79 : //
80 5951 : OGRErr OGRGeoPackageTableLayer::SaveTimestamp()
81 : {
82 5951 : if (!m_poDS->GetUpdate() || !m_bContentChanged)
83 5072 : return OGRERR_NONE;
84 :
85 879 : m_bContentChanged = false;
86 :
87 879 : OGRErr err = m_poDS->UpdateGpkgContentsLastChange(m_pszTableName);
88 :
89 : #ifdef ENABLE_GPKG_OGR_CONTENTS
90 879 : if (m_bIsTable && err == OGRERR_NONE && m_poDS->m_bHasGPKGOGRContents &&
91 870 : !m_bOGRFeatureCountTriggersEnabled && m_nTotalFeatureCount >= 0)
92 : {
93 1636 : CPLString osFeatureCount;
94 818 : osFeatureCount.Printf(CPL_FRMT_GIB, m_nTotalFeatureCount);
95 818 : char *pszSQL = sqlite3_mprintf("UPDATE gpkg_ogr_contents SET "
96 : "feature_count = %s "
97 : "WHERE lower(table_name) = lower('%q')",
98 : osFeatureCount.c_str(), m_pszTableName);
99 818 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
100 818 : sqlite3_free(pszSQL);
101 : }
102 : #endif
103 :
104 879 : return err;
105 : }
106 :
107 : //----------------------------------------------------------------------
108 : // UpdateExtent()
109 : //
110 : // Expand the layer envelope if necessary to reflect the bounds
111 : // of new features being added to the layer.
112 : //
113 262971 : OGRErr OGRGeoPackageTableLayer::UpdateExtent(const OGREnvelope *poExtent)
114 : {
115 262971 : if (!m_poExtent)
116 : {
117 617 : m_poExtent = std::make_unique<OGREnvelope>(*poExtent);
118 : }
119 262971 : m_poExtent->Merge(*poExtent);
120 262971 : m_bExtentChanged = true;
121 262971 : return OGRERR_NONE;
122 : }
123 :
124 : //----------------------------------------------------------------------
125 : // BuildColumns()
126 : //
127 : // Save a list of columns (fid, geometry, attributes) suitable
128 : // for use in a SELECT query that retrieves all fields.
129 : //
130 26887 : OGRErr OGRGeoPackageTableLayer::BuildColumns()
131 : {
132 26887 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
133 0 : return OGRERR_FAILURE;
134 :
135 26887 : if (!m_bFeatureDefnCompleted)
136 27 : GetLayerDefn();
137 :
138 26887 : m_anFieldOrdinals.resize(m_poFeatureDefn->GetFieldCount());
139 26887 : int iCurCol = 0;
140 :
141 : /* Always start with a primary key */
142 26887 : CPLString soColumns;
143 26887 : if (m_bIsTable || m_pszFidColumn != nullptr)
144 : {
145 26873 : soColumns += "m.";
146 26873 : soColumns += m_pszFidColumn
147 53746 : ? "\"" + SQLEscapeName(m_pszFidColumn) + "\""
148 26873 : : "_rowid_";
149 26873 : m_iFIDCol = iCurCol;
150 26873 : iCurCol++;
151 : }
152 :
153 : /* Add a geometry column if there is one (just one) */
154 26887 : if (m_poFeatureDefn->GetGeomFieldCount())
155 : {
156 26663 : const auto poFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
157 26663 : if (poFieldDefn->IsIgnored())
158 : {
159 10 : m_iGeomCol = -1;
160 : }
161 : else
162 : {
163 26653 : if (!soColumns.empty())
164 26642 : soColumns += ", ";
165 26653 : soColumns += "m.\"";
166 26653 : soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
167 26653 : soColumns += "\"";
168 26653 : m_iGeomCol = iCurCol;
169 26653 : iCurCol++;
170 : }
171 : }
172 :
173 : /* Add all the attribute columns */
174 38930 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
175 : {
176 12043 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
177 12043 : if (poFieldDefn->IsIgnored())
178 : {
179 40 : m_anFieldOrdinals[i] = -1;
180 : }
181 : else
182 : {
183 12003 : if (!soColumns.empty())
184 12002 : soColumns += ", ";
185 12003 : soColumns += "m.\"";
186 12003 : soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
187 12003 : soColumns += "\"";
188 12003 : m_anFieldOrdinals[i] = iCurCol;
189 12003 : iCurCol++;
190 : }
191 : }
192 :
193 26887 : if (soColumns.empty())
194 : {
195 : // Can happen if ignoring all fields on a view...
196 2 : soColumns = "NULL";
197 : }
198 26887 : m_soColumns = std::move(soColumns);
199 26887 : return OGRERR_NONE;
200 : }
201 :
202 : //----------------------------------------------------------------------
203 : // IsGeomFieldSet()
204 : //
205 : // Utility method to determine if there is a non-Null geometry
206 : // in an OGRGeometry.
207 : //
208 268817 : bool OGRGeoPackageTableLayer::IsGeomFieldSet(OGRFeature *poFeature)
209 : {
210 534251 : return poFeature->GetDefnRef()->GetGeomFieldCount() &&
211 534251 : poFeature->GetGeomFieldRef(0);
212 : }
213 :
214 268840 : OGRErr OGRGeoPackageTableLayer::FeatureBindParameters(
215 : OGRFeature *poFeature, sqlite3_stmt *poStmt, int *pnColCount, bool bAddFID,
216 : bool bBindUnsetFields, int nUpdatedFieldsCount,
217 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
218 : const int * /*panUpdatedGeomFieldsIdx*/)
219 : {
220 268840 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
221 :
222 268840 : int nColCount = 1;
223 268840 : if (bAddFID)
224 : {
225 67529 : int err = sqlite3_bind_int64(poStmt, nColCount++, poFeature->GetFID());
226 67529 : if (err != SQLITE_OK)
227 : {
228 0 : CPLError(CE_Failure, CPLE_AppDefined,
229 : "sqlite3_bind_int64() failed");
230 0 : return OGRERR_FAILURE;
231 : }
232 : }
233 :
234 : // Bind data values to the statement, here bind the blob for geometry.
235 : // We bind only if there's a geometry column (poFeatureDefn->GetGeomFieldCount() > 0)
236 : // and if we are:
237 : // - either in CreateFeature/SetFeature mode: nUpdatedGeomFieldsCount < 0
238 : // - or in UpdateFeature mode with nUpdatedGeomFieldsCount == 1, which
239 : // implicitly involves that panUpdatedGeomFieldsIdx[0] == 0, so we don't
240 : // need to test this condition.
241 537672 : if ((nUpdatedGeomFieldsCount < 0 || nUpdatedGeomFieldsCount == 1) &&
242 268832 : poFeatureDefn->GetGeomFieldCount())
243 : {
244 : // Non-NULL geometry.
245 265447 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
246 265447 : if (poGeom)
247 : {
248 264018 : size_t szWkb = 0;
249 528036 : GByte *pabyWkb = GPkgGeometryFromOGR(poGeom, m_iSrs,
250 264018 : &m_sBinaryPrecision, &szWkb);
251 264018 : if (!pabyWkb)
252 0 : return OGRERR_FAILURE;
253 264018 : int err = sqlite3_bind_blob(poStmt, nColCount++, pabyWkb,
254 : static_cast<int>(szWkb), CPLFree);
255 264018 : if (err != SQLITE_OK)
256 : {
257 0 : if (err == SQLITE_TOOBIG)
258 : {
259 0 : CPLError(CE_Failure, CPLE_AppDefined,
260 : "sqlite3_bind_blob() failed: too big");
261 : }
262 : else
263 : {
264 0 : CPLError(CE_Failure, CPLE_AppDefined,
265 : "sqlite3_bind_blob() failed");
266 : }
267 0 : return OGRERR_FAILURE;
268 : }
269 264018 : CreateGeometryExtensionIfNecessary(poGeom);
270 : }
271 : /* NULL geometry */
272 : else
273 : {
274 1429 : int err = sqlite3_bind_null(poStmt, nColCount++);
275 1429 : if (err != SQLITE_OK)
276 : {
277 0 : CPLError(CE_Failure, CPLE_AppDefined,
278 : "sqlite3_bind_null() failed");
279 0 : return OGRERR_FAILURE;
280 : }
281 : }
282 : }
283 :
284 : /* Bind the attributes using appropriate SQLite data types */
285 268840 : const int nFieldCount = poFeatureDefn->GetFieldCount();
286 :
287 268840 : size_t nInsertionBufferPos = 0;
288 268840 : if (m_osInsertionBuffer.empty())
289 29502 : m_osInsertionBuffer.resize(OGR_SIZEOF_ISO8601_DATETIME_BUFFER *
290 : nFieldCount);
291 :
292 4737050 : for (int idx = 0;
293 4737050 : idx < (nUpdatedFieldsCount < 0 ? nFieldCount : nUpdatedFieldsCount);
294 : idx++)
295 : {
296 4468210 : const int iField =
297 4468210 : nUpdatedFieldsCount < 0 ? idx : panUpdatedFieldsIdx[idx];
298 4468210 : assert(iField >= 0);
299 4468210 : const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(iField);
300 4468210 : if (iField == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
301 1287 : continue;
302 4468180 : if (!poFeature->IsFieldSetUnsafe(iField))
303 : {
304 1258 : if (bBindUnsetFields)
305 : {
306 1244 : int err = sqlite3_bind_null(poStmt, nColCount++);
307 1244 : if (err != SQLITE_OK)
308 : {
309 0 : CPLError(CE_Failure, CPLE_AppDefined,
310 : "sqlite3_bind_null() failed");
311 0 : return OGRERR_FAILURE;
312 : }
313 : }
314 1258 : continue;
315 : }
316 :
317 : const OGRFieldDefn *poFieldDefn =
318 4466930 : poFeatureDefn->GetFieldDefnUnsafe(iField);
319 4466930 : int err = SQLITE_OK;
320 4466930 : bool bBound = false;
321 4466930 : char *pszValToFree = nullptr;
322 :
323 4466930 : if (!poFeature->IsFieldNullUnsafe(iField))
324 : {
325 4466890 : const auto eType = poFieldDefn->GetType();
326 4466890 : switch (eType)
327 : {
328 10532 : case OFTInteger:
329 : {
330 10532 : err = sqlite3_bind_int(
331 : poStmt, nColCount++,
332 : poFeature->GetFieldAsIntegerUnsafe(iField));
333 10532 : break;
334 : }
335 6069 : case OFTInteger64:
336 : {
337 6069 : err = sqlite3_bind_int64(
338 : poStmt, nColCount++,
339 : poFeature->GetFieldAsInteger64Unsafe(iField));
340 6069 : break;
341 : }
342 20382 : case OFTReal:
343 : {
344 20382 : err = sqlite3_bind_double(
345 : poStmt, nColCount++,
346 : poFeature->GetFieldAsDoubleUnsafe(iField));
347 20382 : break;
348 : }
349 7311 : case OFTBinary:
350 : {
351 7311 : int szBlob = 0;
352 : GByte *pabyBlob =
353 7311 : poFeature->GetFieldAsBinary(iField, &szBlob);
354 7311 : err = sqlite3_bind_blob(poStmt, nColCount++, pabyBlob,
355 : szBlob, SQLITE_STATIC);
356 7311 : break;
357 : }
358 4422600 : default:
359 : {
360 4422600 : const char *pszVal = "";
361 4422600 : CPL_IGNORE_RET_VAL(pszVal); // Make CSA happy
362 4422600 : int nValLengthBytes = -1;
363 4422600 : sqlite3_destructor_type destructorType = SQLITE_TRANSIENT;
364 4422600 : if (eType == OFTDate)
365 : {
366 5141 : destructorType = SQLITE_STATIC;
367 : const auto psFieldRaw =
368 5141 : poFeature->GetRawFieldRef(iField);
369 : char *pszValEdit =
370 5141 : &m_osInsertionBuffer[nInsertionBufferPos];
371 5141 : pszVal = pszValEdit;
372 5141 : if (psFieldRaw->Date.Year < 0 ||
373 5141 : psFieldRaw->Date.Year >= 10000)
374 : {
375 0 : CPLError(
376 : CE_Failure, CPLE_AppDefined,
377 : "OGRGetISO8601DateTime(): year %d unsupported ",
378 0 : psFieldRaw->Date.Year);
379 0 : nValLengthBytes = 0;
380 : }
381 : else
382 : {
383 5141 : int nYear = psFieldRaw->Date.Year;
384 5141 : pszValEdit[3] = (nYear % 10) + '0';
385 5141 : nYear /= 10;
386 5141 : pszValEdit[2] = (nYear % 10) + '0';
387 5141 : nYear /= 10;
388 5141 : pszValEdit[1] = (nYear % 10) + '0';
389 5141 : nYear /= 10;
390 5141 : pszValEdit[0] =
391 5141 : static_cast<char>(nYear /*% 10*/ + '0');
392 5141 : pszValEdit[4] = '-';
393 5141 : pszValEdit[5] =
394 5141 : ((psFieldRaw->Date.Month / 10) % 10) + '0';
395 5141 : pszValEdit[6] = (psFieldRaw->Date.Month % 10) + '0';
396 5141 : pszValEdit[7] = '-';
397 5141 : pszValEdit[8] =
398 5141 : ((psFieldRaw->Date.Day / 10) % 10) + '0';
399 5141 : pszValEdit[9] = (psFieldRaw->Date.Day % 10) + '0';
400 5141 : nValLengthBytes = 10;
401 5141 : nInsertionBufferPos += 10;
402 : }
403 : }
404 4417450 : else if (eType == OFTDateTime)
405 : {
406 5169 : destructorType = SQLITE_STATIC;
407 : const auto psFieldRaw =
408 5169 : poFeature->GetRawFieldRef(iField);
409 : char *pszValEdit =
410 5169 : &m_osInsertionBuffer[nInsertionBufferPos];
411 5169 : pszVal = pszValEdit;
412 5169 : if (m_poDS->m_bDateTimeWithTZ ||
413 6 : psFieldRaw->Date.TZFlag == 100)
414 : {
415 5165 : nValLengthBytes = OGRGetISO8601DateTime(
416 5165 : psFieldRaw, m_sDateTimeFormat, pszValEdit);
417 : }
418 : else
419 : {
420 4 : OGRField sField(*psFieldRaw);
421 4 : if (sField.Date.TZFlag == 0 ||
422 2 : sField.Date.TZFlag == 1)
423 : {
424 2 : sField.Date.TZFlag = 100;
425 : }
426 : else
427 : {
428 : struct tm brokendowntime;
429 2 : brokendowntime.tm_year =
430 2 : sField.Date.Year - 1900;
431 2 : brokendowntime.tm_mon = sField.Date.Month - 1;
432 2 : brokendowntime.tm_mday = sField.Date.Day;
433 2 : brokendowntime.tm_hour = sField.Date.Hour;
434 2 : brokendowntime.tm_min = sField.Date.Minute;
435 2 : brokendowntime.tm_sec = 0;
436 : GIntBig nDT =
437 2 : CPLYMDHMSToUnixTime(&brokendowntime);
438 2 : const int TZOffset =
439 2 : std::abs(sField.Date.TZFlag - 100) * 15;
440 2 : nDT -= TZOffset * 60;
441 2 : CPLUnixTimeToYMDHMS(nDT, &brokendowntime);
442 2 : sField.Date.Year = static_cast<GInt16>(
443 2 : brokendowntime.tm_year + 1900);
444 2 : sField.Date.Month = static_cast<GByte>(
445 2 : brokendowntime.tm_mon + 1);
446 2 : sField.Date.Day =
447 2 : static_cast<GByte>(brokendowntime.tm_mday);
448 2 : sField.Date.Hour =
449 2 : static_cast<GByte>(brokendowntime.tm_hour);
450 2 : sField.Date.Minute =
451 2 : static_cast<GByte>(brokendowntime.tm_min);
452 2 : sField.Date.TZFlag = 100;
453 : }
454 :
455 4 : nValLengthBytes = OGRGetISO8601DateTime(
456 4 : &sField, m_sDateTimeFormat, pszValEdit);
457 : }
458 5169 : nInsertionBufferPos += nValLengthBytes;
459 : }
460 4412280 : else if (eType == OFTString)
461 : {
462 4407280 : pszVal = poFeature->GetFieldAsStringUnsafe(iField);
463 4407280 : if (poFieldDefn->GetWidth() > 0)
464 : {
465 970 : if (!CPLIsUTF8(pszVal, -1))
466 : {
467 4 : CPLError(CE_Warning, CPLE_AppDefined,
468 : "Value of field '%s' is not a valid "
469 : "UTF-8 string.%s",
470 2 : poFeatureDefn->GetFieldDefn(iField)
471 : ->GetNameRef(),
472 2 : m_bTruncateFields
473 : ? " Value will be laundered."
474 : : "");
475 2 : if (m_bTruncateFields)
476 : {
477 1 : pszValToFree =
478 1 : CPLForceToASCII(pszVal, -1, '_');
479 1 : pszVal = pszValToFree;
480 1 : destructorType = CPLFree;
481 : }
482 : }
483 :
484 970 : if (CPLStrlenUTF8Ex(pszVal) >
485 970 : static_cast<size_t>(poFieldDefn->GetWidth()))
486 : {
487 4 : CPLError(CE_Warning, CPLE_AppDefined,
488 : "Value of field '%s' has %" PRIu64
489 : " characters, "
490 : "whereas maximum allowed is %d.%s",
491 2 : poFeatureDefn->GetFieldDefn(iField)
492 : ->GetNameRef(),
493 : static_cast<uint64_t>(
494 2 : CPLStrlenUTF8Ex(pszVal)),
495 : poFieldDefn->GetWidth(),
496 2 : m_bTruncateFields
497 : ? " Value will be truncated."
498 : : "");
499 2 : if (m_bTruncateFields)
500 : {
501 1 : size_t countUTF8Chars = 0;
502 1 : nValLengthBytes = 0;
503 3 : while (pszVal[nValLengthBytes])
504 : {
505 3 : if ((pszVal[nValLengthBytes] & 0xc0) !=
506 : 0x80)
507 : {
508 : // Stop at the start of the
509 : // character just beyond the maximum
510 : // accepted
511 3 : if (countUTF8Chars ==
512 3 : static_cast<size_t>(
513 3 : poFieldDefn->GetWidth()))
514 : {
515 1 : break;
516 : }
517 2 : countUTF8Chars++;
518 : }
519 2 : nValLengthBytes++;
520 : }
521 : }
522 : }
523 : }
524 : else
525 : {
526 4406320 : destructorType = SQLITE_STATIC;
527 : }
528 :
529 : // If the field subtype is JSON and the current value cannot
530 : // be parsed as a valid JSON, encode it as a JSON string.
531 4407280 : if (poFieldDefn->GetSubType() == OFSTJSON)
532 : {
533 15 : json_object *poObjProp = nullptr;
534 15 : std::string osValue(pszVal);
535 15 : if (!OGRJSonParse(
536 : osValue.c_str(), &poObjProp, false,
537 15 : static_cast<int>(osValue.length() + 1)))
538 : {
539 1 : CPLErrorOnce(
540 : CE_Warning, CPLE_AppDefined,
541 : "Field %s is declared as JSON but the "
542 : "value is not a valid JSON. Storing as "
543 : "string.",
544 : poFieldDefn->GetNameRef());
545 : // Escape and quote
546 2 : osValue = CPLJSONObject(pszVal).Format(
547 1 : CPLJSONObject::PrettyFormat::Plain);
548 : }
549 : else
550 : {
551 14 : osValue = json_object_to_json_string(poObjProp);
552 : }
553 15 : err = sqlite3_bind_text(
554 : poStmt, nColCount++, osValue.c_str(),
555 15 : static_cast<int>(osValue.size()),
556 : SQLITE_TRANSIENT);
557 15 : bBound = true;
558 15 : json_object_put(poObjProp);
559 15 : CPLFree(pszValToFree);
560 15 : pszValToFree = nullptr;
561 15 : CPL_IGNORE_RET_VAL(pszValToFree);
562 15 : destructorType = SQLITE_TRANSIENT;
563 15 : pszVal = "";
564 15 : nValLengthBytes = 0;
565 : }
566 : }
567 : else
568 : {
569 5000 : pszVal = poFeature->GetFieldAsString(iField);
570 : }
571 :
572 4422600 : if (!bBound)
573 : {
574 : err =
575 4422580 : sqlite3_bind_text(poStmt, nColCount++, pszVal,
576 : nValLengthBytes, destructorType);
577 : }
578 4422600 : break;
579 : }
580 : }
581 : }
582 : else
583 : {
584 38 : err = sqlite3_bind_null(poStmt, nColCount++);
585 : }
586 4466930 : if (err != SQLITE_OK)
587 : {
588 0 : CPLError(CE_Failure, CPLE_AppDefined,
589 : "sqlite3_bind_() for column %s failed: %s",
590 : poFieldDefn->GetNameRef(),
591 0 : sqlite3_errmsg(m_poDS->GetDB()));
592 0 : return OGRERR_FAILURE;
593 : }
594 : }
595 :
596 268840 : if (pnColCount != nullptr)
597 66 : *pnColCount = nColCount;
598 268840 : return OGRERR_NONE;
599 : }
600 :
601 : //----------------------------------------------------------------------
602 : // FeatureBindUpdateParameters()
603 : //
604 : // Selectively bind the values of an OGRFeature to a prepared
605 : // statement, prior to execution. Carefully binds exactly the
606 : // same parameters that have been set up by FeatureGenerateUpdateSQL()
607 : // as bindable.
608 : //
609 : OGRErr
610 56 : OGRGeoPackageTableLayer::FeatureBindUpdateParameters(OGRFeature *poFeature,
611 : sqlite3_stmt *poStmt)
612 : {
613 :
614 56 : int nColCount = 0;
615 56 : const OGRErr err = FeatureBindParameters(
616 : poFeature, poStmt, &nColCount, false, false, -1, nullptr, -1, nullptr);
617 56 : if (err != OGRERR_NONE)
618 0 : return err;
619 :
620 : // Bind the FID to the "WHERE" clause.
621 : const int sqlite_err =
622 56 : sqlite3_bind_int64(poStmt, nColCount, poFeature->GetFID());
623 56 : if (sqlite_err != SQLITE_OK)
624 : {
625 0 : CPLError(CE_Failure, CPLE_AppDefined,
626 : "failed to bind FID '" CPL_FRMT_GIB "' to statement: %s",
627 0 : poFeature->GetFID(), sqlite3_errmsg(m_poDS->GetDB()));
628 0 : return OGRERR_FAILURE;
629 : }
630 :
631 56 : return OGRERR_NONE;
632 : }
633 :
634 : //----------------------------------------------------------------------
635 : // FeatureBindInsertParameters()
636 : //
637 : // Selectively bind the values of an OGRFeature to a prepared
638 : // statement, prior to execution. Carefully binds exactly the
639 : // same parameters that have been set up by FeatureGenerateInsertSQL()
640 : // as bindable.
641 : //
642 268774 : OGRErr OGRGeoPackageTableLayer::FeatureBindInsertParameters(
643 : OGRFeature *poFeature, sqlite3_stmt *poStmt, bool bAddFID,
644 : bool bBindUnsetFields)
645 : {
646 268774 : return FeatureBindParameters(poFeature, poStmt, nullptr, bAddFID,
647 268774 : bBindUnsetFields, -1, nullptr, -1, nullptr);
648 : }
649 :
650 : //----------------------------------------------------------------------
651 : // FeatureGenerateInsertSQL()
652 : //
653 : // Build a SQL INSERT statement that references all the columns in
654 : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
655 : // statement. All statements start off with geometry (if it exists)
656 : // then reference each column in the order it appears in the OGRFeatureDefn.
657 : // FeatureBindParameters operates on the expectation of this
658 : // column ordering.
659 : //
660 857 : CPLString OGRGeoPackageTableLayer::FeatureGenerateInsertSQL(
661 : OGRFeature *poFeature, bool bAddFID, bool bBindUnsetFields, bool bUpsert,
662 : const std::string &osUpsertUniqueColumnName)
663 : {
664 857 : bool bNeedComma = false;
665 857 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
666 :
667 857 : if (poFeatureDefn->GetFieldCount() ==
668 857 : ((m_iFIDAsRegularColumnIndex >= 0) ? 1 : 0) &&
669 857 : poFeatureDefn->GetGeomFieldCount() == 0 && !bAddFID)
670 : return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
671 8 : SQLEscapeName(m_pszTableName).c_str());
672 :
673 : /* Set up our SQL string basics */
674 1706 : CPLString osSQLFront("INSERT");
675 853 : if (bUpsert && osUpsertUniqueColumnName.empty())
676 8 : osSQLFront += " OR REPLACE";
677 : osSQLFront +=
678 853 : CPLSPrintf(" INTO \"%s\" ( ", SQLEscapeName(m_pszTableName).c_str());
679 :
680 1706 : CPLString osSQLBack;
681 853 : osSQLBack = ") VALUES (";
682 :
683 1706 : CPLString osSQLColumn;
684 :
685 853 : if (bAddFID)
686 : {
687 125 : osSQLColumn.Printf("\"%s\"", SQLEscapeName(GetFIDColumn()).c_str());
688 125 : osSQLFront += osSQLColumn;
689 125 : osSQLBack += "?";
690 125 : bNeedComma = true;
691 : }
692 :
693 853 : if (poFeatureDefn->GetGeomFieldCount())
694 : {
695 794 : if (bNeedComma)
696 : {
697 105 : osSQLFront += ", ";
698 105 : osSQLBack += ", ";
699 : }
700 :
701 : osSQLColumn.Printf(
702 : "\"%s\"",
703 1588 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
704 794 : .c_str());
705 794 : osSQLFront += osSQLColumn;
706 794 : osSQLBack += "?";
707 794 : bNeedComma = true;
708 : }
709 :
710 : /* Add attribute column names (except FID) to the SQL */
711 3343 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
712 : {
713 2490 : const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
714 2490 : if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
715 13 : continue;
716 2477 : if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
717 2 : continue;
718 :
719 2475 : if (!bNeedComma)
720 : {
721 39 : bNeedComma = true;
722 : }
723 : else
724 : {
725 2436 : osSQLFront += ", ";
726 2436 : osSQLBack += ", ";
727 : }
728 :
729 : osSQLColumn.Printf(
730 : "\"%s\"",
731 4950 : SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
732 2475 : .c_str());
733 2475 : osSQLFront += osSQLColumn;
734 2475 : osSQLBack += "?";
735 : }
736 :
737 853 : osSQLBack += ")";
738 :
739 853 : if (!bNeedComma)
740 : return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
741 0 : SQLEscapeName(m_pszTableName).c_str());
742 :
743 853 : if (bUpsert && !osUpsertUniqueColumnName.empty())
744 : {
745 7 : osSQLBack += " ON CONFLICT ";
746 : #if SQLITE_VERSION_NUMBER < 3035000L
747 : osSQLBack += "(\"";
748 : osSQLBack += SQLEscapeName(osUpsertUniqueColumnName.c_str());
749 : osSQLBack += "\") ";
750 : #endif
751 7 : osSQLBack += "DO UPDATE SET ";
752 7 : bNeedComma = false;
753 7 : if (poFeatureDefn->GetGeomFieldCount())
754 : {
755 : osSQLBack += CPLSPrintf(
756 : "\"%s\" = excluded.\"%s\"",
757 8 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
758 : .c_str(),
759 8 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
760 8 : .c_str());
761 4 : bNeedComma = true;
762 : }
763 23 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
764 : {
765 16 : const auto *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
766 16 : if (i == m_iFIDAsRegularColumnIndex || poFieldDefn->IsGenerated())
767 2 : continue;
768 14 : if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
769 0 : continue;
770 :
771 14 : if (!bNeedComma)
772 : {
773 3 : bNeedComma = true;
774 : }
775 : else
776 : {
777 11 : osSQLBack += ", ";
778 : }
779 :
780 : const std::string osEscapedColName =
781 28 : SQLEscapeName(poFieldDefn->GetNameRef());
782 : osSQLBack +=
783 : CPLSPrintf("\"%s\" = excluded.\"%s\"", osEscapedColName.c_str(),
784 14 : osEscapedColName.c_str());
785 : }
786 : #if SQLITE_VERSION_NUMBER >= 3035000L
787 7 : osSQLBack += " RETURNING \"";
788 7 : osSQLBack += SQLEscapeName(GetFIDColumn()).c_str();
789 7 : osSQLBack += "\"";
790 : #endif
791 : }
792 :
793 1706 : return osSQLFront + osSQLBack;
794 : }
795 :
796 : //----------------------------------------------------------------------
797 : // FeatureGenerateUpdateSQL()
798 : //
799 : // Build a SQL UPDATE statement that references all the columns in
800 : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
801 : // statement. All statements start off with geometry (if it exists)
802 : // then reference each column in the order it appears in the OGRFeatureDefn.
803 : // FeatureBindParameters operates on the expectation of this
804 : // column ordering.
805 :
806 : //
807 46 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
808 : const OGRFeature *poFeature) const
809 : {
810 46 : bool bNeedComma = false;
811 46 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
812 :
813 : /* Set up our SQL string basics */
814 92 : std::string osUpdate("UPDATE \"");
815 46 : osUpdate += SQLEscapeName(m_pszTableName);
816 46 : osUpdate += "\" SET ";
817 :
818 46 : if (poFeatureDefn->GetGeomFieldCount() > 0)
819 : {
820 39 : osUpdate += '"';
821 : osUpdate +=
822 39 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
823 39 : osUpdate += "\"=?";
824 39 : bNeedComma = true;
825 : }
826 :
827 : /* Add attribute column names (except FID) to the SQL */
828 46 : const int nFieldCount = poFeatureDefn->GetFieldCount();
829 114 : for (int i = 0; i < nFieldCount; i++)
830 : {
831 68 : const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(i);
832 68 : if (i == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
833 8 : continue;
834 60 : if (!poFeature->IsFieldSet(i))
835 11 : continue;
836 49 : if (!bNeedComma)
837 6 : bNeedComma = true;
838 : else
839 43 : osUpdate += ", ";
840 :
841 49 : osUpdate += '"';
842 49 : osUpdate += SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
843 49 : osUpdate += "\"=?";
844 : }
845 46 : if (!bNeedComma)
846 1 : return CPLString();
847 :
848 45 : osUpdate += " WHERE \"";
849 45 : osUpdate += SQLEscapeName(m_pszFidColumn);
850 45 : osUpdate += "\" = ?";
851 :
852 45 : return osUpdate;
853 : }
854 :
855 : /************************************************************************/
856 : /* GetLayerDefn() */
857 : /************************************************************************/
858 :
859 67644 : const OGRFeatureDefn *OGRGeoPackageTableLayer::GetLayerDefn() const
860 : {
861 67644 : if (!m_bFeatureDefnCompleted)
862 : {
863 1074 : m_bFeatureDefnCompleted = true;
864 1074 : const_cast<OGRGeoPackageTableLayer *>(this)->ReadTableDefinition();
865 1074 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
866 : }
867 67644 : return m_poFeatureDefn;
868 : }
869 :
870 : /************************************************************************/
871 : /* GetFIDColumn() */
872 : /************************************************************************/
873 :
874 3478 : const char *OGRGeoPackageTableLayer::GetFIDColumn() const
875 : {
876 3478 : if (!m_bFeatureDefnCompleted)
877 25 : GetLayerDefn();
878 3478 : return OGRGeoPackageLayer::GetFIDColumn();
879 : }
880 :
881 : /************************************************************************/
882 : /* GetGeomType() */
883 : /************************************************************************/
884 :
885 273091 : OGRwkbGeometryType OGRGeoPackageTableLayer::GetGeomType() const
886 : {
887 273091 : return m_poFeatureDefn->GetGeomType();
888 : }
889 :
890 : /************************************************************************/
891 : /* GetGeometryColumn() */
892 : /************************************************************************/
893 :
894 7133 : const char *OGRGeoPackageTableLayer::GetGeometryColumn() const
895 :
896 : {
897 7133 : if (m_poFeatureDefn->GetGeomFieldCount() > 0)
898 7119 : return m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
899 : else
900 14 : return "";
901 : }
902 :
903 : //----------------------------------------------------------------------
904 : // ReadTableDefinition()
905 : //
906 : // Initialization routine. Read all the metadata about a table,
907 : // starting from just the table name. Reads information from GPKG
908 : // metadata tables and from SQLite table metadata. Uses it to
909 : // populate OGRSpatialReference information and OGRFeatureDefn objects,
910 : // among others.
911 : //
912 1074 : OGRErr OGRGeoPackageTableLayer::ReadTableDefinition()
913 : {
914 1074 : m_poDS->IncrementReadTableDefCounter();
915 :
916 1074 : bool bReadExtent = false;
917 1074 : sqlite3 *poDb = m_poDS->GetDB();
918 1074 : OGREnvelope oExtent;
919 2148 : CPLString osGeomColumnName;
920 2148 : CPLString osGeomColsType;
921 1074 : bool bHasZ = false;
922 1074 : bool bHasM = false;
923 :
924 : #ifdef ENABLE_GPKG_OGR_CONTENTS
925 1074 : if (m_poDS->m_bHasGPKGOGRContents)
926 : {
927 : CPLString osTrigger1Name(
928 2140 : CPLSPrintf("trigger_insert_feature_count_%s", m_pszTableName));
929 : CPLString osTrigger2Name(
930 2140 : CPLSPrintf("trigger_delete_feature_count_%s", m_pszTableName));
931 : const std::map<CPLString, CPLString> &oMap =
932 1070 : m_poDS->GetNameTypeMapFromSQliteMaster();
933 2093 : if (cpl::contains(oMap, osTrigger1Name.toupper()) &&
934 1023 : cpl::contains(oMap, osTrigger2Name.toupper()))
935 : {
936 1023 : m_bOGRFeatureCountTriggersEnabled = true;
937 : }
938 47 : else if (m_bIsTable)
939 : {
940 39 : CPLDebug("GPKG",
941 : "Insert/delete feature_count triggers "
942 : "missing on %s",
943 : m_pszTableName);
944 : }
945 : }
946 : #endif
947 :
948 : #ifdef ENABLE_GPKG_OGR_CONTENTS
949 1074 : if (m_poDS->m_bHasGPKGOGRContents)
950 : {
951 1070 : char *pszSQL = sqlite3_mprintf("SELECT feature_count "
952 : "FROM gpkg_ogr_contents "
953 : "WHERE table_name = '%q'"
954 : #ifdef WORKAROUND_SQLITE3_BUGS
955 : " OR 0"
956 : #endif
957 : " LIMIT 2",
958 : m_pszTableName);
959 2140 : auto oResultFeatureCount = SQLQuery(poDb, pszSQL);
960 1070 : sqlite3_free(pszSQL);
961 1070 : if (oResultFeatureCount && oResultFeatureCount->RowCount() == 0)
962 : {
963 35 : pszSQL = sqlite3_mprintf("SELECT feature_count "
964 : "FROM gpkg_ogr_contents "
965 : "WHERE lower(table_name) = lower('%q')"
966 : #ifdef WORKAROUND_SQLITE3_BUGS
967 : " OR 0"
968 : #endif
969 : " LIMIT 2",
970 : m_pszTableName);
971 35 : oResultFeatureCount = SQLQuery(poDb, pszSQL);
972 35 : sqlite3_free(pszSQL);
973 : }
974 :
975 1070 : if (oResultFeatureCount && oResultFeatureCount->RowCount() == 1)
976 : {
977 1035 : const char *pszFeatureCount = oResultFeatureCount->GetValue(0, 0);
978 1035 : if (pszFeatureCount)
979 : {
980 1018 : m_nTotalFeatureCount =
981 1018 : std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
982 : }
983 : }
984 : }
985 : #endif
986 :
987 : bool bHasPreexistingSingleGeomColumn =
988 1074 : m_poFeatureDefn->GetGeomFieldCount() == 1;
989 1074 : bool bHasMultipleGeomColsInGpkgGeometryColumns = false;
990 :
991 1074 : if (m_bIsInGpkgContents)
992 : {
993 : /* Check that the table name is registered in gpkg_contents */
994 : const std::map<CPLString, GPKGContentsDesc> &oMapContents =
995 1055 : m_poDS->GetContents();
996 : const auto oIterContents =
997 1055 : oMapContents.find(CPLString(m_pszTableName).toupper());
998 1055 : if (oIterContents == oMapContents.end())
999 : {
1000 0 : CPLError(CE_Failure, CPLE_AppDefined,
1001 : "layer '%s' is not registered in gpkg_contents",
1002 : m_pszTableName);
1003 0 : return OGRERR_FAILURE;
1004 : }
1005 :
1006 1055 : const GPKGContentsDesc &oContents = oIterContents->second;
1007 :
1008 1055 : const char *pszIdentifier = oContents.osIdentifier.c_str();
1009 1055 : if (pszIdentifier[0] != 0 && strcmp(pszIdentifier, m_pszTableName) != 0)
1010 6 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
1011 1055 : const char *pszDescription = oContents.osDescription.c_str();
1012 1055 : if (pszDescription[0])
1013 12 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
1014 :
1015 1055 : if (m_bIsSpatial)
1016 : {
1017 : /* All the extrema have to be non-NULL for this to make sense */
1018 1736 : if (!oContents.osMinX.empty() && !oContents.osMinY.empty() &&
1019 1736 : !oContents.osMaxX.empty() && !oContents.osMaxY.empty())
1020 : {
1021 741 : oExtent.MinX = CPLAtof(oContents.osMinX);
1022 741 : oExtent.MinY = CPLAtof(oContents.osMinY);
1023 741 : oExtent.MaxX = CPLAtof(oContents.osMaxX);
1024 741 : oExtent.MaxY = CPLAtof(oContents.osMaxY);
1025 1482 : bReadExtent = oExtent.MinX <= oExtent.MaxX &&
1026 741 : oExtent.MinY <= oExtent.MaxY;
1027 : }
1028 :
1029 : /* Check that the table name is registered in gpkg_geometry_columns
1030 : */
1031 995 : char *pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
1032 : "geometry_type_name, srs_id, z, m "
1033 : "FROM gpkg_geometry_columns "
1034 : "WHERE table_name = '%q'"
1035 : #ifdef WORKAROUND_SQLITE3_BUGS
1036 : " OR 0"
1037 : #endif
1038 : " LIMIT 2000",
1039 : m_pszTableName);
1040 :
1041 1990 : auto oResultGeomCols = SQLQuery(poDb, pszSQL);
1042 995 : sqlite3_free(pszSQL);
1043 995 : if (oResultGeomCols && oResultGeomCols->RowCount() == 0)
1044 : {
1045 0 : pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
1046 : "geometry_type_name, srs_id, z, m "
1047 : "FROM gpkg_geometry_columns "
1048 : "WHERE lower(table_name) = lower('%q')"
1049 : #ifdef WORKAROUND_SQLITE3_BUGS
1050 : " OR 0"
1051 : #endif
1052 : " LIMIT 2000",
1053 : m_pszTableName);
1054 :
1055 0 : oResultGeomCols = SQLQuery(poDb, pszSQL);
1056 0 : sqlite3_free(pszSQL);
1057 : }
1058 :
1059 : /* gpkg_geometry_columns query has to work */
1060 995 : if (!(oResultGeomCols && oResultGeomCols->RowCount() > 0))
1061 : {
1062 0 : CPLError(
1063 : CE_Warning, CPLE_AppDefined,
1064 : "layer '%s' is not registered in gpkg_geometry_columns",
1065 : m_pszTableName);
1066 : }
1067 : else
1068 : {
1069 995 : int iRow = -1;
1070 995 : bHasMultipleGeomColsInGpkgGeometryColumns =
1071 995 : oResultGeomCols->RowCount() > 1;
1072 996 : for (int i = 0; i < oResultGeomCols->RowCount(); ++i)
1073 : {
1074 : const char *pszGeomColName =
1075 996 : oResultGeomCols->GetValue(1, i);
1076 996 : if (!pszGeomColName)
1077 0 : continue;
1078 1992 : if (!bHasPreexistingSingleGeomColumn ||
1079 996 : strcmp(pszGeomColName,
1080 996 : m_poFeatureDefn->GetGeomFieldDefn(0)
1081 : ->GetNameRef()) == 0)
1082 : {
1083 995 : iRow = i;
1084 995 : break;
1085 : }
1086 : }
1087 :
1088 995 : if (iRow >= 0)
1089 : {
1090 : const char *pszGeomColName =
1091 995 : oResultGeomCols->GetValue(1, iRow);
1092 995 : if (pszGeomColName != nullptr)
1093 995 : osGeomColumnName = pszGeomColName;
1094 : const char *pszGeomColsType =
1095 995 : oResultGeomCols->GetValue(2, iRow);
1096 995 : if (pszGeomColsType != nullptr)
1097 995 : osGeomColsType = pszGeomColsType;
1098 995 : m_iSrs = oResultGeomCols->GetValueAsInteger(3, iRow);
1099 995 : m_nZFlag = oResultGeomCols->GetValueAsInteger(4, iRow);
1100 995 : m_nMFlag = oResultGeomCols->GetValueAsInteger(5, iRow);
1101 995 : if (!(EQUAL(osGeomColsType, "GEOMETRY") && m_nZFlag == 2))
1102 : {
1103 989 : bHasZ = CPL_TO_BOOL(m_nZFlag);
1104 989 : bHasM = CPL_TO_BOOL(m_nMFlag);
1105 : }
1106 : }
1107 : else
1108 : {
1109 0 : CPLError(
1110 : CE_Warning, CPLE_AppDefined,
1111 : "Cannot find record for layer '%s' and geometry column "
1112 : "'%s' in gpkg_geometry_columns",
1113 : m_pszTableName,
1114 : bHasPreexistingSingleGeomColumn
1115 0 : ? m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()
1116 : : "unknown");
1117 : }
1118 : }
1119 : }
1120 : }
1121 :
1122 : // set names (in upper case) of fields with unique constraint
1123 2148 : std::set<std::string> uniqueFieldsUC;
1124 1074 : if (m_bIsTable)
1125 : {
1126 : // If resolving the layer definition of a substantial number of tables,
1127 : // fetch in a single time the content of the sqlite_master to increase
1128 : // performance
1129 : // Threshold somewhat arbitrary. If changing it, change
1130 : // ogr_gpkg.py::test_ogr_gpkg_unique_many_layers as well
1131 1066 : constexpr int THRESHOLD_GET_SQLITE_MASTER = 10;
1132 1066 : if (m_poDS->GetReadTableDefCounter() >= THRESHOLD_GET_SQLITE_MASTER)
1133 : {
1134 2 : uniqueFieldsUC = SQLGetUniqueFieldUCConstraints(
1135 2 : poDb, m_pszTableName, m_poDS->GetSqliteMasterContent());
1136 : }
1137 : else
1138 : {
1139 : uniqueFieldsUC =
1140 1064 : SQLGetUniqueFieldUCConstraints(poDb, m_pszTableName);
1141 : }
1142 : }
1143 :
1144 : /* Use the "PRAGMA TABLE_INFO()" call to get table definition */
1145 : /* #|name|type|notnull|default|pk */
1146 : /* 0|id|integer|0||1 */
1147 : /* 1|name|varchar|0||0 */
1148 1074 : char *pszSQL = sqlite3_mprintf("pragma table_xinfo('%q')", m_pszTableName);
1149 2148 : auto oResultTable = SQLQuery(poDb, pszSQL);
1150 1074 : sqlite3_free(pszSQL);
1151 :
1152 1074 : if (!oResultTable || oResultTable->RowCount() == 0)
1153 : {
1154 0 : if (oResultTable)
1155 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
1156 : m_pszTableName);
1157 0 : return OGRERR_FAILURE;
1158 : }
1159 :
1160 : /* Populate feature definition from table description */
1161 :
1162 : // First pass to determine if we have a single PKID column
1163 1074 : int nCountPKIDColumns = 0;
1164 4986 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1165 : {
1166 3912 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1167 3912 : if (nPKIDIndex > 0)
1168 1059 : nCountPKIDColumns++;
1169 : }
1170 1074 : if (nCountPKIDColumns > 1)
1171 : {
1172 1 : CPLDebug("GPKG",
1173 : "For table %s, multiple columns make "
1174 : "the primary key. Ignoring them",
1175 : m_pszTableName);
1176 : }
1177 :
1178 4986 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1179 : {
1180 3912 : const char *pszName = oResultTable->GetValue(1, iRecord);
1181 7824 : std::string osType = oResultTable->GetValue(2, iRecord);
1182 3912 : int bNotNull = oResultTable->GetValueAsInteger(3, iRecord);
1183 3912 : const char *pszDefault = oResultTable->GetValue(4, iRecord);
1184 3912 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1185 3912 : int nHiddenValue = oResultTable->GetValueAsInteger(6, iRecord);
1186 :
1187 3912 : OGRFieldSubType eSubType = OFSTNone;
1188 3912 : int nMaxWidth = 0;
1189 3912 : int nType = OFTMaxType + 1;
1190 :
1191 : // SQLite 3.31 has a " GENERATED ALWAYS" suffix in the type column,
1192 : // but more recent versions no longer have it.
1193 3912 : bool bIsGenerated = false;
1194 3912 : constexpr const char *GENERATED_ALWAYS_SUFFIX = " GENERATED ALWAYS";
1195 3912 : if (osType.size() > strlen(GENERATED_ALWAYS_SUFFIX) &&
1196 3912 : CPLString(osType).toupper().compare(
1197 0 : osType.size() - strlen(GENERATED_ALWAYS_SUFFIX),
1198 : strlen(GENERATED_ALWAYS_SUFFIX), GENERATED_ALWAYS_SUFFIX) == 0)
1199 : {
1200 0 : bIsGenerated = true;
1201 0 : osType.resize(osType.size() - strlen(GENERATED_ALWAYS_SUFFIX));
1202 : }
1203 3912 : constexpr int GENERATED_VIRTUAL = 2;
1204 3912 : constexpr int GENERATED_STORED = 3;
1205 3912 : if (nHiddenValue == GENERATED_VIRTUAL ||
1206 : nHiddenValue == GENERATED_STORED)
1207 : {
1208 2 : bIsGenerated = true;
1209 : }
1210 :
1211 3912 : if (!osType.empty() || m_bIsTable)
1212 : {
1213 3909 : nType = GPkgFieldToOGR(osType.c_str(), eSubType, nMaxWidth);
1214 : }
1215 : else
1216 : {
1217 : // For a view, if the geometry column is computed, we don't
1218 : // get a type, so trust the one from gpkg_geometry_columns
1219 3 : if (EQUAL(osGeomColumnName, pszName))
1220 : {
1221 1 : osType = osGeomColsType;
1222 : }
1223 : }
1224 :
1225 : /* Not a standard field type... */
1226 7818 : if (!osType.empty() && !EQUAL(pszName, "OGC_FID") &&
1227 999 : ((nType > OFTMaxType && !osGeomColsType.empty()) ||
1228 2909 : EQUAL(osGeomColumnName, pszName)))
1229 : {
1230 : /* Maybe it is a geometry type? */
1231 : OGRwkbGeometryType oGeomType;
1232 997 : if (nType > OFTMaxType)
1233 997 : oGeomType = GPkgGeometryTypeToWKB(osType.c_str(), bHasZ, bHasM);
1234 : else
1235 0 : oGeomType = wkbUnknown;
1236 997 : if (oGeomType != wkbNone)
1237 : {
1238 1993 : if ((bHasPreexistingSingleGeomColumn &&
1239 996 : (!bHasMultipleGeomColsInGpkgGeometryColumns ||
1240 3 : strcmp(pszName, m_poFeatureDefn->GetGeomFieldDefn(0)
1241 1994 : ->GetNameRef()) == 0)) ||
1242 2 : m_poFeatureDefn->GetGeomFieldCount() == 0)
1243 : {
1244 : OGRwkbGeometryType oGeomTypeGeomCols =
1245 995 : GPkgGeometryTypeToWKB(osGeomColsType.c_str(), bHasZ,
1246 : bHasM);
1247 : /* Enforce consistency between table and metadata */
1248 995 : if (wkbFlatten(oGeomType) == wkbUnknown)
1249 577 : oGeomType = oGeomTypeGeomCols;
1250 995 : if (oGeomType != oGeomTypeGeomCols)
1251 : {
1252 0 : CPLError(CE_Warning, CPLE_AppDefined,
1253 : "geometry column type for layer '%s' in "
1254 : "'%s.%s' (%s) is not "
1255 : "consistent with type in "
1256 : "gpkg_geometry_columns (%s)",
1257 : GetName(), m_pszTableName, pszName,
1258 : osType.c_str(), osGeomColsType.c_str());
1259 : }
1260 :
1261 995 : if (!bHasPreexistingSingleGeomColumn)
1262 : {
1263 0 : OGRGeomFieldDefn oGeomField(pszName, oGeomType);
1264 0 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1265 : }
1266 995 : bHasPreexistingSingleGeomColumn = false;
1267 995 : if (bNotNull)
1268 3 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetNullable(
1269 : FALSE);
1270 :
1271 : /* Read the SRS */
1272 1990 : auto poSRS = m_poDS->GetSpatialRef(m_iSrs);
1273 995 : if (poSRS)
1274 : {
1275 890 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
1276 445 : poSRS.get());
1277 : }
1278 : }
1279 2 : else if (!STARTS_WITH(
1280 : GetName(),
1281 : (std::string(m_pszTableName) + " (").c_str()))
1282 : {
1283 0 : CPLError(CE_Warning, CPLE_AppDefined,
1284 : "table '%s' has multiple geometry fields. "
1285 : "Ignoring field '%s' for this layer",
1286 : m_pszTableName, pszName);
1287 : }
1288 : }
1289 : else
1290 : {
1291 0 : CPLError(CE_Warning, CPLE_AppDefined,
1292 : "geometry column '%s' of type '%s' ignored", pszName,
1293 : osType.c_str());
1294 : }
1295 : }
1296 : else
1297 : {
1298 2915 : if (nType > OFTMaxType)
1299 : {
1300 5 : CPLDebug("GPKG",
1301 : "For table %s, unrecognized type name %s for "
1302 : "column %s. Using string type",
1303 : m_pszTableName, osType.c_str(), pszName);
1304 5 : nType = OFTString;
1305 : }
1306 :
1307 : /* Is this the FID column? */
1308 2915 : if (nPKIDIndex > 0 && nCountPKIDColumns == 1 &&
1309 1057 : (nType == OFTInteger || nType == OFTInteger64))
1310 : {
1311 1057 : m_pszFidColumn = CPLStrdup(pszName);
1312 : }
1313 : else
1314 : {
1315 3716 : OGRFieldDefn oField(pszName, static_cast<OGRFieldType>(nType));
1316 1858 : oField.SetSubType(eSubType);
1317 1858 : oField.SetWidth(nMaxWidth);
1318 1858 : if (bNotNull)
1319 14 : oField.SetNullable(FALSE);
1320 :
1321 1858 : if (cpl::contains(uniqueFieldsUC, CPLString(pszName).toupper()))
1322 : {
1323 48 : oField.SetUnique(TRUE);
1324 : }
1325 :
1326 1858 : if (pszDefault != nullptr)
1327 : {
1328 15 : int nYear = 0;
1329 15 : int nMonth = 0;
1330 15 : int nDay = 0;
1331 15 : int nHour = 0;
1332 15 : int nMinute = 0;
1333 15 : float fSecond = 0.0f;
1334 15 : if (oField.GetType() == OFTString &&
1335 3 : !EQUAL(pszDefault, "NULL") &&
1336 3 : !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
1337 18 : pszDefault[0] != '(' && pszDefault[0] != '\'' &&
1338 0 : CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
1339 : {
1340 0 : CPLString osDefault("'");
1341 : char *pszTmp =
1342 0 : CPLEscapeString(pszDefault, -1, CPLES_SQL);
1343 0 : osDefault += pszTmp;
1344 0 : CPLFree(pszTmp);
1345 0 : osDefault += "'";
1346 0 : oField.SetDefault(osDefault);
1347 : }
1348 23 : else if (nType == OFTDateTime &&
1349 8 : sscanf(pszDefault, "'%d-%d-%dT%d:%d:%fZ'", &nYear,
1350 : &nMonth, &nDay, &nHour, &nMinute,
1351 : &fSecond) == 6)
1352 : {
1353 2 : if (strchr(pszDefault, '.') == nullptr)
1354 1 : oField.SetDefault(
1355 : CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
1356 : nYear, nMonth, nDay, nHour, nMinute,
1357 1 : static_cast<int>(fSecond + 0.5)));
1358 : else
1359 1 : oField.SetDefault(CPLSPrintf(
1360 : "'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
1361 : nMonth, nDay, nHour, nMinute, fSecond));
1362 : }
1363 25 : else if ((oField.GetType() == OFTDate ||
1364 12 : oField.GetType() == OFTDateTime) &&
1365 7 : !EQUAL(pszDefault, "NULL") &&
1366 7 : !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
1367 6 : pszDefault[0] != '(' && pszDefault[0] != '\'' &&
1368 30 : !(pszDefault[0] >= '0' && pszDefault[0] <= '9') &&
1369 4 : CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
1370 : {
1371 8 : CPLString osDefault("(");
1372 4 : osDefault += pszDefault;
1373 4 : osDefault += ")";
1374 4 : if (EQUAL(osDefault,
1375 : "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))"))
1376 4 : oField.SetDefault("CURRENT_TIMESTAMP");
1377 : else
1378 0 : oField.SetDefault(osDefault);
1379 : }
1380 : else
1381 : {
1382 9 : oField.SetDefault(pszDefault);
1383 : }
1384 : }
1385 1858 : oField.SetGenerated(bIsGenerated);
1386 1858 : m_poFeatureDefn->AddFieldDefn(&oField);
1387 : }
1388 : }
1389 : }
1390 :
1391 : /* Wait, we didn't find a FID? Some operations will not be possible */
1392 1074 : if (m_bIsTable && m_pszFidColumn == nullptr)
1393 : {
1394 9 : CPLDebug("GPKG", "no integer primary key defined for table '%s'",
1395 : m_pszTableName);
1396 : }
1397 :
1398 1074 : if (bReadExtent)
1399 : {
1400 741 : m_poExtent = std::make_unique<OGREnvelope>(oExtent);
1401 : }
1402 :
1403 : // Look for sub-types such as JSON
1404 1074 : if (m_poDS->HasDataColumnsTable())
1405 : {
1406 46 : pszSQL = sqlite3_mprintf(
1407 : "SELECT column_name, name, mime_type, "
1408 : "constraint_name, description FROM gpkg_data_columns "
1409 : "WHERE table_name = '%q'",
1410 : m_pszTableName);
1411 46 : oResultTable = SQLQuery(poDb, pszSQL);
1412 46 : sqlite3_free(pszSQL);
1413 46 : if (oResultTable)
1414 : {
1415 154 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1416 : {
1417 108 : const char *pszColumn = oResultTable->GetValue(0, iRecord);
1418 108 : if (pszColumn == nullptr)
1419 0 : continue;
1420 108 : const char *pszName = oResultTable->GetValue(1, iRecord);
1421 :
1422 : // We use the "name" attribute from gpkg_data_columns as the
1423 : // field alternative name, so long as it isn't just a copy
1424 : // of the column name
1425 108 : const char *pszAlias = nullptr;
1426 108 : if (pszName && !EQUAL(pszName, pszColumn))
1427 10 : pszAlias = pszName;
1428 :
1429 108 : if (pszAlias)
1430 : {
1431 10 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1432 10 : if (iIdx >= 0)
1433 : {
1434 10 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetAlternativeName(
1435 : pszAlias);
1436 : }
1437 : }
1438 :
1439 108 : if (const char *pszDescription =
1440 108 : oResultTable->GetValue(4, iRecord))
1441 : {
1442 7 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1443 7 : if (iIdx >= 0)
1444 : {
1445 7 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetComment(
1446 : pszDescription);
1447 : }
1448 : }
1449 :
1450 108 : const char *pszMimeType = oResultTable->GetValue(2, iRecord);
1451 : const char *pszConstraintName =
1452 108 : oResultTable->GetValue(3, iRecord);
1453 108 : if (pszMimeType && EQUAL(pszMimeType, "application/json"))
1454 : {
1455 7 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1456 14 : if (iIdx >= 0 &&
1457 7 : m_poFeatureDefn->GetFieldDefn(iIdx)->GetType() ==
1458 : OFTString)
1459 : {
1460 7 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetSubType(
1461 : OFSTJSON);
1462 7 : }
1463 : }
1464 101 : else if (pszConstraintName)
1465 : {
1466 89 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1467 89 : if (iIdx >= 0)
1468 : {
1469 89 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetDomainName(
1470 : pszConstraintName);
1471 : }
1472 : }
1473 : }
1474 : }
1475 : }
1476 :
1477 : // Look for geometry column coordinate precision in gpkg_metadata
1478 1074 : if (m_poDS->HasMetadataTables() && m_poFeatureDefn->GetGeomFieldCount() > 0)
1479 : {
1480 419 : pszSQL = sqlite3_mprintf(
1481 : "SELECT md.metadata, mdr.column_name "
1482 : "FROM gpkg_metadata md "
1483 : "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id) "
1484 : "WHERE lower(mdr.table_name) = lower('%q') "
1485 : "AND md.md_standard_uri = 'http://gdal.org' "
1486 : "AND md.mime_type = 'text/xml' "
1487 : "AND mdr.reference_scope = 'column' "
1488 : "AND md.metadata LIKE '<CoordinatePrecision%%' "
1489 : "ORDER BY md.id LIMIT 1000", // to avoid denial of service
1490 : m_pszTableName);
1491 :
1492 838 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
1493 419 : sqlite3_free(pszSQL);
1494 :
1495 435 : for (int i = 0; oResult && i < oResult->RowCount(); i++)
1496 : {
1497 16 : const char *pszMetadata = oResult->GetValue(0, i);
1498 16 : const char *pszColumn = oResult->GetValue(1, i);
1499 16 : if (pszMetadata && pszColumn)
1500 : {
1501 : const int iGeomCol =
1502 16 : m_poFeatureDefn->GetGeomFieldIndex(pszColumn);
1503 16 : if (iGeomCol >= 0)
1504 : {
1505 : auto psXMLNode =
1506 32 : CPLXMLTreeCloser(CPLParseXMLString(pszMetadata));
1507 16 : if (psXMLNode)
1508 : {
1509 32 : OGRGeomCoordinatePrecision sCoordPrec;
1510 16 : if (const char *pszVal = CPLGetXMLValue(
1511 16 : psXMLNode.get(), "xy_resolution", nullptr))
1512 : {
1513 16 : sCoordPrec.dfXYResolution = CPLAtof(pszVal);
1514 : }
1515 16 : if (const char *pszVal = CPLGetXMLValue(
1516 16 : psXMLNode.get(), "z_resolution", nullptr))
1517 : {
1518 12 : sCoordPrec.dfZResolution = CPLAtof(pszVal);
1519 : }
1520 16 : if (const char *pszVal = CPLGetXMLValue(
1521 16 : psXMLNode.get(), "m_resolution", nullptr))
1522 : {
1523 12 : sCoordPrec.dfMResolution = CPLAtof(pszVal);
1524 : }
1525 16 : m_poFeatureDefn->GetGeomFieldDefn(iGeomCol)
1526 16 : ->SetCoordinatePrecision(sCoordPrec);
1527 16 : if (CPLTestBool(CPLGetXMLValue(
1528 16 : psXMLNode.get(), "discard_coord_lsb", "false")))
1529 : {
1530 4 : m_sBinaryPrecision.SetFrom(sCoordPrec);
1531 4 : m_bUndoDiscardCoordLSBOnReading =
1532 4 : CPLTestBool(CPLGetXMLValue(
1533 4 : psXMLNode.get(),
1534 : "undo_discard_coord_lsb_on_reading",
1535 : "false"));
1536 : }
1537 : }
1538 : }
1539 : }
1540 : }
1541 : }
1542 :
1543 : /* Update the columns string */
1544 1074 : BuildColumns();
1545 :
1546 1074 : CheckUnknownExtensions();
1547 :
1548 1074 : InitView();
1549 :
1550 1074 : return OGRERR_NONE;
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* OGRGeoPackageTableLayer() */
1555 : /************************************************************************/
1556 :
1557 4432 : OGRGeoPackageTableLayer::OGRGeoPackageTableLayer(GDALGeoPackageDataset *poDS,
1558 4432 : const char *pszTableName)
1559 4432 : : OGRGeoPackageLayer(poDS), m_pszTableName(CPLStrdup(pszTableName))
1560 : {
1561 4432 : memset(m_abHasGeometryExtension, 0, sizeof(m_abHasGeometryExtension));
1562 :
1563 4432 : m_poFeatureDefn = new OGRFeatureDefn(m_pszTableName);
1564 4432 : SetDescription(m_poFeatureDefn->GetName());
1565 4432 : m_poFeatureDefn->SetGeomType(wkbNone);
1566 4432 : m_poFeatureDefn->Reference();
1567 4432 : }
1568 :
1569 : /************************************************************************/
1570 : /* ~OGRGeoPackageTableLayer() */
1571 : /************************************************************************/
1572 :
1573 8864 : OGRGeoPackageTableLayer::~OGRGeoPackageTableLayer()
1574 : {
1575 4432 : OGRGeoPackageTableLayer::SyncToDisk();
1576 :
1577 : /* Clean up resources in memory */
1578 4432 : if (m_pszTableName)
1579 4432 : CPLFree(m_pszTableName);
1580 :
1581 4432 : if (m_poUpdateStatement)
1582 23 : sqlite3_finalize(m_poUpdateStatement);
1583 :
1584 4432 : if (m_poInsertStatement)
1585 686 : sqlite3_finalize(m_poInsertStatement);
1586 :
1587 4432 : if (m_poGetFeatureStatement)
1588 34 : sqlite3_finalize(m_poGetFeatureStatement);
1589 :
1590 4432 : CancelAsyncNextArrowArray();
1591 8864 : }
1592 :
1593 : /************************************************************************/
1594 : /* CancelAsyncNextArrowArray() */
1595 : /************************************************************************/
1596 :
1597 336577 : void OGRGeoPackageTableLayer::CancelAsyncNextArrowArray()
1598 : {
1599 336577 : if (m_poFillArrowArray)
1600 : {
1601 100 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
1602 50 : m_poFillArrowArray->nCountRows = -1;
1603 50 : m_poFillArrowArray->oCV.notify_one();
1604 : }
1605 :
1606 336577 : if (m_oThreadNextArrowArray.joinable())
1607 : {
1608 2 : m_oThreadNextArrowArray.join();
1609 : }
1610 :
1611 336577 : m_poFillArrowArray.reset();
1612 :
1613 336591 : while (!m_oQueueArrowArrayPrefetchTasks.empty())
1614 : {
1615 28 : auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
1616 14 : m_oQueueArrowArrayPrefetchTasks.pop();
1617 :
1618 : {
1619 28 : std::lock_guard oLock(task->m_oMutex);
1620 14 : task->m_bStop = true;
1621 14 : task->m_oCV.notify_one();
1622 : }
1623 14 : if (task->m_oThread.joinable())
1624 14 : task->m_oThread.join();
1625 :
1626 14 : if (task->m_psArrowArray)
1627 : {
1628 14 : if (task->m_psArrowArray->release)
1629 14 : task->m_psArrowArray->release(task->m_psArrowArray.get());
1630 : }
1631 : }
1632 336577 : }
1633 :
1634 : /************************************************************************/
1635 : /* InitView() */
1636 : /************************************************************************/
1637 :
1638 1074 : void OGRGeoPackageTableLayer::InitView()
1639 : {
1640 : #ifdef SQLITE_HAS_COLUMN_METADATA
1641 1074 : if (!m_bIsTable)
1642 : {
1643 : /* Detect if the view columns have the FID and geom columns of a */
1644 : /* table that has itself a spatial index */
1645 8 : sqlite3_stmt *hStmt = nullptr;
1646 8 : char *pszSQL = sqlite3_mprintf("SELECT * FROM \"%w\"", m_pszTableName);
1647 8 : CPL_IGNORE_RET_VAL(
1648 8 : sqlite3_prepare_v2(m_poDS->GetDB(), pszSQL, -1, &hStmt, nullptr));
1649 8 : sqlite3_free(pszSQL);
1650 8 : if (hStmt)
1651 : {
1652 8 : if (sqlite3_step(hStmt) == SQLITE_ROW)
1653 : {
1654 8 : OGRGeoPackageTableLayer *poLayerGeom = nullptr;
1655 8 : const int nRawColumns = sqlite3_column_count(hStmt);
1656 33 : for (int iCol = 0; iCol < nRawColumns; iCol++)
1657 : {
1658 : CPLString osColName(
1659 50 : SQLUnescape(sqlite3_column_name(hStmt, iCol)));
1660 : const char *pszTableName =
1661 25 : sqlite3_column_table_name(hStmt, iCol);
1662 : const char *pszOriginName =
1663 25 : sqlite3_column_origin_name(hStmt, iCol);
1664 26 : if (EQUAL(osColName, "OGC_FID") &&
1665 1 : (pszOriginName == nullptr ||
1666 1 : osColName != pszOriginName))
1667 : {
1668 : // in the case we have a OGC_FID column, and that
1669 : // is not the name of the original column, then
1670 : // interpret this as an explicit intent to be a
1671 : // PKID.
1672 : // We cannot just take the FID of a source table as
1673 : // a FID because of potential joins that would result
1674 : // in multiple records with same source FID.
1675 1 : CPLFree(m_pszFidColumn);
1676 1 : m_pszFidColumn = CPLStrdup(osColName);
1677 1 : m_poFeatureDefn->DeleteFieldDefn(
1678 1 : m_poFeatureDefn->GetFieldIndex(osColName));
1679 : }
1680 32 : else if (iCol == 0 &&
1681 8 : sqlite3_column_type(hStmt, iCol) == SQLITE_INTEGER)
1682 : {
1683 : // Assume the first column of integer type is the FID
1684 : // column per the latest requirements of the GPKG spec
1685 6 : CPLFree(m_pszFidColumn);
1686 6 : m_pszFidColumn = CPLStrdup(osColName);
1687 6 : m_poFeatureDefn->DeleteFieldDefn(
1688 6 : m_poFeatureDefn->GetFieldIndex(osColName));
1689 : }
1690 18 : else if (pszTableName != nullptr &&
1691 : pszOriginName != nullptr)
1692 : {
1693 : OGRGeoPackageTableLayer *poLayer =
1694 0 : dynamic_cast<OGRGeoPackageTableLayer *>(
1695 16 : m_poDS->GetLayerByName(pszTableName));
1696 16 : if (poLayer != nullptr &&
1697 32 : osColName == GetGeometryColumn() &&
1698 6 : strcmp(pszOriginName,
1699 : poLayer->GetGeometryColumn()) == 0)
1700 : {
1701 6 : poLayerGeom = poLayer;
1702 : }
1703 : }
1704 : }
1705 :
1706 8 : if (poLayerGeom != nullptr && poLayerGeom->HasSpatialIndex())
1707 : {
1708 9 : for (int iCol = 0; iCol < nRawColumns; iCol++)
1709 : {
1710 : const std::string osColName(
1711 9 : SQLUnescape(sqlite3_column_name(hStmt, iCol)));
1712 : const char *pszTableName =
1713 9 : sqlite3_column_table_name(hStmt, iCol);
1714 : const char *pszOriginName =
1715 9 : sqlite3_column_origin_name(hStmt, iCol);
1716 9 : if (pszTableName != nullptr && pszOriginName != nullptr)
1717 : {
1718 : OGRGeoPackageTableLayer *poLayer =
1719 0 : dynamic_cast<OGRGeoPackageTableLayer *>(
1720 8 : m_poDS->GetLayerByName(pszTableName));
1721 16 : if (poLayer != nullptr && poLayer == poLayerGeom &&
1722 8 : strcmp(pszOriginName,
1723 : poLayer->GetFIDColumn()) == 0)
1724 : {
1725 6 : m_bHasSpatialIndex = true;
1726 6 : m_osRTreeName = poLayerGeom->m_osRTreeName;
1727 6 : m_osFIDForRTree = osColName;
1728 6 : break;
1729 : }
1730 : }
1731 : }
1732 : }
1733 : }
1734 8 : sqlite3_finalize(hStmt);
1735 : }
1736 :
1737 : /* Update the columns string */
1738 8 : BuildColumns();
1739 : }
1740 : #endif
1741 1074 : }
1742 :
1743 : /************************************************************************/
1744 : /* CheckUpdatableTable() */
1745 : /************************************************************************/
1746 :
1747 5726 : bool OGRGeoPackageTableLayer::CheckUpdatableTable(const char *pszOperation)
1748 : {
1749 5726 : if (!m_poDS->GetUpdate())
1750 : {
1751 4 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1752 : pszOperation);
1753 4 : return false;
1754 : }
1755 : /* -------------------------------------------------------------------- */
1756 : /* Check that is a table and not a view */
1757 : /* -------------------------------------------------------------------- */
1758 5722 : if (!m_bIsTable)
1759 : {
1760 6 : CPLError(CE_Failure, CPLE_AppDefined, "Layer %s is not a table",
1761 : m_pszTableName);
1762 6 : return false;
1763 : }
1764 5716 : return true;
1765 : }
1766 :
1767 : /************************************************************************/
1768 : /* CreateField() */
1769 : /************************************************************************/
1770 :
1771 4520 : OGRErr OGRGeoPackageTableLayer::CreateField(const OGRFieldDefn *poField,
1772 : int /* bApproxOK */)
1773 : {
1774 4520 : if (!m_bFeatureDefnCompleted)
1775 0 : GetLayerDefn();
1776 4520 : if (!CheckUpdatableTable("CreateField"))
1777 1 : return OGRERR_FAILURE;
1778 :
1779 9038 : OGRFieldDefn oFieldDefn(poField);
1780 4519 : int nMaxWidth = 0;
1781 4519 : if (m_bPreservePrecision && poField->GetType() == OFTString)
1782 2962 : nMaxWidth = poField->GetWidth();
1783 : else
1784 1557 : oFieldDefn.SetWidth(0);
1785 4519 : oFieldDefn.SetPrecision(0);
1786 :
1787 4519 : if (m_bLaunder)
1788 1 : oFieldDefn.SetName(
1789 2 : GDALGeoPackageDataset::LaunderName(oFieldDefn.GetNameRef())
1790 : .c_str());
1791 :
1792 4519 : if (m_poFeatureDefn->GetFieldIndex(oFieldDefn.GetNameRef()) >= 0)
1793 : {
1794 3 : CPLError(CE_Failure, CPLE_AppDefined,
1795 : "Cannot create field %s. "
1796 : "A field with the same name already exists.",
1797 : oFieldDefn.GetNameRef());
1798 3 : return OGRERR_FAILURE;
1799 : }
1800 :
1801 4516 : if (m_poFeatureDefn->GetGeomFieldIndex(oFieldDefn.GetNameRef()) >= 0)
1802 : {
1803 7 : CPLError(CE_Failure, CPLE_AppDefined,
1804 : "Cannot create field %s. "
1805 : "It has the same name as the geometry field.",
1806 : oFieldDefn.GetNameRef());
1807 7 : return OGRERR_FAILURE;
1808 : }
1809 :
1810 13527 : if (m_pszFidColumn != nullptr &&
1811 4516 : EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn) &&
1812 12 : poField->GetType() != OFTInteger &&
1813 9024 : poField->GetType() != OFTInteger64 &&
1814 : // typically a GeoPackage exported with QGIS as a shapefile and
1815 : // re-imported See https://github.com/qgis/QGIS/pull/43118
1816 6 : !(poField->GetType() == OFTReal && poField->GetWidth() == 20 &&
1817 1 : poField->GetPrecision() == 0))
1818 : {
1819 3 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
1820 : oFieldDefn.GetNameRef());
1821 3 : return OGRERR_FAILURE;
1822 : }
1823 :
1824 : const int nMaxColumns =
1825 4506 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_COLUMN, -1);
1826 : // + 1 for the FID column
1827 4506 : if (m_poFeatureDefn->GetFieldCount() +
1828 4506 : m_poFeatureDefn->GetGeomFieldCount() + 1 >=
1829 : nMaxColumns)
1830 : {
1831 1 : CPLError(CE_Failure, CPLE_AppDefined,
1832 : "Cannot add field %s. Limit of %d columns reached",
1833 : oFieldDefn.GetNameRef(), nMaxColumns);
1834 1 : return OGRERR_FAILURE;
1835 : }
1836 :
1837 4505 : if (!m_bDeferredCreation)
1838 : {
1839 15 : CPLString osCommand;
1840 :
1841 : // ADD COLUMN has several restrictions
1842 : // See https://www.sqlite.org/lang_altertable.html#altertabaddcol
1843 :
1844 : osCommand.Printf("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s",
1845 30 : SQLEscapeName(m_pszTableName).c_str(),
1846 30 : SQLEscapeName(oFieldDefn.GetNameRef()).c_str(),
1847 : GPkgFieldFromOGR(poField->GetType(),
1848 45 : poField->GetSubType(), nMaxWidth));
1849 15 : if (!poField->IsNullable())
1850 1 : osCommand += " NOT NULL";
1851 15 : if (poField->IsUnique())
1852 : {
1853 : // this will fail when SQLCommand() is run, as it is not allowed
1854 : // by SQLite. This is a bit of an artificial restriction.
1855 : // We could override it by rewriting the table.
1856 1 : osCommand += " UNIQUE";
1857 : }
1858 18 : if (poField->GetDefault() != nullptr &&
1859 3 : !poField->IsDefaultDriverSpecific())
1860 : {
1861 3 : osCommand += " DEFAULT ";
1862 3 : int nYear = 0;
1863 3 : int nMonth = 0;
1864 3 : int nDay = 0;
1865 3 : int nHour = 0;
1866 3 : int nMinute = 0;
1867 3 : float fSecond = 0.0f;
1868 5 : if (poField->GetType() == OFTDateTime &&
1869 2 : sscanf(poField->GetDefault(), "'%d/%d/%d %d:%d:%f'", &nYear,
1870 : &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6)
1871 : {
1872 2 : if (strchr(poField->GetDefault(), '.') == nullptr)
1873 : osCommand += CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%02dZ'",
1874 : nYear, nMonth, nDay, nHour, nMinute,
1875 1 : static_cast<int>(fSecond + 0.5));
1876 : else
1877 : osCommand +=
1878 : CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%06.3fZ'", nYear,
1879 1 : nMonth, nDay, nHour, nMinute, fSecond);
1880 : }
1881 : else
1882 : {
1883 : // This could fail if it is CURRENT_TIMESTAMP, etc.
1884 1 : osCommand += poField->GetDefault();
1885 : }
1886 : }
1887 12 : else if (!poField->IsNullable())
1888 : {
1889 : // SQLite mandates a DEFAULT value when adding a NOT NULL column in
1890 : // an ALTER TABLE ADD COLUMN.
1891 1 : osCommand += " DEFAULT ''";
1892 : }
1893 :
1894 15 : OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
1895 :
1896 15 : if (err != OGRERR_NONE)
1897 1 : return err;
1898 :
1899 14 : if (!DoSpecialProcessingForColumnCreation(poField))
1900 : {
1901 0 : return OGRERR_FAILURE;
1902 : }
1903 : }
1904 :
1905 4504 : whileUnsealing(m_poFeatureDefn)->AddFieldDefn(&oFieldDefn);
1906 :
1907 4504 : if (m_poDS->IsInTransaction())
1908 : {
1909 : m_apoFieldDefnChanges.emplace_back(
1910 503 : std::make_unique<OGRFieldDefn>(oFieldDefn),
1911 503 : m_poFeatureDefn->GetFieldCount() - 1, FieldChangeType::ADD_FIELD,
1912 1509 : m_poDS->GetCurrentSavepoint());
1913 : }
1914 :
1915 9008 : if (m_pszFidColumn != nullptr &&
1916 4504 : EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn))
1917 : {
1918 4 : m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
1919 : }
1920 :
1921 4504 : if (!m_bDeferredCreation)
1922 : {
1923 14 : ResetReading();
1924 : }
1925 :
1926 4504 : return OGRERR_NONE;
1927 : }
1928 :
1929 : /************************************************************************/
1930 : /* DoSpecialProcessingForColumnCreation() */
1931 : /************************************************************************/
1932 :
1933 4512 : bool OGRGeoPackageTableLayer::DoSpecialProcessingForColumnCreation(
1934 : const OGRFieldDefn *poField)
1935 : {
1936 4512 : const std::string &osConstraintName(poField->GetDomainName());
1937 9024 : const std::string osName(poField->GetAlternativeNameRef());
1938 4512 : const std::string &osDescription(poField->GetComment());
1939 :
1940 9024 : std::string osMimeType;
1941 4512 : if (poField->GetType() == OFTString && poField->GetSubType() == OFSTJSON)
1942 : {
1943 16 : osMimeType = "application/json";
1944 : }
1945 :
1946 9004 : if (osConstraintName.empty() && osName.empty() && osDescription.empty() &&
1947 4492 : osMimeType.empty())
1948 : {
1949 : // no record required
1950 4476 : return true;
1951 : }
1952 :
1953 36 : if (!m_poDS->CreateColumnsTableAndColumnConstraintsTablesIfNecessary())
1954 0 : return false;
1955 :
1956 : /* Now let's register our column. */
1957 72 : std::string osNameSqlValue;
1958 36 : if (osName.empty())
1959 : {
1960 26 : osNameSqlValue = "NULL";
1961 : }
1962 : else
1963 : {
1964 10 : char *pszName = sqlite3_mprintf("'%q'", osName.c_str());
1965 10 : osNameSqlValue = std::string(pszName);
1966 10 : sqlite3_free(pszName);
1967 : }
1968 :
1969 72 : std::string osDescriptionSqlValue;
1970 36 : if (osDescription.empty())
1971 : {
1972 31 : osDescriptionSqlValue = "NULL";
1973 : }
1974 : else
1975 : {
1976 5 : char *pszDescription = sqlite3_mprintf("'%q'", osDescription.c_str());
1977 5 : osDescriptionSqlValue = std::string(pszDescription);
1978 5 : sqlite3_free(pszDescription);
1979 : }
1980 :
1981 72 : std::string osMimeTypeSqlValue;
1982 36 : if (osMimeType.empty())
1983 : {
1984 20 : osMimeTypeSqlValue = "NULL";
1985 : }
1986 : else
1987 : {
1988 16 : char *pszMimeType = sqlite3_mprintf("'%q'", osMimeType.c_str());
1989 16 : osMimeTypeSqlValue = std::string(pszMimeType);
1990 16 : sqlite3_free(pszMimeType);
1991 : }
1992 :
1993 36 : std::string osConstraintNameValue;
1994 36 : if (osConstraintName.empty())
1995 : {
1996 26 : osConstraintNameValue = "NULL";
1997 : }
1998 : else
1999 : {
2000 : char *pszConstraintName =
2001 10 : sqlite3_mprintf("'%q'", osConstraintName.c_str());
2002 10 : osConstraintNameValue = std::string(pszConstraintName);
2003 10 : sqlite3_free(pszConstraintName);
2004 : }
2005 :
2006 36 : char *pszSQL = sqlite3_mprintf(
2007 : "INSERT INTO gpkg_data_columns (table_name, column_name, name, "
2008 : "title, description, mime_type, constraint_name) VALUES ("
2009 : "'%q', '%q', %s, NULL, %s, %s, %s)",
2010 : m_pszTableName, poField->GetNameRef(), osNameSqlValue.c_str(),
2011 : osDescriptionSqlValue.c_str(), osMimeTypeSqlValue.c_str(),
2012 : osConstraintNameValue.c_str());
2013 :
2014 36 : bool ok = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
2015 36 : sqlite3_free(pszSQL);
2016 36 : return ok;
2017 : }
2018 :
2019 : /************************************************************************/
2020 : /* CreateGeomField() */
2021 : /************************************************************************/
2022 :
2023 : OGRErr
2024 6 : OGRGeoPackageTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
2025 : int /* bApproxOK */)
2026 : {
2027 6 : if (!m_bFeatureDefnCompleted)
2028 1 : GetLayerDefn();
2029 6 : if (!CheckUpdatableTable("CreateGeomField"))
2030 1 : return OGRERR_FAILURE;
2031 :
2032 5 : if (m_poFeatureDefn->GetGeomFieldCount() == 1)
2033 : {
2034 2 : CPLError(CE_Failure, CPLE_AppDefined,
2035 : "Cannot create more than one geometry field in GeoPackage");
2036 2 : return OGRERR_FAILURE;
2037 : }
2038 :
2039 3 : OGRwkbGeometryType eType = poGeomFieldIn->GetType();
2040 3 : if (eType == wkbNone)
2041 : {
2042 0 : CPLError(CE_Failure, CPLE_AppDefined,
2043 : "Cannot create geometry field of type wkbNone");
2044 0 : return OGRERR_FAILURE;
2045 : }
2046 :
2047 6 : OGRGeomFieldDefn oGeomField(poGeomFieldIn);
2048 3 : const auto poSRSOri = poGeomFieldIn->GetSpatialRef();
2049 3 : if (poSRSOri)
2050 : {
2051 0 : auto poSRS = OGRSpatialReferenceRefCountedPtr::makeClone(poSRSOri);
2052 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2053 0 : oGeomField.SetSpatialRef(poSRS.get());
2054 : }
2055 3 : if (EQUAL(oGeomField.GetNameRef(), ""))
2056 : {
2057 1 : oGeomField.SetName("geom");
2058 : }
2059 :
2060 3 : const OGRSpatialReference *poSRS = oGeomField.GetSpatialRef();
2061 3 : m_iSrs = m_poDS->GetSrsId(poSRS);
2062 :
2063 : /* -------------------------------------------------------------------- */
2064 : /* Create the new field. */
2065 : /* -------------------------------------------------------------------- */
2066 3 : if (!m_bDeferredCreation)
2067 : {
2068 4 : char *pszSQL = sqlite3_mprintf(
2069 : "ALTER TABLE \"%w\" ADD COLUMN \"%w\" %s%s"
2070 : ";"
2071 : "UPDATE gpkg_contents SET data_type = 'features' "
2072 : "WHERE lower(table_name) = lower('%q')",
2073 : m_pszTableName, oGeomField.GetNameRef(),
2074 2 : m_poDS->GetGeometryTypeString(oGeomField.GetType()),
2075 2 : !oGeomField.IsNullable() ? " NOT NULL DEFAULT ''" : "",
2076 : m_pszTableName);
2077 2 : CPLString osSQL(pszSQL);
2078 2 : sqlite3_free(pszSQL);
2079 :
2080 2 : OGRErr err = SQLCommand(m_poDS->GetDB(), osSQL);
2081 2 : if (err != OGRERR_NONE)
2082 0 : return err;
2083 : }
2084 :
2085 3 : if (m_poDS->IsInTransaction())
2086 : {
2087 : m_apoGeomFieldDefnChanges.emplace_back(
2088 0 : std::make_unique<OGRGeomFieldDefn>(oGeomField),
2089 0 : m_poFeatureDefn->GetGeomFieldCount(), FieldChangeType::ADD_FIELD);
2090 : }
2091 :
2092 3 : whileUnsealing(m_poFeatureDefn)->AddGeomFieldDefn(&oGeomField);
2093 :
2094 3 : if (!m_bDeferredCreation)
2095 : {
2096 2 : OGRErr err = RegisterGeometryColumn();
2097 2 : if (err != OGRERR_NONE)
2098 0 : return err;
2099 :
2100 2 : ResetReading();
2101 : }
2102 :
2103 3 : return OGRERR_NONE;
2104 : }
2105 :
2106 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2107 :
2108 : /************************************************************************/
2109 : /* DisableFeatureCount() */
2110 : /************************************************************************/
2111 :
2112 242 : void OGRGeoPackageTableLayer::DisableFeatureCount()
2113 : {
2114 242 : m_nTotalFeatureCount = -1;
2115 242 : }
2116 :
2117 : /************************************************************************/
2118 : /* CreateFeatureCountTriggers() */
2119 : /************************************************************************/
2120 :
2121 5961 : void OGRGeoPackageTableLayer::CreateFeatureCountTriggers(
2122 : const char *pszTableName)
2123 : {
2124 5961 : if (m_bAddOGRFeatureCountTriggers)
2125 : {
2126 1087 : if (pszTableName == nullptr)
2127 1078 : pszTableName = m_pszTableName;
2128 :
2129 1087 : m_bOGRFeatureCountTriggersEnabled = true;
2130 1087 : m_bAddOGRFeatureCountTriggers = false;
2131 1087 : m_bFeatureCountTriggersDeletedInTransaction = false;
2132 :
2133 1087 : CPLDebug("GPKG", "Creating insert/delete feature_count triggers");
2134 1087 : char *pszSQL = sqlite3_mprintf(
2135 : "CREATE TRIGGER \"trigger_insert_feature_count_%w\" "
2136 : "AFTER INSERT ON \"%w\" "
2137 : "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
2138 : "feature_count + 1 WHERE lower(table_name) = lower('%q'); END;",
2139 : pszTableName, pszTableName, pszTableName);
2140 1087 : SQLCommand(m_poDS->GetDB(), pszSQL);
2141 1087 : sqlite3_free(pszSQL);
2142 :
2143 1087 : pszSQL = sqlite3_mprintf(
2144 : "CREATE TRIGGER \"trigger_delete_feature_count_%w\" "
2145 : "AFTER DELETE ON \"%w\" "
2146 : "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
2147 : "feature_count - 1 WHERE lower(table_name) = lower('%q'); END;",
2148 : pszTableName, pszTableName, pszTableName);
2149 1087 : SQLCommand(m_poDS->GetDB(), pszSQL);
2150 1087 : sqlite3_free(pszSQL);
2151 : }
2152 5961 : }
2153 :
2154 : /************************************************************************/
2155 : /* DisableFeatureCountTriggers() */
2156 : /************************************************************************/
2157 :
2158 70 : void OGRGeoPackageTableLayer::DisableFeatureCountTriggers(
2159 : bool bNullifyFeatureCount)
2160 : {
2161 70 : if (m_bOGRFeatureCountTriggersEnabled)
2162 : {
2163 70 : m_bOGRFeatureCountTriggersEnabled = false;
2164 70 : m_bAddOGRFeatureCountTriggers = true;
2165 70 : m_bFeatureCountTriggersDeletedInTransaction = m_poDS->IsInTransaction();
2166 :
2167 70 : CPLDebug("GPKG", "Deleting insert/delete feature_count triggers");
2168 :
2169 70 : char *pszSQL = sqlite3_mprintf(
2170 : "DROP TRIGGER \"trigger_insert_feature_count_%w\"", m_pszTableName);
2171 70 : SQLCommand(m_poDS->GetDB(), pszSQL);
2172 70 : sqlite3_free(pszSQL);
2173 :
2174 70 : pszSQL = sqlite3_mprintf(
2175 : "DROP TRIGGER \"trigger_delete_feature_count_%w\"", m_pszTableName);
2176 70 : SQLCommand(m_poDS->GetDB(), pszSQL);
2177 70 : sqlite3_free(pszSQL);
2178 :
2179 70 : if (m_poDS->m_bHasGPKGOGRContents && bNullifyFeatureCount)
2180 : {
2181 61 : pszSQL = sqlite3_mprintf(
2182 : "UPDATE gpkg_ogr_contents SET feature_count = NULL WHERE "
2183 : "lower(table_name )= lower('%q')",
2184 : m_pszTableName);
2185 61 : SQLCommand(m_poDS->GetDB(), pszSQL);
2186 61 : sqlite3_free(pszSQL);
2187 : }
2188 : }
2189 70 : }
2190 :
2191 : #endif // #ifdef ENABLE_GPKG_OGR_CONTENTS
2192 :
2193 : /************************************************************************/
2194 : /* CheckGeometryType() */
2195 : /************************************************************************/
2196 :
2197 : /** Check that the feature geometry type is consistent with the layer geometry
2198 : * type.
2199 : *
2200 : * And potentially update the Z and M flags of gpkg_geometry_columns to
2201 : * reflect the dimensionality of feature geometries.
2202 : */
2203 268847 : void OGRGeoPackageTableLayer::CheckGeometryType(const OGRFeature *poFeature)
2204 : {
2205 268847 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
2206 268847 : const OGRwkbGeometryType eFlattenLayerGeomType = wkbFlatten(eLayerGeomType);
2207 268847 : const OGRGeometry *poGeom = poFeature->GetGeometryRef();
2208 268847 : if (eFlattenLayerGeomType != wkbNone && eFlattenLayerGeomType != wkbUnknown)
2209 : {
2210 17709 : if (poGeom != nullptr)
2211 : {
2212 : OGRwkbGeometryType eGeomType =
2213 17532 : wkbFlatten(poGeom->getGeometryType());
2214 17548 : if (!OGR_GT_IsSubClassOf(eGeomType, eFlattenLayerGeomType) &&
2215 16 : !cpl::contains(m_eSetBadGeomTypeWarned, eGeomType))
2216 : {
2217 15 : CPLError(CE_Warning, CPLE_AppDefined,
2218 : "A geometry of type %s is inserted into layer %s "
2219 : "of geometry type %s, which is not normally allowed "
2220 : "by the GeoPackage specification, but the driver will "
2221 : "however do it. "
2222 : "To create a conformant GeoPackage, if using ogr2ogr, "
2223 : "the -nlt option can be used to override the layer "
2224 : "geometry type. "
2225 : "This warning will no longer be emitted for this "
2226 : "combination of layer and feature geometry type.",
2227 : OGRToOGCGeomType(eGeomType), GetName(),
2228 : OGRToOGCGeomType(eFlattenLayerGeomType));
2229 15 : m_eSetBadGeomTypeWarned.insert(eGeomType);
2230 : }
2231 : }
2232 : }
2233 :
2234 : // Make sure to update the z and m columns of gpkg_geometry_columns to 2
2235 : // if we have geometries with Z and M components
2236 268847 : if (m_nZFlag == 0 || m_nMFlag == 0)
2237 : {
2238 268842 : if (poGeom != nullptr)
2239 : {
2240 264018 : bool bUpdateGpkgGeometryColumnsTable = false;
2241 264018 : const OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
2242 264018 : if (m_nZFlag == 0 && wkbHasZ(eGeomType))
2243 : {
2244 18 : if (eLayerGeomType != wkbUnknown && !wkbHasZ(eLayerGeomType))
2245 : {
2246 2 : CPLError(
2247 : CE_Warning, CPLE_AppDefined,
2248 : "Layer '%s' has been declared with non-Z geometry type "
2249 : "%s, but it does contain geometries with Z. Setting "
2250 : "the Z=2 hint into gpkg_geometry_columns. "
2251 : "If using ogr2ogr, specify the -nlt option to avoid "
2252 : "ambiguity.",
2253 : GetName(),
2254 : OGRToOGCGeomType(eLayerGeomType, true, true, true));
2255 : }
2256 18 : m_nZFlag = 2;
2257 18 : bUpdateGpkgGeometryColumnsTable = true;
2258 : }
2259 264018 : if (m_nMFlag == 0 && wkbHasM(eGeomType))
2260 : {
2261 8 : if (eLayerGeomType != wkbUnknown && !wkbHasM(eLayerGeomType))
2262 : {
2263 1 : CPLError(
2264 : CE_Warning, CPLE_AppDefined,
2265 : "Layer '%s' has been declared with non-M geometry type "
2266 : "%s, but it does contain geometries with M. Setting "
2267 : "the M=2 hint into gpkg_geometry_columns. "
2268 : "If using ogr2ogr, specify the -nlt option to avoid "
2269 : "ambiguity.",
2270 : GetName(),
2271 : OGRToOGCGeomType(eLayerGeomType, true, true, true));
2272 : }
2273 8 : m_nMFlag = 2;
2274 8 : bUpdateGpkgGeometryColumnsTable = true;
2275 : }
2276 264018 : if (bUpdateGpkgGeometryColumnsTable)
2277 : {
2278 : /* Update gpkg_geometry_columns */
2279 21 : char *pszSQL = sqlite3_mprintf(
2280 : "UPDATE gpkg_geometry_columns SET z = %d, m = %d WHERE "
2281 : "table_name = '%q' AND column_name = '%q'",
2282 : m_nZFlag, m_nMFlag, GetName(), GetGeometryColumn());
2283 21 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
2284 21 : sqlite3_free(pszSQL);
2285 : }
2286 : }
2287 : }
2288 268847 : }
2289 :
2290 : /************************************************************************/
2291 : /* CheckFIDAndFIDColumnConsistency() */
2292 : /************************************************************************/
2293 :
2294 16 : static bool CheckFIDAndFIDColumnConsistency(const OGRFeature *poFeature,
2295 : int iFIDAsRegularColumnIndex)
2296 : {
2297 16 : bool ok = true;
2298 16 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
2299 : {
2300 : // nothing to do
2301 : }
2302 14 : else if (poFeature->GetDefnRef()
2303 14 : ->GetFieldDefn(iFIDAsRegularColumnIndex)
2304 14 : ->GetType() == OFTReal)
2305 : {
2306 : const double dfFID =
2307 4 : poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex);
2308 4 : if (GDALIsValueInRange<int64_t>(dfFID))
2309 : {
2310 4 : const auto nFID = static_cast<GIntBig>(dfFID);
2311 4 : if (nFID != poFeature->GetFID())
2312 : {
2313 1 : ok = false;
2314 1 : CPLError(CE_Failure, CPLE_AppDefined,
2315 : "Inconsistent values of FID (" CPL_FRMT_GIB
2316 : ") and field of same name (%g)",
2317 : poFeature->GetFID(),
2318 : poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex));
2319 : }
2320 : }
2321 : }
2322 20 : else if (poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
2323 10 : poFeature->GetFID())
2324 : {
2325 3 : ok = false;
2326 3 : CPLError(CE_Failure, CPLE_AppDefined,
2327 : "Inconsistent values of FID (" CPL_FRMT_GIB
2328 : ") and field of same name (" CPL_FRMT_GIB ")",
2329 : poFeature->GetFID(),
2330 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
2331 : }
2332 16 : return ok;
2333 : }
2334 :
2335 : /************************************************************************/
2336 : /* ICreateFeature() */
2337 : /************************************************************************/
2338 :
2339 : // rtreeValueDown() / rtreeValueUp() come from SQLite3 source code
2340 : // SQLite3 RTree stores min/max values as float. So do the same for our
2341 : // GPKGRTreeEntry
2342 :
2343 : /*
2344 : ** Rounding constants for float->double conversion.
2345 : */
2346 : #define RNDTOWARDS (1.0 - 1.0 / 8388608.0) /* Round towards zero */
2347 : #define RNDAWAY (1.0 + 1.0 / 8388608.0) /* Round away from zero */
2348 :
2349 : /*
2350 : ** Convert an sqlite3_value into an RtreeValue (presumably a float)
2351 : ** while taking care to round toward negative or positive, respectively.
2352 : */
2353 495644 : static float rtreeValueDown(double d)
2354 : {
2355 495644 : float f = static_cast<float>(d);
2356 495644 : if (f > d)
2357 : {
2358 13089 : f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
2359 : }
2360 495644 : return f;
2361 : }
2362 :
2363 495644 : static float rtreeValueUp(double d)
2364 : {
2365 495644 : float f = static_cast<float>(d);
2366 495644 : if (f < d)
2367 : {
2368 13191 : f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
2369 : }
2370 495644 : return f;
2371 : }
2372 :
2373 268778 : OGRErr OGRGeoPackageTableLayer::CreateOrUpsertFeature(OGRFeature *poFeature,
2374 : bool bUpsert)
2375 : {
2376 268778 : if (!m_bFeatureDefnCompleted)
2377 0 : GetLayerDefn();
2378 268778 : if (!m_poDS->GetUpdate())
2379 : {
2380 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2381 : "CreateFeature");
2382 0 : return OGRERR_FAILURE;
2383 : }
2384 :
2385 268778 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2386 0 : return OGRERR_FAILURE;
2387 :
2388 268778 : CancelAsyncNextArrowArray();
2389 :
2390 537556 : std::string osUpsertUniqueColumnName;
2391 268778 : if (bUpsert && poFeature->GetFID() == OGRNullFID)
2392 : {
2393 15 : int nUniqueColumns = 0;
2394 15 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2395 47 : for (int i = 0; i < nFieldCount; ++i)
2396 : {
2397 32 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
2398 32 : if (poFieldDefn->IsUnique())
2399 : {
2400 15 : if (osUpsertUniqueColumnName.empty())
2401 15 : osUpsertUniqueColumnName = poFieldDefn->GetNameRef();
2402 15 : nUniqueColumns++;
2403 : }
2404 : }
2405 15 : if (nUniqueColumns == 0)
2406 : {
2407 : // This is just a regular INSERT
2408 0 : bUpsert = false;
2409 : }
2410 : }
2411 :
2412 268778 : if (bUpsert)
2413 : {
2414 27 : if (m_bThreadRTreeStarted)
2415 0 : CancelAsyncRTree();
2416 27 : if (!RunDeferredSpatialIndexUpdate())
2417 0 : return OGRERR_FAILURE;
2418 27 : if (!m_bUpdate1TriggerDisabled && HasSpatialIndex())
2419 18 : WorkaroundUpdate1TriggerIssue();
2420 : }
2421 :
2422 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2423 268778 : if (bUpsert)
2424 : {
2425 27 : if (m_nTotalFeatureCount >= 0)
2426 : {
2427 : // There's no reliable way of knowing if a new row has been inserted
2428 : // or just updated, so serialize known value and then
2429 : // invalidate feature count.
2430 10 : if (m_poDS->m_bHasGPKGOGRContents)
2431 : {
2432 : const char *pszCount =
2433 10 : CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
2434 10 : char *pszSQL = sqlite3_mprintf(
2435 : "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
2436 : "lower(table_name )= lower('%q')",
2437 : pszCount, m_pszTableName);
2438 10 : SQLCommand(m_poDS->GetDB(), pszSQL);
2439 10 : sqlite3_free(pszSQL);
2440 : }
2441 10 : m_nTotalFeatureCount = -1;
2442 :
2443 10 : if (!m_bOGRFeatureCountTriggersEnabled)
2444 1 : CreateFeatureCountTriggers();
2445 : }
2446 : }
2447 : else
2448 : {
2449 : // To maximize performance of insertion, disable feature count triggers
2450 268751 : if (m_bOGRFeatureCountTriggersEnabled)
2451 : {
2452 44 : DisableFeatureCountTriggers();
2453 : }
2454 : }
2455 : #endif
2456 :
2457 268778 : CheckGeometryType(poFeature);
2458 :
2459 : /* Substitute default values for null Date/DateTime fields as the standard
2460 : */
2461 : /* format of SQLite is not the one mandated by GeoPackage */
2462 268778 : poFeature->FillUnsetWithDefault(FALSE, nullptr);
2463 268778 : bool bHasDefaultValue = false;
2464 268778 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2465 4736870 : for (int iField = 0; iField < nFieldCount; iField++)
2466 : {
2467 4468100 : if (poFeature->IsFieldSetUnsafe(iField))
2468 4466840 : continue;
2469 : const char *pszDefault =
2470 1257 : m_poFeatureDefn->GetFieldDefnUnsafe(iField)->GetDefault();
2471 1257 : if (pszDefault != nullptr)
2472 : {
2473 1 : bHasDefaultValue = true;
2474 : }
2475 : }
2476 :
2477 : /* In case the FID column has also been created as a regular field */
2478 268778 : if (m_iFIDAsRegularColumnIndex >= 0)
2479 : {
2480 13 : if (poFeature->GetFID() == OGRNullFID)
2481 : {
2482 6 : if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
2483 : {
2484 10 : if (m_poFeatureDefn->GetFieldDefn(m_iFIDAsRegularColumnIndex)
2485 5 : ->GetType() == OFTReal)
2486 : {
2487 2 : bool ok = false;
2488 : const double dfFID =
2489 2 : poFeature->GetFieldAsDouble(m_iFIDAsRegularColumnIndex);
2490 4 : if (dfFID >= static_cast<double>(
2491 4 : std::numeric_limits<int64_t>::min()) &&
2492 2 : dfFID <= static_cast<double>(
2493 2 : std::numeric_limits<int64_t>::max()))
2494 : {
2495 2 : const auto nFID = static_cast<GIntBig>(dfFID);
2496 2 : if (static_cast<double>(nFID) == dfFID)
2497 : {
2498 1 : poFeature->SetFID(nFID);
2499 1 : ok = true;
2500 : }
2501 : }
2502 2 : if (!ok)
2503 : {
2504 1 : CPLError(
2505 : CE_Failure, CPLE_AppDefined,
2506 : "Value of FID %g cannot be parsed to an Integer64",
2507 : dfFID);
2508 1 : return OGRERR_FAILURE;
2509 : }
2510 : }
2511 : else
2512 : {
2513 3 : poFeature->SetFID(poFeature->GetFieldAsInteger64(
2514 3 : m_iFIDAsRegularColumnIndex));
2515 : }
2516 : }
2517 : }
2518 7 : else if (!CheckFIDAndFIDColumnConsistency(poFeature,
2519 : m_iFIDAsRegularColumnIndex))
2520 : {
2521 3 : return OGRERR_FAILURE;
2522 : }
2523 : }
2524 :
2525 : /* If there's a unset field with a default value, then we must create */
2526 : /* a specific INSERT statement to avoid unset fields to be bound to NULL */
2527 536694 : if (m_poInsertStatement &&
2528 267920 : (bHasDefaultValue ||
2529 267920 : m_bInsertStatementWithFID != (poFeature->GetFID() != OGRNullFID) ||
2530 535835 : m_bInsertStatementWithUpsert != bUpsert ||
2531 267917 : m_osInsertStatementUpsertUniqueColumnName != osUpsertUniqueColumnName))
2532 : {
2533 3 : sqlite3_finalize(m_poInsertStatement);
2534 3 : m_poInsertStatement = nullptr;
2535 : }
2536 :
2537 268774 : if (!m_poInsertStatement)
2538 : {
2539 : /* Construct a SQL INSERT statement from the OGRFeature */
2540 : /* Only work with fields that are set */
2541 : /* Do not stick values into SQL, use placeholder and bind values later
2542 : */
2543 857 : m_bInsertStatementWithFID = poFeature->GetFID() != OGRNullFID;
2544 857 : m_bInsertStatementWithUpsert = bUpsert;
2545 857 : m_osInsertStatementUpsertUniqueColumnName = osUpsertUniqueColumnName;
2546 : CPLString osCommand = FeatureGenerateInsertSQL(
2547 857 : poFeature, m_bInsertStatementWithFID, !bHasDefaultValue, bUpsert,
2548 857 : osUpsertUniqueColumnName);
2549 :
2550 : /* Prepare the SQL into a statement */
2551 857 : sqlite3 *poDb = m_poDS->GetDB();
2552 857 : int err = SQLPrepareWithError(poDb, osCommand, -1, &m_poInsertStatement,
2553 : nullptr);
2554 857 : if (err != SQLITE_OK)
2555 : {
2556 0 : return OGRERR_FAILURE;
2557 : }
2558 : }
2559 :
2560 : /* Bind values onto the statement now */
2561 537548 : OGRErr errOgr = FeatureBindInsertParameters(poFeature, m_poInsertStatement,
2562 268774 : m_bInsertStatementWithFID,
2563 268774 : !bHasDefaultValue);
2564 268774 : if (errOgr != OGRERR_NONE)
2565 : {
2566 0 : sqlite3_reset(m_poInsertStatement);
2567 0 : sqlite3_clear_bindings(m_poInsertStatement);
2568 0 : sqlite3_finalize(m_poInsertStatement);
2569 0 : m_poInsertStatement = nullptr;
2570 0 : return errOgr;
2571 : }
2572 :
2573 : /* From here execute the statement and check errors */
2574 268774 : const int err = sqlite3_step(m_poInsertStatement);
2575 268774 : if (!(err == SQLITE_OK || err == SQLITE_DONE
2576 : #if SQLITE_VERSION_NUMBER >= 3035000L
2577 : || err == SQLITE_ROW
2578 : #endif
2579 : ))
2580 : {
2581 9 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute insert : %s",
2582 9 : sqlite3_errmsg(m_poDS->GetDB())
2583 9 : ? sqlite3_errmsg(m_poDS->GetDB())
2584 : : "");
2585 9 : sqlite3_reset(m_poInsertStatement);
2586 9 : sqlite3_clear_bindings(m_poInsertStatement);
2587 9 : sqlite3_finalize(m_poInsertStatement);
2588 9 : m_poInsertStatement = nullptr;
2589 9 : return OGRERR_FAILURE;
2590 : }
2591 :
2592 : /* Read the latest FID value */
2593 27 : const GIntBig nFID = (bUpsert && !osUpsertUniqueColumnName.empty())
2594 268765 : ?
2595 : #if SQLITE_VERSION_NUMBER >= 3035000L
2596 15 : sqlite3_column_int64(m_poInsertStatement, 0)
2597 : #else
2598 : OGRNullFID
2599 : #endif
2600 268750 : : sqlite3_last_insert_rowid(m_poDS->GetDB());
2601 :
2602 268765 : sqlite3_reset(m_poInsertStatement);
2603 268765 : sqlite3_clear_bindings(m_poInsertStatement);
2604 :
2605 268765 : if (bHasDefaultValue)
2606 : {
2607 1 : sqlite3_finalize(m_poInsertStatement);
2608 1 : m_poInsertStatement = nullptr;
2609 : }
2610 :
2611 268765 : if (nFID != OGRNullFID)
2612 : {
2613 268765 : poFeature->SetFID(nFID);
2614 268765 : if (m_iFIDAsRegularColumnIndex >= 0)
2615 9 : poFeature->SetField(m_iFIDAsRegularColumnIndex, nFID);
2616 : }
2617 : else
2618 : {
2619 0 : poFeature->SetFID(OGRNullFID);
2620 : }
2621 :
2622 : /* Update the layer extents with this new object */
2623 268765 : if (IsGeomFieldSet(poFeature))
2624 : {
2625 263975 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
2626 263975 : if (!poGeom->IsEmpty())
2627 : {
2628 262934 : OGREnvelope oEnv;
2629 262934 : poGeom->getEnvelope(&oEnv);
2630 262934 : UpdateExtent(&oEnv);
2631 :
2632 262920 : if (!bUpsert && !m_bDeferredSpatialIndexCreation &&
2633 525854 : HasSpatialIndex() && m_poDS->IsInTransaction())
2634 : {
2635 1640 : m_nCountInsertInTransaction++;
2636 1640 : if (m_nCountInsertInTransactionThreshold < 0)
2637 : {
2638 16 : m_nCountInsertInTransactionThreshold =
2639 16 : atoi(CPLGetConfigOption(
2640 : "OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "100"));
2641 : }
2642 1640 : if (m_nCountInsertInTransaction ==
2643 1640 : m_nCountInsertInTransactionThreshold)
2644 : {
2645 10 : StartDeferredSpatialIndexUpdate();
2646 : }
2647 1630 : else if (!m_aoRTreeTriggersSQL.empty())
2648 : {
2649 1190 : if (m_aoRTreeEntries.size() == 1000 * 1000)
2650 : {
2651 0 : if (!FlushPendingSpatialIndexUpdate())
2652 0 : return OGRERR_FAILURE;
2653 : }
2654 : GPKGRTreeEntry sEntry;
2655 1190 : sEntry.nId = nFID;
2656 1190 : sEntry.fMinX = rtreeValueDown(oEnv.MinX);
2657 1190 : sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
2658 1190 : sEntry.fMinY = rtreeValueDown(oEnv.MinY);
2659 1190 : sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
2660 1190 : m_aoRTreeEntries.push_back(sEntry);
2661 : }
2662 : }
2663 261294 : else if (!bUpsert && m_bAllowedRTreeThread &&
2664 250225 : !m_bErrorDuringRTreeThread)
2665 : {
2666 : GPKGRTreeEntry sEntry;
2667 : #ifdef DEBUG_VERBOSE
2668 : if (m_aoRTreeEntries.empty())
2669 : CPLDebug("GPKG",
2670 : "Starting to fill m_aoRTreeEntries at "
2671 : "FID " CPL_FRMT_GIB,
2672 : nFID);
2673 : #endif
2674 246632 : sEntry.nId = nFID;
2675 246632 : sEntry.fMinX = rtreeValueDown(oEnv.MinX);
2676 246632 : sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
2677 246632 : sEntry.fMinY = rtreeValueDown(oEnv.MinY);
2678 246632 : sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
2679 : try
2680 : {
2681 246632 : m_aoRTreeEntries.push_back(sEntry);
2682 246632 : if (m_aoRTreeEntries.size() == m_nRTreeBatchSize)
2683 : {
2684 896 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
2685 896 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
2686 : }
2687 484965 : if (!m_bThreadRTreeStarted &&
2688 238333 : m_oQueueRTreeEntries.size() ==
2689 238333 : m_nRTreeBatchesBeforeStart)
2690 : {
2691 48 : StartAsyncRTree();
2692 : }
2693 : }
2694 0 : catch (const std::bad_alloc &)
2695 : {
2696 0 : CPLDebug("GPKG",
2697 : "Memory allocation error regarding RTree "
2698 : "structures. Falling back to slower method");
2699 0 : if (m_bThreadRTreeStarted)
2700 0 : CancelAsyncRTree();
2701 : else
2702 0 : m_bAllowedRTreeThread = false;
2703 : }
2704 : }
2705 : }
2706 : }
2707 :
2708 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2709 268765 : if (m_nTotalFeatureCount >= 0)
2710 : {
2711 268668 : if (m_nTotalFeatureCount < std::numeric_limits<int64_t>::max())
2712 : {
2713 268667 : m_nTotalFeatureCount++;
2714 : }
2715 : else
2716 : {
2717 1 : if (m_poDS->m_bHasGPKGOGRContents)
2718 : {
2719 1 : char *pszSQL = sqlite3_mprintf(
2720 : "UPDATE gpkg_ogr_contents SET feature_count = null "
2721 : "WHERE lower(table_name) = lower('%q')",
2722 : m_pszTableName);
2723 1 : CPL_IGNORE_RET_VAL(sqlite3_exec(m_poDS->hDB, pszSQL, nullptr,
2724 : nullptr, nullptr));
2725 1 : sqlite3_free(pszSQL);
2726 : }
2727 1 : m_nTotalFeatureCount = -1;
2728 : }
2729 : }
2730 : #endif
2731 :
2732 268765 : m_bContentChanged = true;
2733 :
2734 : /* All done! */
2735 268765 : return OGRERR_NONE;
2736 : }
2737 :
2738 100 : void OGRGeoPackageTableLayer::FinishRollbackTransaction(
2739 : const std::string &osSavepointName)
2740 : {
2741 100 : OGRGeoPackageLayer::FinishRollbackTransaction(osSavepointName);
2742 : // Invalidate feature count
2743 100 : m_nTotalFeatureCount = -1;
2744 100 : }
2745 :
2746 268751 : OGRErr OGRGeoPackageTableLayer::ICreateFeature(OGRFeature *poFeature)
2747 : {
2748 268751 : return CreateOrUpsertFeature(poFeature, /* bUpsert=*/false);
2749 : }
2750 :
2751 : /************************************************************************/
2752 : /* SetDeferredSpatialIndexCreation() */
2753 : /************************************************************************/
2754 :
2755 926 : void OGRGeoPackageTableLayer::SetDeferredSpatialIndexCreation(bool bFlag)
2756 : {
2757 926 : m_bDeferredSpatialIndexCreation = bFlag;
2758 926 : if (bFlag)
2759 : {
2760 : // This method is invoked before the layer is added to the dataset,
2761 : // so GetLayerCount() will return 0 for the first layer added.
2762 926 : m_bAllowedRTreeThread =
2763 1749 : m_poDS->GetLayerCount() == 0 && sqlite3_threadsafe() != 0 &&
2764 2572 : CPLGetNumCPUs() >= 2 &&
2765 823 : CPLTestBool(
2766 : CPLGetConfigOption("OGR_GPKG_ALLOW_THREADED_RTREE", "YES"));
2767 :
2768 : // For unit tests
2769 926 : if (CPLTestBool(CPLGetConfigOption(
2770 : "OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "NO")))
2771 : {
2772 60 : m_nRTreeBatchSize = 10;
2773 60 : m_nRTreeBatchesBeforeStart = 1;
2774 : }
2775 : }
2776 926 : }
2777 :
2778 : /************************************************************************/
2779 : /* StartAsyncRTree() */
2780 : /************************************************************************/
2781 :
2782 : // We create a temporary database with only the RTree, and we insert
2783 : // records into it in a dedicated thread, in parallel of the main thread
2784 : // that inserts rows in the user table. When the layer is finalized, we
2785 : // just use bulk copy statements of the form
2786 : // INSERT INTO rtree_xxxx_rowid/node/parent SELECT * FROM
2787 : // temp_rtree.my_rtree_rowid/node/parent to copy the RTree auxiliary tables into
2788 : // the main database, which is a very fast operation.
2789 :
2790 48 : void OGRGeoPackageTableLayer::StartAsyncRTree()
2791 : {
2792 48 : m_osAsyncDBName = m_poDS->GetDescription();
2793 48 : m_osAsyncDBName += ".tmp_rtree_";
2794 48 : bool bCanUseTableName = false;
2795 48 : if (strlen(m_pszTableName) <= 32)
2796 : {
2797 36 : bCanUseTableName = true;
2798 36 : constexpr char DIGIT_ZERO = '0';
2799 144 : for (int i = 0; m_pszTableName[i] != '\0'; ++i)
2800 : {
2801 120 : const char ch = m_pszTableName[i];
2802 132 : if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
2803 12 : (ch >= DIGIT_ZERO && ch <= '9') || ch == '.' || ch == '_'))
2804 : {
2805 12 : bCanUseTableName = false;
2806 12 : break;
2807 : }
2808 : }
2809 : }
2810 48 : if (bCanUseTableName)
2811 24 : m_osAsyncDBName += m_pszTableName;
2812 : else
2813 : {
2814 24 : m_osAsyncDBName += CPLMD5String(m_pszTableName);
2815 : }
2816 48 : m_osAsyncDBName += ".db";
2817 :
2818 48 : m_osAsyncDBAttachName = "temp_rtree_";
2819 48 : m_osAsyncDBAttachName += CPLMD5String(m_pszTableName);
2820 :
2821 48 : VSIUnlink(m_osAsyncDBName.c_str());
2822 48 : CPLDebug("GPKG", "Creating background RTree DB %s",
2823 : m_osAsyncDBName.c_str());
2824 72 : if (sqlite3_open_v2(m_osAsyncDBName.c_str(), &m_hAsyncDBHandle,
2825 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
2826 120 : m_poDS->GetVFS() ? m_poDS->GetVFS()->zName : nullptr) !=
2827 : SQLITE_OK)
2828 : {
2829 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_open_v2() of %s failed",
2830 : m_osAsyncDBName.c_str());
2831 0 : sqlite3_close(m_hAsyncDBHandle);
2832 0 : m_hAsyncDBHandle = nullptr;
2833 : }
2834 48 : if (m_hAsyncDBHandle != nullptr)
2835 : {
2836 : /* Make sure our auxiliary DB has the same page size as the main one.
2837 : * Because the number of RTree cells depends on the SQLite page size.
2838 : * However the sqlite implementation limits to 51 cells maximum per page,
2839 : * which is reached starting with a page size of 2048 bytes.
2840 : * As the default SQLite page size is 4096 currently, having potentially
2841 : * different page sizes >= 4096 between the main and auxiliary DBs would
2842 : * not be a practical issue, but better be consistent.
2843 : */
2844 : const int nPageSize =
2845 48 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA page_size", nullptr);
2846 :
2847 48 : if (SQLCommand(m_hAsyncDBHandle,
2848 : CPLSPrintf("PRAGMA page_size = %d;\n"
2849 : "PRAGMA journal_mode = OFF;\n"
2850 : "PRAGMA synchronous = OFF;",
2851 48 : nPageSize)) == OGRERR_NONE)
2852 : {
2853 48 : char *pszSQL = sqlite3_mprintf("ATTACH DATABASE '%q' AS '%q'",
2854 : m_osAsyncDBName.c_str(),
2855 : m_osAsyncDBAttachName.c_str());
2856 48 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
2857 48 : sqlite3_free(pszSQL);
2858 :
2859 48 : if (eErr == OGRERR_NONE)
2860 : {
2861 48 : m_hRTree = gdal_sqlite_rtree_bl_new(nPageSize);
2862 : try
2863 : {
2864 : m_oThreadRTree =
2865 96 : std::thread([this]() { AsyncRTreeThreadFunction(); });
2866 48 : m_bThreadRTreeStarted = true;
2867 : }
2868 0 : catch (const std::exception &e)
2869 : {
2870 0 : CPLError(CE_Failure, CPLE_AppDefined,
2871 0 : "RTree thread cannot be created: %s", e.what());
2872 : }
2873 : }
2874 : }
2875 :
2876 48 : if (!m_bThreadRTreeStarted)
2877 : {
2878 0 : if (m_hRTree)
2879 : {
2880 0 : gdal_sqlite_rtree_bl_free(m_hRTree);
2881 0 : m_hRTree = nullptr;
2882 : }
2883 0 : m_oQueueRTreeEntries.clear();
2884 0 : m_bErrorDuringRTreeThread = true;
2885 0 : sqlite3_close(m_hAsyncDBHandle);
2886 0 : m_hAsyncDBHandle = nullptr;
2887 0 : VSIUnlink(m_osAsyncDBName.c_str());
2888 : }
2889 : }
2890 : else
2891 : {
2892 0 : m_oQueueRTreeEntries.clear();
2893 0 : m_bErrorDuringRTreeThread = true;
2894 : }
2895 48 : }
2896 :
2897 : /************************************************************************/
2898 : /* RemoveAsyncRTreeTempDB() */
2899 : /************************************************************************/
2900 :
2901 48 : void OGRGeoPackageTableLayer::RemoveAsyncRTreeTempDB()
2902 : {
2903 48 : if (!m_osAsyncDBAttachName.empty())
2904 : {
2905 96 : SQLCommand(
2906 48 : m_poDS->GetDB(),
2907 : CPLSPrintf("DETACH DATABASE \"%s\"",
2908 96 : SQLEscapeName(m_osAsyncDBAttachName.c_str()).c_str()));
2909 48 : m_osAsyncDBAttachName.clear();
2910 48 : VSIUnlink(m_osAsyncDBName.c_str());
2911 48 : m_osAsyncDBName.clear();
2912 : }
2913 48 : }
2914 :
2915 : /************************************************************************/
2916 : /* CancelAsyncRTree() */
2917 : /************************************************************************/
2918 :
2919 36 : void OGRGeoPackageTableLayer::CancelAsyncRTree()
2920 : {
2921 36 : CPLDebug("GPKG", "Cancel background RTree creation");
2922 36 : m_oQueueRTreeEntries.push({});
2923 36 : m_oThreadRTree.join();
2924 36 : m_bThreadRTreeStarted = false;
2925 36 : if (m_hAsyncDBHandle)
2926 : {
2927 36 : sqlite3_close(m_hAsyncDBHandle);
2928 36 : m_hAsyncDBHandle = nullptr;
2929 : }
2930 36 : gdal_sqlite_rtree_bl_free(m_hRTree);
2931 36 : m_hRTree = nullptr;
2932 36 : m_bErrorDuringRTreeThread = true;
2933 36 : RemoveAsyncRTreeTempDB();
2934 36 : }
2935 :
2936 : /************************************************************************/
2937 : /* FinishOrDisableThreadedRTree() */
2938 : /************************************************************************/
2939 :
2940 95 : void OGRGeoPackageTableLayer::FinishOrDisableThreadedRTree()
2941 : {
2942 95 : if (m_bThreadRTreeStarted)
2943 : {
2944 12 : CreateSpatialIndexIfNecessary();
2945 : }
2946 95 : m_bAllowedRTreeThread = false;
2947 95 : }
2948 :
2949 : /************************************************************************/
2950 : /* FlushInMemoryRTree() */
2951 : /************************************************************************/
2952 :
2953 12 : bool OGRGeoPackageTableLayer::FlushInMemoryRTree(sqlite3 *hRTreeDB,
2954 : const char *pszRTreeName)
2955 : {
2956 12 : if (hRTreeDB == m_hAsyncDBHandle)
2957 8 : SQLCommand(hRTreeDB, "BEGIN");
2958 :
2959 12 : char *pszErrMsg = nullptr;
2960 12 : bool bRet = gdal_sqlite_rtree_bl_serialize(m_hRTree, hRTreeDB, pszRTreeName,
2961 : "id", "minx", "miny", "maxx",
2962 : "maxy", &pszErrMsg);
2963 12 : if (hRTreeDB == m_hAsyncDBHandle)
2964 : {
2965 8 : if (bRet)
2966 8 : bRet = SQLCommand(hRTreeDB, "COMMIT") == OGRERR_NONE;
2967 : else
2968 0 : SQLCommand(hRTreeDB, "ROLLBACK");
2969 : }
2970 :
2971 12 : gdal_sqlite_rtree_bl_free(m_hRTree);
2972 12 : m_hRTree = nullptr;
2973 :
2974 12 : if (!bRet)
2975 : {
2976 0 : CPLError(CE_Failure, CPLE_AppDefined,
2977 : "sqlite_rtree_bl_serialize() failed with %s",
2978 0 : pszErrMsg ? pszErrMsg : "(null)");
2979 :
2980 0 : m_bErrorDuringRTreeThread = true;
2981 :
2982 0 : if (m_hAsyncDBHandle)
2983 : {
2984 0 : sqlite3_close(m_hAsyncDBHandle);
2985 0 : m_hAsyncDBHandle = nullptr;
2986 : }
2987 :
2988 0 : m_oQueueRTreeEntries.clear();
2989 : }
2990 12 : sqlite3_free(pszErrMsg);
2991 :
2992 12 : return bRet;
2993 : }
2994 :
2995 : /************************************************************************/
2996 : /* GetMaxRAMUsageAllowedForRTree() */
2997 : /************************************************************************/
2998 :
2999 973 : static size_t GetMaxRAMUsageAllowedForRTree()
3000 : {
3001 973 : const uint64_t nUsableRAM = CPLGetUsablePhysicalRAM();
3002 973 : uint64_t nMaxRAMUsageAllowed =
3003 973 : (nUsableRAM ? nUsableRAM / 10 : 100 * 1024 * 1024);
3004 : const char *pszMaxRAMUsageAllowed =
3005 973 : CPLGetConfigOption("OGR_GPKG_MAX_RAM_USAGE_RTREE", nullptr);
3006 973 : if (pszMaxRAMUsageAllowed)
3007 : {
3008 : nMaxRAMUsageAllowed = static_cast<uint64_t>(
3009 20 : std::strtoull(pszMaxRAMUsageAllowed, nullptr, 10));
3010 : }
3011 973 : if (nMaxRAMUsageAllowed > std::numeric_limits<size_t>::max() - 1U)
3012 : {
3013 0 : nMaxRAMUsageAllowed = std::numeric_limits<size_t>::max() - 1U;
3014 : }
3015 973 : return static_cast<size_t>(nMaxRAMUsageAllowed);
3016 : }
3017 :
3018 : /************************************************************************/
3019 : /* AsyncRTreeThreadFunction() */
3020 : /************************************************************************/
3021 :
3022 48 : void OGRGeoPackageTableLayer::AsyncRTreeThreadFunction()
3023 : {
3024 48 : CPLAssert(m_hRTree);
3025 :
3026 48 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
3027 48 : sqlite3_stmt *hStmt = nullptr;
3028 48 : GIntBig nCount = 0;
3029 : while (true)
3030 : {
3031 884 : const auto aoEntries = m_oQueueRTreeEntries.get_and_pop_front();
3032 884 : if (aoEntries.empty())
3033 44 : break;
3034 :
3035 840 : constexpr int NOTIFICATION_INTERVAL = 500 * 1000;
3036 :
3037 840 : auto oIter = aoEntries.begin();
3038 840 : if (m_hRTree)
3039 : {
3040 4808 : for (; oIter != aoEntries.end(); ++oIter)
3041 : {
3042 4372 : const auto &entry = *oIter;
3043 4372 : if (gdal_sqlite_rtree_bl_ram_usage(m_hRTree) >
3044 8736 : nMaxRAMUsageAllowed ||
3045 4364 : !gdal_sqlite_rtree_bl_insert(m_hRTree, entry.nId,
3046 4364 : entry.fMinX, entry.fMinY,
3047 4364 : entry.fMaxX, entry.fMaxY))
3048 : {
3049 8 : CPLDebug("GPKG", "Too large in-memory RTree. "
3050 : "Flushing it and using memory friendly "
3051 : "algorithm for the rest");
3052 8 : if (!FlushInMemoryRTree(m_hAsyncDBHandle, "my_rtree"))
3053 0 : return;
3054 8 : break;
3055 : }
3056 4364 : ++nCount;
3057 4364 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
3058 : {
3059 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree",
3060 : nCount);
3061 : }
3062 : }
3063 444 : if (oIter == aoEntries.end())
3064 436 : continue;
3065 : }
3066 :
3067 404 : if (hStmt == nullptr)
3068 : {
3069 : const char *pszInsertSQL =
3070 8 : CPLGetConfigOption(
3071 : "OGR_GPKG_SIMULATE_INSERT_INTO_MY_RTREE_PREPARATION_ERROR",
3072 : nullptr)
3073 8 : ? "INSERT INTO my_rtree_SIMULATE_ERROR VALUES (?,?,?,?,?)"
3074 8 : : "INSERT INTO my_rtree VALUES (?,?,?,?,?)";
3075 8 : if (SQLPrepareWithError(m_hAsyncDBHandle, pszInsertSQL, -1, &hStmt,
3076 8 : nullptr) != SQLITE_OK)
3077 : {
3078 4 : m_bErrorDuringRTreeThread = true;
3079 :
3080 4 : sqlite3_close(m_hAsyncDBHandle);
3081 4 : m_hAsyncDBHandle = nullptr;
3082 :
3083 4 : m_oQueueRTreeEntries.clear();
3084 4 : return;
3085 : }
3086 :
3087 4 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3088 : }
3089 :
3090 : #ifdef DEBUG_VERBOSE
3091 : CPLDebug("GPKG",
3092 : "AsyncRTreeThreadFunction(): "
3093 : "Processing batch of %d features, "
3094 : "starting at FID " CPL_FRMT_GIB " and ending "
3095 : "at FID " CPL_FRMT_GIB,
3096 : static_cast<int>(aoEntries.size()), aoEntries.front().nId,
3097 : aoEntries.back().nId);
3098 : #endif
3099 4398 : for (; oIter != aoEntries.end(); ++oIter)
3100 : {
3101 3998 : const auto &entry = *oIter;
3102 3998 : sqlite3_reset(hStmt);
3103 :
3104 3998 : sqlite3_bind_int64(hStmt, 1, entry.nId);
3105 3998 : sqlite3_bind_double(hStmt, 2, entry.fMinX);
3106 3998 : sqlite3_bind_double(hStmt, 3, entry.fMaxX);
3107 3998 : sqlite3_bind_double(hStmt, 4, entry.fMinY);
3108 3998 : sqlite3_bind_double(hStmt, 5, entry.fMaxY);
3109 3998 : int sqlite_err = sqlite3_step(hStmt);
3110 3998 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
3111 : {
3112 0 : CPLError(CE_Failure, CPLE_AppDefined,
3113 : "failed to execute insertion in RTree : %s",
3114 : sqlite3_errmsg(m_hAsyncDBHandle));
3115 0 : m_bErrorDuringRTreeThread = true;
3116 0 : break;
3117 : }
3118 3998 : ++nCount;
3119 3998 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
3120 : {
3121 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree", nCount);
3122 0 : if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3123 : {
3124 0 : m_bErrorDuringRTreeThread = true;
3125 0 : break;
3126 : }
3127 0 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3128 : }
3129 : }
3130 836 : }
3131 44 : if (!m_hRTree)
3132 : {
3133 4 : if (m_bErrorDuringRTreeThread)
3134 : {
3135 0 : SQLCommand(m_hAsyncDBHandle, "ROLLBACK");
3136 : }
3137 4 : else if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3138 : {
3139 0 : m_bErrorDuringRTreeThread = true;
3140 : }
3141 :
3142 4 : sqlite3_finalize(hStmt);
3143 :
3144 4 : if (m_bErrorDuringRTreeThread)
3145 : {
3146 0 : sqlite3_close(m_hAsyncDBHandle);
3147 0 : m_hAsyncDBHandle = nullptr;
3148 :
3149 0 : VSIUnlink(m_osAsyncDBName.c_str());
3150 :
3151 0 : m_oQueueRTreeEntries.clear();
3152 : }
3153 : }
3154 44 : CPLDebug("GPKG",
3155 : "AsyncRTreeThreadFunction(): " CPL_FRMT_GIB
3156 : " rows inserted into RTree",
3157 : nCount);
3158 : }
3159 :
3160 : /************************************************************************/
3161 : /* ISetFeature() */
3162 : /************************************************************************/
3163 :
3164 59 : OGRErr OGRGeoPackageTableLayer::ISetFeature(OGRFeature *poFeature)
3165 : {
3166 59 : if (!m_bFeatureDefnCompleted)
3167 0 : GetLayerDefn();
3168 59 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3169 : {
3170 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3171 : "SetFeature");
3172 0 : return OGRERR_FAILURE;
3173 : }
3174 :
3175 : /* No FID? */
3176 59 : if (poFeature->GetFID() == OGRNullFID)
3177 : {
3178 0 : CPLError(CE_Failure, CPLE_AppDefined,
3179 : "FID required on features given to SetFeature().");
3180 0 : return OGRERR_FAILURE;
3181 : }
3182 :
3183 : /* In case the FID column has also been created as a regular field */
3184 68 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3185 9 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3186 : {
3187 1 : return OGRERR_FAILURE;
3188 : }
3189 :
3190 58 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3191 0 : return OGRERR_FAILURE;
3192 :
3193 58 : CancelAsyncNextArrowArray();
3194 :
3195 58 : if (m_bThreadRTreeStarted)
3196 12 : CancelAsyncRTree();
3197 58 : if (!RunDeferredSpatialIndexUpdate())
3198 0 : return OGRERR_FAILURE;
3199 :
3200 : const sqlite3_int64 nTotalChangesBefore =
3201 : #if SQLITE_VERSION_NUMBER >= 3037000L
3202 58 : sqlite3_total_changes64(m_poDS->GetDB());
3203 : #else
3204 : sqlite3_total_changes(m_poDS->GetDB());
3205 : #endif
3206 :
3207 58 : CheckGeometryType(poFeature);
3208 :
3209 58 : if (!m_osUpdateStatementSQL.empty())
3210 : {
3211 1 : m_osUpdateStatementSQL.clear();
3212 1 : if (m_poUpdateStatement)
3213 1 : sqlite3_finalize(m_poUpdateStatement);
3214 1 : m_poUpdateStatement = nullptr;
3215 : }
3216 58 : if (!m_poUpdateStatement)
3217 : {
3218 : /* Construct a SQL UPDATE statement from the OGRFeature */
3219 : /* Only work with fields that are set */
3220 : /* Do not stick values into SQL, use placeholder and bind values later
3221 : */
3222 46 : const std::string osCommand = FeatureGenerateUpdateSQL(poFeature);
3223 46 : if (osCommand.empty())
3224 1 : return OGRERR_NONE;
3225 :
3226 : /* Prepare the SQL into a statement */
3227 45 : int err = SQLPrepareWithError(m_poDS->GetDB(), osCommand.c_str(),
3228 45 : static_cast<int>(osCommand.size()),
3229 : &m_poUpdateStatement, nullptr);
3230 45 : if (err != SQLITE_OK)
3231 : {
3232 1 : return OGRERR_FAILURE;
3233 : }
3234 : }
3235 :
3236 : /* Bind values onto the statement now */
3237 56 : OGRErr errOgr = FeatureBindUpdateParameters(poFeature, m_poUpdateStatement);
3238 56 : if (errOgr != OGRERR_NONE)
3239 : {
3240 0 : sqlite3_reset(m_poUpdateStatement);
3241 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3242 0 : return errOgr;
3243 : }
3244 :
3245 : /* From here execute the statement and check errors */
3246 56 : int err = sqlite3_step(m_poUpdateStatement);
3247 56 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3248 : {
3249 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3250 0 : sqlite3_errmsg(m_poDS->GetDB()));
3251 0 : sqlite3_reset(m_poUpdateStatement);
3252 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3253 0 : return OGRERR_FAILURE;
3254 : }
3255 :
3256 56 : sqlite3_reset(m_poUpdateStatement);
3257 56 : sqlite3_clear_bindings(m_poUpdateStatement);
3258 :
3259 : const sqlite3_int64 nTotalChangesAfter =
3260 : #if SQLITE_VERSION_NUMBER >= 3037000L
3261 56 : sqlite3_total_changes64(m_poDS->GetDB());
3262 : #else
3263 : sqlite3_total_changes(m_poDS->GetDB());
3264 : #endif
3265 :
3266 : /* Only update the envelope if we changed something */
3267 56 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3268 56 : ? OGRERR_NONE
3269 : : OGRERR_NON_EXISTING_FEATURE;
3270 56 : if (eErr == OGRERR_NONE)
3271 : {
3272 : /* Update the layer extents with this new object */
3273 50 : if (IsGeomFieldSet(poFeature))
3274 : {
3275 36 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3276 36 : if (!poGeom->IsEmpty())
3277 : {
3278 36 : OGREnvelope oEnv;
3279 36 : poGeom->getEnvelope(&oEnv);
3280 36 : UpdateExtent(&oEnv);
3281 : }
3282 : }
3283 :
3284 50 : m_bContentChanged = true;
3285 : }
3286 :
3287 : /* All done! */
3288 56 : return eErr;
3289 : }
3290 :
3291 : /************************************************************************/
3292 : /* IUpsertFeature() */
3293 : /************************************************************************/
3294 :
3295 27 : OGRErr OGRGeoPackageTableLayer::IUpsertFeature(OGRFeature *poFeature)
3296 :
3297 : {
3298 27 : return CreateOrUpsertFeature(poFeature, /* bUpsert = */ true);
3299 : }
3300 :
3301 : //----------------------------------------------------------------------
3302 : // FeatureGenerateUpdateSQL()
3303 : //
3304 : // Build a SQL UPDATE statement that references all the columns in
3305 : // the OGRFeatureDefn that the user asked to be updated, then prepare it for
3306 : // repeated use in a prepared statement. All statements start off with geometry
3307 : // (if it exists, and if it is asked to be updated), then reference each column
3308 : // in the order it appears in the OGRFeatureDefn.
3309 : // FeatureBindParameters operates on the expectation of this
3310 : // column ordering.
3311 :
3312 : //
3313 11 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
3314 : const OGRFeature *poFeature, int nUpdatedFieldsCount,
3315 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3316 : const int * /*panUpdatedGeomFieldsIdx*/) const
3317 : {
3318 11 : bool bNeedComma = false;
3319 11 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
3320 :
3321 : /* Set up our SQL string basics */
3322 22 : std::string osUpdate("UPDATE \"");
3323 11 : osUpdate += SQLEscapeName(m_pszTableName);
3324 11 : osUpdate += "\" SET ";
3325 :
3326 11 : if (nUpdatedGeomFieldsCount == 1 && poFeatureDefn->GetGeomFieldCount() > 0)
3327 : {
3328 2 : osUpdate += '"';
3329 : osUpdate +=
3330 2 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
3331 2 : osUpdate += "\"=?";
3332 2 : bNeedComma = true;
3333 : }
3334 :
3335 : /* Add attribute column names (except FID) to the SQL */
3336 20 : for (int i = 0; i < nUpdatedFieldsCount; i++)
3337 : {
3338 9 : const int iField = panUpdatedFieldsIdx[i];
3339 18 : if (iField == m_iFIDAsRegularColumnIndex ||
3340 9 : poFeatureDefn->GetFieldDefn(iField)->IsGenerated())
3341 0 : continue;
3342 9 : if (!poFeature->IsFieldSet(iField))
3343 1 : continue;
3344 8 : if (!bNeedComma)
3345 8 : bNeedComma = true;
3346 : else
3347 0 : osUpdate += ", ";
3348 :
3349 8 : osUpdate += '"';
3350 : osUpdate +=
3351 8 : SQLEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
3352 8 : osUpdate += "\"=?";
3353 : }
3354 11 : if (!bNeedComma)
3355 1 : return CPLString();
3356 :
3357 10 : osUpdate += " WHERE \"";
3358 10 : osUpdate += SQLEscapeName(m_pszFidColumn);
3359 10 : osUpdate += "\" = ?";
3360 :
3361 10 : return osUpdate;
3362 : }
3363 :
3364 : /************************************************************************/
3365 : /* UpdateFeature() */
3366 : /************************************************************************/
3367 :
3368 11 : OGRErr OGRGeoPackageTableLayer::IUpdateFeature(
3369 : OGRFeature *poFeature, int nUpdatedFieldsCount,
3370 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3371 : const int *panUpdatedGeomFieldsIdx, bool /* bUpdateStyleString*/)
3372 :
3373 : {
3374 11 : if (!m_bFeatureDefnCompleted)
3375 0 : GetLayerDefn();
3376 11 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3377 : {
3378 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3379 : "UpdateFeature");
3380 0 : return OGRERR_FAILURE;
3381 : }
3382 :
3383 : /* No FID? */
3384 11 : if (poFeature->GetFID() == OGRNullFID)
3385 : {
3386 0 : CPLError(CE_Failure, CPLE_AppDefined,
3387 : "FID required on features given to SetFeature().");
3388 0 : return OGRERR_FAILURE;
3389 : }
3390 :
3391 : /* In case the FID column has also been created as a regular field */
3392 11 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3393 0 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3394 : {
3395 0 : return OGRERR_FAILURE;
3396 : }
3397 :
3398 11 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3399 0 : return OGRERR_FAILURE;
3400 :
3401 11 : CancelAsyncNextArrowArray();
3402 :
3403 11 : if (m_bThreadRTreeStarted)
3404 0 : CancelAsyncRTree();
3405 11 : if (!RunDeferredSpatialIndexUpdate())
3406 0 : return OGRERR_FAILURE;
3407 :
3408 11 : CheckGeometryType(poFeature);
3409 :
3410 : /* Construct a SQL UPDATE statement from the OGRFeature */
3411 : /* Only work with fields that are set */
3412 : /* Do not stick values into SQL, use placeholder and bind values later
3413 : */
3414 : std::string osUpdateStatementSQL = FeatureGenerateUpdateSQL(
3415 : poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3416 22 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3417 11 : if (osUpdateStatementSQL.empty())
3418 1 : return OGRERR_NONE;
3419 :
3420 10 : if (m_osUpdateStatementSQL != osUpdateStatementSQL)
3421 : {
3422 8 : if (m_poUpdateStatement)
3423 5 : sqlite3_finalize(m_poUpdateStatement);
3424 8 : m_poUpdateStatement = nullptr;
3425 : /* Prepare the SQL into a statement */
3426 : int err =
3427 8 : SQLPrepareWithError(m_poDS->GetDB(), osUpdateStatementSQL.c_str(),
3428 8 : static_cast<int>(osUpdateStatementSQL.size()),
3429 : &m_poUpdateStatement, nullptr);
3430 8 : if (err != SQLITE_OK)
3431 : {
3432 0 : return OGRERR_FAILURE;
3433 : }
3434 8 : m_osUpdateStatementSQL = std::move(osUpdateStatementSQL);
3435 : }
3436 :
3437 : /* Bind values onto the statement now */
3438 10 : int nColCount = 0;
3439 : const OGRErr errOgr =
3440 10 : FeatureBindParameters(poFeature, m_poUpdateStatement, &nColCount, false,
3441 : false, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3442 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3443 10 : if (errOgr != OGRERR_NONE)
3444 : {
3445 0 : sqlite3_reset(m_poUpdateStatement);
3446 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3447 0 : return errOgr;
3448 : }
3449 :
3450 : // Bind the FID to the "WHERE" clause.
3451 : const int sqlite_err =
3452 10 : sqlite3_bind_int64(m_poUpdateStatement, nColCount, poFeature->GetFID());
3453 10 : if (sqlite_err != SQLITE_OK)
3454 : {
3455 0 : CPLError(CE_Failure, CPLE_AppDefined,
3456 : "failed to bind FID '" CPL_FRMT_GIB "' to statement",
3457 : poFeature->GetFID());
3458 0 : sqlite3_reset(m_poUpdateStatement);
3459 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3460 0 : return OGRERR_FAILURE;
3461 : }
3462 :
3463 : const sqlite3_int64 nTotalChangesBefore =
3464 : #if SQLITE_VERSION_NUMBER >= 3037000L
3465 10 : sqlite3_total_changes64(m_poDS->GetDB());
3466 : #else
3467 : sqlite3_total_changes(m_poDS->GetDB());
3468 : #endif
3469 :
3470 : /* From here execute the statement and check errors */
3471 10 : int err = sqlite3_step(m_poUpdateStatement);
3472 10 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3473 : {
3474 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3475 0 : sqlite3_errmsg(m_poDS->GetDB()));
3476 0 : sqlite3_reset(m_poUpdateStatement);
3477 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3478 0 : return OGRERR_FAILURE;
3479 : }
3480 :
3481 10 : sqlite3_reset(m_poUpdateStatement);
3482 10 : sqlite3_clear_bindings(m_poUpdateStatement);
3483 :
3484 : const sqlite3_int64 nTotalChangesAfter =
3485 : #if SQLITE_VERSION_NUMBER >= 3037000L
3486 10 : sqlite3_total_changes64(m_poDS->GetDB());
3487 : #else
3488 : sqlite3_total_changes(m_poDS->GetDB());
3489 : #endif
3490 :
3491 : /* Only update the envelope if we changed something */
3492 10 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3493 10 : ? OGRERR_NONE
3494 : : OGRERR_NON_EXISTING_FEATURE;
3495 10 : if (eErr == OGRERR_NONE)
3496 : {
3497 : /* Update the layer extents with this new object */
3498 9 : if (nUpdatedGeomFieldsCount == 1 && IsGeomFieldSet(poFeature))
3499 : {
3500 1 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3501 1 : if (!poGeom->IsEmpty())
3502 : {
3503 1 : OGREnvelope oEnv;
3504 1 : poGeom->getEnvelope(&oEnv);
3505 1 : UpdateExtent(&oEnv);
3506 : }
3507 : }
3508 :
3509 9 : m_bContentChanged = true;
3510 : }
3511 :
3512 : /* All done! */
3513 10 : return eErr;
3514 : }
3515 :
3516 : /************************************************************************/
3517 : /* SetAttributeFilter() */
3518 : /************************************************************************/
3519 :
3520 89 : OGRErr OGRGeoPackageTableLayer::SetAttributeFilter(const char *pszQuery)
3521 :
3522 : {
3523 89 : if (!m_bFeatureDefnCompleted)
3524 9 : GetLayerDefn();
3525 89 : CPLFree(m_pszAttrQueryString);
3526 89 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
3527 :
3528 89 : if (pszQuery == nullptr)
3529 40 : osQuery = "";
3530 : else
3531 49 : osQuery = pszQuery;
3532 :
3533 89 : BuildWhere();
3534 :
3535 89 : ResetReading();
3536 :
3537 89 : return OGRERR_NONE;
3538 : }
3539 :
3540 : /************************************************************************/
3541 : /* ResetReading() */
3542 : /************************************************************************/
3543 :
3544 25803 : void OGRGeoPackageTableLayer::ResetReading()
3545 : {
3546 25803 : m_bEOF = false;
3547 25803 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3548 0 : return;
3549 :
3550 25803 : OGRGeoPackageLayer::ResetReading();
3551 :
3552 25803 : if (m_poInsertStatement)
3553 : {
3554 158 : sqlite3_finalize(m_poInsertStatement);
3555 158 : m_poInsertStatement = nullptr;
3556 : }
3557 :
3558 25803 : if (m_poUpdateStatement)
3559 : {
3560 23 : sqlite3_finalize(m_poUpdateStatement);
3561 23 : m_poUpdateStatement = nullptr;
3562 : }
3563 25803 : m_osUpdateStatementSQL.clear();
3564 :
3565 25803 : if (m_poGetFeatureStatement)
3566 : {
3567 22 : sqlite3_finalize(m_poGetFeatureStatement);
3568 22 : m_poGetFeatureStatement = nullptr;
3569 : }
3570 :
3571 25803 : CancelAsyncNextArrowArray();
3572 :
3573 25803 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3574 :
3575 25803 : BuildColumns();
3576 : }
3577 :
3578 : /************************************************************************/
3579 : /* SetNextByIndex() */
3580 : /************************************************************************/
3581 :
3582 17 : OGRErr OGRGeoPackageTableLayer::SetNextByIndex(GIntBig nIndex)
3583 : {
3584 17 : if (nIndex < 0)
3585 : {
3586 3 : m_bEOF = true;
3587 3 : return OGRERR_NON_EXISTING_FEATURE;
3588 : }
3589 14 : if (m_soColumns.empty())
3590 2 : BuildColumns();
3591 14 : return ResetStatementInternal(nIndex);
3592 : }
3593 :
3594 : /************************************************************************/
3595 : /* ResetStatement() */
3596 : /************************************************************************/
3597 :
3598 895 : OGRErr OGRGeoPackageTableLayer::ResetStatement()
3599 :
3600 : {
3601 895 : return ResetStatementInternal(0);
3602 : }
3603 :
3604 : /************************************************************************/
3605 : /* ResetStatementInternal() */
3606 : /************************************************************************/
3607 :
3608 909 : OGRErr OGRGeoPackageTableLayer::ResetStatementInternal(GIntBig nStartIndex)
3609 :
3610 : {
3611 909 : ClearStatement();
3612 :
3613 : /* There is no active query statement set up, */
3614 : /* so job #1 is to prepare the statement. */
3615 : /* Append the attribute filter, if there is one */
3616 1818 : CPLString soSQL;
3617 909 : if (!m_soFilter.empty())
3618 : {
3619 : soSQL.Printf("SELECT %s FROM \"%s\" m WHERE %s", m_soColumns.c_str(),
3620 280 : SQLEscapeName(m_pszTableName).c_str(), m_soFilter.c_str());
3621 :
3622 545 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
3623 265 : HasSpatialIndex())
3624 : {
3625 264 : OGREnvelope sEnvelope;
3626 :
3627 264 : m_poFilterGeom->getEnvelope(&sEnvelope);
3628 :
3629 264 : bool bUseSpatialIndex = true;
3630 522 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
3631 169 : sEnvelope.MinY <= m_poExtent->MinY &&
3632 662 : sEnvelope.MaxX >= m_poExtent->MaxX &&
3633 140 : sEnvelope.MaxY >= m_poExtent->MaxY)
3634 : {
3635 : // Selecting from spatial filter on whole extent can be rather
3636 : // slow. So use function based filtering, just in case the
3637 : // advertized global extent might be wrong. Otherwise we might
3638 : // just discard completely the spatial filter.
3639 137 : bUseSpatialIndex = false;
3640 : }
3641 :
3642 127 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
3643 518 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
3644 127 : !std::isinf(sEnvelope.MaxY))
3645 : {
3646 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3647 : "JOIN \"%s\" r "
3648 : "ON m.\"%s\" = r.id WHERE "
3649 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
3650 : "r.maxy >= %.12f AND r.miny <= %.12f",
3651 : m_soColumns.c_str(),
3652 254 : SQLEscapeName(m_pszTableName).c_str(),
3653 254 : SQLEscapeName(m_osRTreeName).c_str(),
3654 254 : SQLEscapeName(m_osFIDForRTree).c_str(),
3655 127 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
3656 508 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
3657 : }
3658 : }
3659 : }
3660 : else
3661 : soSQL.Printf("SELECT %s FROM \"%s\" m", m_soColumns.c_str(),
3662 629 : SQLEscapeName(m_pszTableName).c_str());
3663 909 : if (nStartIndex > 0)
3664 : {
3665 11 : soSQL += CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB, nStartIndex);
3666 : }
3667 :
3668 909 : CPLDebug("GPKG", "ResetStatement(%s)", soSQL.c_str());
3669 :
3670 909 : int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
3671 : &m_poQueryStatement, nullptr);
3672 909 : if (err != SQLITE_OK)
3673 : {
3674 1 : return OGRERR_FAILURE;
3675 : }
3676 :
3677 908 : m_iNextShapeId = nStartIndex;
3678 908 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3679 :
3680 908 : return OGRERR_NONE;
3681 : }
3682 :
3683 : /************************************************************************/
3684 : /* GetNextFeature() */
3685 : /************************************************************************/
3686 :
3687 12198 : OGRFeature *OGRGeoPackageTableLayer::GetNextFeature()
3688 : {
3689 12198 : if (m_bEOF)
3690 40 : return nullptr;
3691 12158 : if (!m_bFeatureDefnCompleted)
3692 36 : GetLayerDefn();
3693 12158 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3694 0 : return nullptr;
3695 :
3696 12158 : CancelAsyncNextArrowArray();
3697 :
3698 12158 : if (m_poFilterGeom != nullptr)
3699 : {
3700 : // Both are exclusive
3701 10175 : CreateSpatialIndexIfNecessary();
3702 10175 : if (!RunDeferredSpatialIndexUpdate())
3703 0 : return nullptr;
3704 : }
3705 :
3706 12158 : OGRFeature *poFeature = OGRGeoPackageLayer::GetNextFeature();
3707 12158 : if (poFeature && m_iFIDAsRegularColumnIndex >= 0)
3708 : {
3709 1 : poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
3710 : }
3711 12158 : return poFeature;
3712 : }
3713 :
3714 : /************************************************************************/
3715 : /* GetFeature() */
3716 : /************************************************************************/
3717 :
3718 1302 : OGRFeature *OGRGeoPackageTableLayer::GetFeature(GIntBig nFID)
3719 : {
3720 1302 : if (!m_bFeatureDefnCompleted)
3721 11 : GetLayerDefn();
3722 1302 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3723 0 : return nullptr;
3724 1302 : CancelAsyncNextArrowArray();
3725 :
3726 1302 : if (m_pszFidColumn == nullptr)
3727 1 : return OGRLayer::GetFeature(nFID);
3728 :
3729 1301 : if (m_poGetFeatureStatement == nullptr)
3730 : {
3731 56 : CPLString soSQL;
3732 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3733 : "WHERE \"%s\" = ?",
3734 112 : m_soColumns.c_str(), SQLEscapeName(m_pszTableName).c_str(),
3735 168 : SQLEscapeName(m_pszFidColumn).c_str());
3736 :
3737 56 : const int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
3738 : &m_poGetFeatureStatement, nullptr);
3739 56 : if (err != SQLITE_OK)
3740 : {
3741 0 : return nullptr;
3742 : }
3743 : }
3744 :
3745 1301 : CPL_IGNORE_RET_VAL(sqlite3_bind_int64(m_poGetFeatureStatement, 1, nFID));
3746 :
3747 : /* Should be only one or zero results */
3748 1301 : const int err = sqlite3_step(m_poGetFeatureStatement);
3749 :
3750 : /* Aha, got one */
3751 1301 : if (err == SQLITE_ROW)
3752 : {
3753 1289 : OGRFeature *poFeature = TranslateFeature(m_poGetFeatureStatement);
3754 1289 : if (m_iFIDAsRegularColumnIndex >= 0)
3755 : {
3756 7 : poFeature->SetField(m_iFIDAsRegularColumnIndex,
3757 : poFeature->GetFID());
3758 : }
3759 :
3760 1289 : sqlite3_reset(m_poGetFeatureStatement);
3761 1289 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3762 :
3763 1289 : return poFeature;
3764 : }
3765 :
3766 12 : sqlite3_reset(m_poGetFeatureStatement);
3767 12 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3768 :
3769 : /* Error out on all other return codes */
3770 12 : return nullptr;
3771 : }
3772 :
3773 : /************************************************************************/
3774 : /* DeleteFeature() */
3775 : /************************************************************************/
3776 :
3777 60 : OGRErr OGRGeoPackageTableLayer::DeleteFeature(GIntBig nFID)
3778 : {
3779 60 : if (!m_bFeatureDefnCompleted)
3780 4 : GetLayerDefn();
3781 60 : if (!m_poDS->GetUpdate())
3782 : {
3783 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3784 : "DeleteFeature");
3785 0 : return OGRERR_FAILURE;
3786 : }
3787 60 : if (m_pszFidColumn == nullptr)
3788 : {
3789 0 : return OGRERR_FAILURE;
3790 : }
3791 :
3792 60 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3793 0 : return OGRERR_FAILURE;
3794 :
3795 60 : CancelAsyncNextArrowArray();
3796 :
3797 60 : if (m_bThreadRTreeStarted)
3798 12 : CancelAsyncRTree();
3799 :
3800 60 : if (!RunDeferredSpatialIndexUpdate())
3801 0 : return OGRERR_FAILURE;
3802 :
3803 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3804 60 : if (m_bOGRFeatureCountTriggersEnabled)
3805 : {
3806 17 : DisableFeatureCountTriggers();
3807 : }
3808 : #endif
3809 :
3810 : /* Clear out any existing query */
3811 60 : ResetReading();
3812 :
3813 : /* No filters apply, just use the FID */
3814 60 : CPLString soSQL;
3815 : soSQL.Printf("DELETE FROM \"%s\" WHERE \"%s\" = " CPL_FRMT_GIB,
3816 120 : SQLEscapeName(m_pszTableName).c_str(),
3817 180 : SQLEscapeName(m_pszFidColumn).c_str(), nFID);
3818 :
3819 : const sqlite3_int64 nTotalChangesBefore =
3820 : #if SQLITE_VERSION_NUMBER >= 3037000L
3821 60 : sqlite3_total_changes64(m_poDS->GetDB());
3822 : #else
3823 : sqlite3_total_changes(m_poDS->GetDB());
3824 : #endif
3825 :
3826 60 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), soSQL.c_str());
3827 60 : if (eErr == OGRERR_NONE)
3828 : {
3829 : const sqlite3_int64 nTotalChangesAfter =
3830 : #if SQLITE_VERSION_NUMBER >= 3037000L
3831 59 : sqlite3_total_changes64(m_poDS->GetDB());
3832 : #else
3833 : sqlite3_total_changes(m_poDS->GetDB());
3834 : #endif
3835 :
3836 59 : eErr = nTotalChangesAfter != nTotalChangesBefore
3837 59 : ? OGRERR_NONE
3838 : : OGRERR_NON_EXISTING_FEATURE;
3839 :
3840 59 : if (eErr == OGRERR_NONE)
3841 : {
3842 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3843 51 : if (m_nTotalFeatureCount >= 0)
3844 43 : m_nTotalFeatureCount--;
3845 : #endif
3846 :
3847 51 : m_bContentChanged = true;
3848 : }
3849 : }
3850 60 : return eErr;
3851 : }
3852 :
3853 : /************************************************************************/
3854 : /* DoJobAtTransactionCommit() */
3855 : /************************************************************************/
3856 :
3857 469 : bool OGRGeoPackageTableLayer::DoJobAtTransactionCommit()
3858 : {
3859 469 : if (m_bAllowedRTreeThread)
3860 315 : return true;
3861 :
3862 308 : bool ret = RunDeferredCreationIfNecessary() == OGRERR_NONE &&
3863 154 : RunDeferredSpatialIndexUpdate();
3864 154 : m_nCountInsertInTransaction = 0;
3865 154 : m_aoRTreeTriggersSQL.clear();
3866 154 : m_aoRTreeEntries.clear();
3867 154 : return ret;
3868 : }
3869 :
3870 : /************************************************************************/
3871 : /* DoJobAtTransactionRollback() */
3872 : /************************************************************************/
3873 :
3874 38 : bool OGRGeoPackageTableLayer::DoJobAtTransactionRollback()
3875 : {
3876 38 : if (m_bThreadRTreeStarted)
3877 12 : CancelAsyncRTree();
3878 38 : m_nCountInsertInTransaction = 0;
3879 38 : m_aoRTreeTriggersSQL.clear();
3880 38 : m_aoRTreeEntries.clear();
3881 38 : if (m_bTableCreatedInTransaction)
3882 : {
3883 2 : SyncToDisk();
3884 : }
3885 : else
3886 : {
3887 36 : bool bDeferredSpatialIndexCreationBackup =
3888 : m_bDeferredSpatialIndexCreation;
3889 36 : m_bDeferredSpatialIndexCreation = false;
3890 36 : SyncToDisk();
3891 36 : m_bDeferredSpatialIndexCreation = bDeferredSpatialIndexCreationBackup;
3892 : }
3893 :
3894 38 : ResetReading();
3895 38 : return true;
3896 : }
3897 :
3898 : /************************************************************************/
3899 : /* StartDeferredSpatialIndexUpdate() */
3900 : /************************************************************************/
3901 :
3902 10 : bool OGRGeoPackageTableLayer::StartDeferredSpatialIndexUpdate()
3903 : {
3904 10 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
3905 0 : return true;
3906 :
3907 10 : RevertWorkaroundUpdate1TriggerIssue();
3908 :
3909 10 : m_aoRTreeTriggersSQL.clear();
3910 10 : m_aoRTreeEntries.clear();
3911 :
3912 10 : const char *pszT = m_pszTableName;
3913 10 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3914 10 : m_osRTreeName = "rtree_";
3915 10 : m_osRTreeName += pszT;
3916 10 : m_osRTreeName += "_";
3917 10 : m_osRTreeName += pszC;
3918 :
3919 90 : char *pszSQL = sqlite3_mprintf(
3920 : "SELECT sql FROM sqlite_master WHERE type = 'trigger' "
3921 : "AND name IN ('%q', '%q', '%q', '%q', '%q', '%q', "
3922 : "'%q', '%q', '%q')",
3923 20 : (m_osRTreeName + "_insert").c_str(),
3924 20 : (m_osRTreeName + "_update1").c_str(),
3925 20 : (m_osRTreeName + "_update2").c_str(),
3926 20 : (m_osRTreeName + "_update3").c_str(),
3927 20 : (m_osRTreeName + "_update4").c_str(),
3928 : // update5 replaces update3 in GPKG 1.4
3929 : // cf https://github.com/opengeospatial/geopackage/pull/661
3930 20 : (m_osRTreeName + "_update5").c_str(),
3931 : // update6 and update7 replace update1 in GPKG 1.4
3932 : // cf https://github.com/opengeospatial/geopackage/pull/661
3933 20 : (m_osRTreeName + "_update6").c_str(),
3934 20 : (m_osRTreeName + "_update7").c_str(),
3935 20 : (m_osRTreeName + "_delete").c_str());
3936 20 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
3937 10 : sqlite3_free(pszSQL);
3938 10 : if (oResult)
3939 : {
3940 77 : for (int iRecord = 0; iRecord < oResult->RowCount(); iRecord++)
3941 : {
3942 67 : const char *pszTriggerSQL = oResult->GetValue(0, iRecord);
3943 67 : if (pszTriggerSQL)
3944 : {
3945 67 : m_aoRTreeTriggersSQL.push_back(pszTriggerSQL);
3946 : }
3947 : }
3948 : }
3949 10 : if (m_aoRTreeTriggersSQL.size() != 6 && m_aoRTreeTriggersSQL.size() != 7)
3950 : {
3951 0 : CPLDebug("GPKG", "Could not find expected RTree triggers");
3952 0 : m_aoRTreeTriggersSQL.clear();
3953 0 : return false;
3954 : }
3955 :
3956 10 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers());
3957 :
3958 10 : return true;
3959 : }
3960 :
3961 : /************************************************************************/
3962 : /* FlushPendingSpatialIndexUpdate() */
3963 : /************************************************************************/
3964 :
3965 8 : bool OGRGeoPackageTableLayer::FlushPendingSpatialIndexUpdate()
3966 : {
3967 8 : bool ret = true;
3968 :
3969 : // CPLDebug("GPKG", "Insert %d features in spatial index",
3970 : // static_cast<int>(m_aoRTreeEntries.size()));
3971 :
3972 8 : const char *pszT = m_pszTableName;
3973 8 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3974 :
3975 8 : m_osRTreeName = "rtree_";
3976 8 : m_osRTreeName += pszT;
3977 8 : m_osRTreeName += "_";
3978 8 : m_osRTreeName += pszC;
3979 :
3980 8 : char *pszSQL = sqlite3_mprintf("INSERT INTO \"%w\" VALUES (?,?,?,?,?)",
3981 : m_osRTreeName.c_str());
3982 8 : sqlite3_stmt *hInsertStmt = nullptr;
3983 8 : if (SQLPrepareWithError(m_poDS->GetDB(), pszSQL, -1, &hInsertStmt,
3984 8 : nullptr) != SQLITE_OK)
3985 : {
3986 0 : sqlite3_free(pszSQL);
3987 0 : m_aoRTreeEntries.clear();
3988 0 : return false;
3989 : }
3990 8 : sqlite3_free(pszSQL);
3991 :
3992 1196 : for (size_t i = 0; i < m_aoRTreeEntries.size(); ++i)
3993 : {
3994 1188 : sqlite3_reset(hInsertStmt);
3995 :
3996 1188 : sqlite3_bind_int64(hInsertStmt, 1, m_aoRTreeEntries[i].nId);
3997 1188 : sqlite3_bind_double(hInsertStmt, 2, m_aoRTreeEntries[i].fMinX);
3998 1188 : sqlite3_bind_double(hInsertStmt, 3, m_aoRTreeEntries[i].fMaxX);
3999 1188 : sqlite3_bind_double(hInsertStmt, 4, m_aoRTreeEntries[i].fMinY);
4000 1188 : sqlite3_bind_double(hInsertStmt, 5, m_aoRTreeEntries[i].fMaxY);
4001 1188 : int sqlite_err = sqlite3_step(hInsertStmt);
4002 1188 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
4003 : {
4004 0 : CPLError(CE_Failure, CPLE_AppDefined,
4005 : "failed to execute insertion in RTree : %s",
4006 0 : sqlite3_errmsg(m_poDS->GetDB()));
4007 0 : ret = false;
4008 0 : break;
4009 : }
4010 : }
4011 8 : sqlite3_finalize(hInsertStmt);
4012 8 : m_aoRTreeEntries.clear();
4013 8 : return ret;
4014 : }
4015 :
4016 : /************************************************************************/
4017 : /* RunDeferredSpatialIndexUpdate() */
4018 : /************************************************************************/
4019 :
4020 39562 : bool OGRGeoPackageTableLayer::RunDeferredSpatialIndexUpdate()
4021 : {
4022 39562 : m_nCountInsertInTransaction = 0;
4023 39562 : if (m_aoRTreeTriggersSQL.empty())
4024 39554 : return true;
4025 :
4026 8 : bool ret = FlushPendingSpatialIndexUpdate();
4027 :
4028 8 : RevertWorkaroundUpdate1TriggerIssue();
4029 :
4030 62 : for (const auto &osSQL : m_aoRTreeTriggersSQL)
4031 : {
4032 54 : ret &= SQLCommand(m_poDS->GetDB(), osSQL) == OGRERR_NONE;
4033 : }
4034 8 : m_aoRTreeTriggersSQL.clear();
4035 8 : return ret;
4036 : }
4037 :
4038 : /************************************************************************/
4039 : /* SyncToDisk() */
4040 : /************************************************************************/
4041 :
4042 11096 : OGRErr OGRGeoPackageTableLayer::SyncToDisk()
4043 : {
4044 11096 : if (!m_bFeatureDefnCompleted)
4045 5145 : return OGRERR_NONE;
4046 :
4047 5951 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4048 0 : return OGRERR_FAILURE;
4049 :
4050 : // Both are exclusive
4051 5951 : CreateSpatialIndexIfNecessary();
4052 5951 : if (!RunDeferredSpatialIndexUpdate())
4053 0 : return OGRERR_FAILURE;
4054 5951 : RevertWorkaroundUpdate1TriggerIssue();
4055 :
4056 : /* Save metadata back to the database */
4057 5951 : SaveExtent();
4058 5951 : SaveTimestamp();
4059 :
4060 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4061 5951 : CreateFeatureCountTriggers();
4062 : #endif
4063 :
4064 5951 : return OGRERR_NONE;
4065 : }
4066 :
4067 : /************************************************************************/
4068 : /* StartTransaction() */
4069 : /************************************************************************/
4070 :
4071 129 : OGRErr OGRGeoPackageTableLayer::StartTransaction()
4072 : {
4073 129 : CancelAsyncNextArrowArray();
4074 129 : return m_poDS->StartTransaction();
4075 : }
4076 :
4077 : /************************************************************************/
4078 : /* CommitTransaction() */
4079 : /************************************************************************/
4080 :
4081 92 : OGRErr OGRGeoPackageTableLayer::CommitTransaction()
4082 : {
4083 92 : return m_poDS->CommitTransaction();
4084 : }
4085 :
4086 : /************************************************************************/
4087 : /* RollbackTransaction() */
4088 : /************************************************************************/
4089 :
4090 18 : OGRErr OGRGeoPackageTableLayer::RollbackTransaction()
4091 : {
4092 18 : return m_poDS->RollbackTransaction();
4093 : }
4094 :
4095 : /************************************************************************/
4096 : /* GetTotalFeatureCount() */
4097 : /************************************************************************/
4098 :
4099 605 : GIntBig OGRGeoPackageTableLayer::GetTotalFeatureCount()
4100 : {
4101 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4102 605 : if (m_nTotalFeatureCount < 0 && m_poDS->m_bHasGPKGOGRContents)
4103 : {
4104 : char *pszSQL =
4105 20 : sqlite3_mprintf("SELECT feature_count FROM gpkg_ogr_contents WHERE "
4106 : "lower(table_name) = lower('%q') LIMIT 2",
4107 : m_pszTableName);
4108 40 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4109 20 : sqlite3_free(pszSQL);
4110 20 : if (oResult && oResult->RowCount() == 1)
4111 : {
4112 16 : const char *pszFeatureCount = oResult->GetValue(0, 0);
4113 16 : if (pszFeatureCount)
4114 : {
4115 7 : m_nTotalFeatureCount =
4116 7 : std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
4117 : // According to https://sqlite.org/limits.html
4118 : // "A 281 terabytes database can hold no more than approximately 2e+13 rows"
4119 : // We do that check to avoid returning values close to INT64_MAX
4120 7 : if (m_nTotalFeatureCount >
4121 : static_cast<GIntBig>(30) * 1000 * 1000 * 1000 * 1000)
4122 : {
4123 0 : CPLDebug("GPKG", "Invalid value in feature_count field of "
4124 : "gpkg_ogr_contents");
4125 0 : m_nTotalFeatureCount = -1;
4126 : }
4127 : }
4128 : }
4129 : }
4130 605 : return m_nTotalFeatureCount;
4131 : #else
4132 : return 0;
4133 : #endif
4134 : }
4135 :
4136 : /************************************************************************/
4137 : /* GetFeatureCount() */
4138 : /************************************************************************/
4139 :
4140 23132 : GIntBig OGRGeoPackageTableLayer::GetFeatureCount(int /*bForce*/)
4141 : {
4142 23132 : if (!m_bFeatureDefnCompleted)
4143 107 : GetLayerDefn();
4144 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4145 23132 : if (m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr)
4146 : {
4147 252 : const auto nCount = GetTotalFeatureCount();
4148 252 : if (nCount >= 0)
4149 239 : return nCount;
4150 : }
4151 : #endif
4152 :
4153 22893 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4154 0 : return 0;
4155 :
4156 22893 : CancelAsyncNextArrowArray();
4157 :
4158 22893 : if (m_poFilterGeom != nullptr)
4159 : {
4160 : // Both are exclusive
4161 22874 : CreateSpatialIndexIfNecessary();
4162 22874 : if (!RunDeferredSpatialIndexUpdate())
4163 0 : return -1;
4164 : }
4165 :
4166 : /* Ignore bForce, because we always do a full count on the database */
4167 : OGRErr err;
4168 45786 : CPLString soSQL;
4169 22893 : bool bUnregisterSQLFunction = false;
4170 22892 : if (m_bIsTable && m_poFilterGeom != nullptr &&
4171 45785 : m_pszAttrQueryString == nullptr && HasSpatialIndex())
4172 : {
4173 22866 : OGREnvelope sEnvelope;
4174 :
4175 22866 : m_poFilterGeom->getEnvelope(&sEnvelope);
4176 :
4177 45732 : if (!std::isinf(sEnvelope.MinX) && !std::isinf(sEnvelope.MinY) &&
4178 45732 : !std::isinf(sEnvelope.MaxX) && !std::isinf(sEnvelope.MaxY))
4179 : {
4180 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE "
4181 : "maxx >= %.12f AND minx <= %.12f AND "
4182 : "maxy >= %.12f AND miny <= %.12f",
4183 45732 : SQLEscapeName(m_osRTreeName).c_str(),
4184 22866 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4185 45732 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
4186 :
4187 45732 : if (OGRGeometryFactory::haveGEOS() &&
4188 22866 : !(m_bFilterIsEnvelope &&
4189 22866 : wkbFlatten(
4190 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4191 : ->GetType()) == wkbPoint))
4192 : {
4193 22854 : bUnregisterSQLFunction = true;
4194 22854 : sqlite3_create_function(
4195 22854 : m_poDS->hDB, "OGR_GPKG_Intersects_Spatial_Filter", 1,
4196 : SQLITE_UTF8, this, OGR_GPKG_Intersects_Spatial_Filter,
4197 : nullptr, nullptr);
4198 : const char *pszC =
4199 22854 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4200 22854 : ->GetNameRef();
4201 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" m "
4202 : "JOIN \"%s\" r "
4203 : "ON m.\"%s\" = r.id WHERE "
4204 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
4205 : "r.maxy >= %.12f AND r.miny <= %.12f AND "
4206 : "OGR_GPKG_Intersects_Spatial_Filter(m.\"%s\")",
4207 45708 : SQLEscapeName(m_pszTableName).c_str(),
4208 45708 : SQLEscapeName(m_osRTreeName).c_str(),
4209 45708 : SQLEscapeName(m_osFIDForRTree).c_str(),
4210 22854 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4211 22854 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11,
4212 114270 : SQLEscapeName(pszC).c_str());
4213 : }
4214 : }
4215 : }
4216 :
4217 22893 : if (soSQL.empty())
4218 : {
4219 27 : if (!m_soFilter.empty())
4220 : soSQL.Printf("SELECT Count(*) FROM \"%s\" WHERE %s",
4221 28 : SQLEscapeName(m_pszTableName).c_str(),
4222 28 : m_soFilter.c_str());
4223 : else
4224 : soSQL.Printf("SELECT Count(*) FROM \"%s\"",
4225 13 : SQLEscapeName(m_pszTableName).c_str());
4226 : }
4227 :
4228 : /* Just run the query directly and get back integer */
4229 : GIntBig iFeatureCount =
4230 22893 : SQLGetInteger64(m_poDS->GetDB(), soSQL.c_str(), &err);
4231 :
4232 22893 : if (bUnregisterSQLFunction)
4233 : {
4234 22854 : sqlite3_create_function(m_poDS->hDB,
4235 : "OGR_GPKG_Intersects_Spatial_Filter", 1,
4236 : SQLITE_UTF8, this, nullptr, nullptr, nullptr);
4237 : }
4238 :
4239 : /* Generic implementation uses -1 for error condition, so we will too */
4240 22893 : if (err == OGRERR_NONE)
4241 : {
4242 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4243 22893 : if (m_bIsTable && m_poFilterGeom == nullptr &&
4244 19 : m_pszAttrQueryString == nullptr)
4245 : {
4246 13 : m_nTotalFeatureCount = iFeatureCount;
4247 :
4248 13 : if (m_poDS->GetUpdate() && m_poDS->m_bHasGPKGOGRContents)
4249 : {
4250 : const char *pszCount =
4251 6 : CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
4252 6 : char *pszSQL = sqlite3_mprintf(
4253 : "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
4254 : "lower(table_name )= lower('%q')",
4255 : pszCount, m_pszTableName);
4256 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4257 6 : sqlite3_free(pszSQL);
4258 : }
4259 : }
4260 : #endif
4261 22893 : return iFeatureCount;
4262 : }
4263 : else
4264 0 : return -1;
4265 : }
4266 :
4267 : /************************************************************************/
4268 : /* GetExtentFromRTree() */
4269 : /************************************************************************/
4270 :
4271 155 : static bool GetExtentFromRTree(sqlite3 *hDB, const std::string &osRTreeName,
4272 : double &minx, double &miny, double &maxx,
4273 : double &maxy)
4274 : {
4275 : // Cf https://github.com/sqlite/sqlite/blob/master/ext/rtree/rtree.c
4276 : // for the description of the content of the rtree _node table
4277 : // We fetch the root node (nodeno = 1) and iterates over its cells, to
4278 : // take the min/max of their minx/maxx/miny/maxy values.
4279 155 : char *pszSQL = sqlite3_mprintf(
4280 : "SELECT data FROM \"%w_node\" WHERE nodeno = 1", osRTreeName.c_str());
4281 155 : sqlite3_stmt *hStmt = nullptr;
4282 155 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(hDB, pszSQL, -1, &hStmt, nullptr));
4283 155 : sqlite3_free(pszSQL);
4284 155 : bool bOK = false;
4285 155 : if (hStmt)
4286 : {
4287 310 : if (sqlite3_step(hStmt) == SQLITE_ROW &&
4288 155 : sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
4289 : {
4290 155 : const int nBytes = sqlite3_column_bytes(hStmt, 0);
4291 : // coverity[tainted_data_return]
4292 : const GByte *pabyData =
4293 155 : static_cast<const GByte *>(sqlite3_column_blob(hStmt, 0));
4294 155 : constexpr int BLOB_HEADER_SIZE = 4;
4295 155 : if (nBytes > BLOB_HEADER_SIZE)
4296 : {
4297 155 : const int nCellCount = (pabyData[2] << 8) | pabyData[3];
4298 155 : constexpr int SIZEOF_CELL = 24; // int64_t + 4 float
4299 155 : if (nCellCount >= 1 &&
4300 148 : nBytes >= BLOB_HEADER_SIZE + SIZEOF_CELL * nCellCount)
4301 : {
4302 148 : minx = std::numeric_limits<double>::max();
4303 148 : miny = std::numeric_limits<double>::max();
4304 148 : maxx = -std::numeric_limits<double>::max();
4305 148 : maxy = -std::numeric_limits<double>::max();
4306 148 : size_t offset = BLOB_HEADER_SIZE;
4307 486 : for (int i = 0; i < nCellCount; ++i)
4308 : {
4309 338 : offset += sizeof(int64_t);
4310 :
4311 : float fMinX;
4312 338 : memcpy(&fMinX, pabyData + offset, sizeof(float));
4313 338 : offset += sizeof(float);
4314 338 : CPL_MSBPTR32(&fMinX);
4315 338 : minx = std::min(minx, static_cast<double>(fMinX));
4316 :
4317 : float fMaxX;
4318 338 : memcpy(&fMaxX, pabyData + offset, sizeof(float));
4319 338 : offset += sizeof(float);
4320 338 : CPL_MSBPTR32(&fMaxX);
4321 338 : maxx = std::max(maxx, static_cast<double>(fMaxX));
4322 :
4323 : float fMinY;
4324 338 : memcpy(&fMinY, pabyData + offset, sizeof(float));
4325 338 : offset += sizeof(float);
4326 338 : CPL_MSBPTR32(&fMinY);
4327 338 : miny = std::min(miny, static_cast<double>(fMinY));
4328 :
4329 : float fMaxY;
4330 338 : memcpy(&fMaxY, pabyData + offset, sizeof(float));
4331 338 : offset += sizeof(float);
4332 338 : CPL_MSBPTR32(&fMaxY);
4333 338 : maxy = std::max(maxy, static_cast<double>(fMaxY));
4334 : }
4335 :
4336 148 : bOK = true;
4337 : }
4338 : }
4339 : }
4340 155 : sqlite3_finalize(hStmt);
4341 : }
4342 155 : return bOK;
4343 : }
4344 :
4345 : /************************************************************************/
4346 : /* IGetExtent() */
4347 : /************************************************************************/
4348 :
4349 334 : OGRErr OGRGeoPackageTableLayer::IGetExtent(int /* iGeomField */,
4350 : OGREnvelope *psExtent, bool bForce)
4351 : {
4352 334 : if (!m_bFeatureDefnCompleted)
4353 0 : GetLayerDefn();
4354 : /* Extent already calculated! We're done. */
4355 334 : if (m_poExtent != nullptr)
4356 : {
4357 319 : if (psExtent)
4358 : {
4359 319 : *psExtent = *m_poExtent;
4360 : }
4361 319 : return OGRERR_NONE;
4362 : }
4363 :
4364 15 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4365 0 : return OGRERR_FAILURE;
4366 :
4367 15 : CancelAsyncNextArrowArray();
4368 :
4369 24 : if (m_poFeatureDefn->GetGeomFieldCount() && HasSpatialIndex() &&
4370 9 : CPLTestBool(
4371 : CPLGetConfigOption("OGR_GPKG_USE_RTREE_FOR_GET_EXTENT", "TRUE")))
4372 : {
4373 9 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, psExtent->MinX,
4374 9 : psExtent->MinY, psExtent->MaxX, psExtent->MaxY))
4375 : {
4376 2 : m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
4377 2 : m_bExtentChanged = true;
4378 2 : SaveExtent();
4379 2 : return OGRERR_NONE;
4380 : }
4381 : else
4382 : {
4383 7 : UpdateContentsToNullExtent();
4384 7 : return OGRERR_FAILURE;
4385 : }
4386 : }
4387 :
4388 : /* User is OK with expensive calculation */
4389 6 : if (bForce && m_poFeatureDefn->GetGeomFieldCount())
4390 : {
4391 : /* fall back to default implementation (scan all features) and save */
4392 : /* the result for later */
4393 4 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4394 4 : char *pszSQL = sqlite3_mprintf(
4395 : "SELECT MIN(ST_MinX(\"%w\")), MIN(ST_MinY(\"%w\")), "
4396 : "MAX(ST_MaxX(\"%w\")), MAX(ST_MaxY(\"%w\")) FROM \"%w\" WHERE "
4397 : "\"%w\" IS NOT NULL AND NOT ST_IsEmpty(\"%w\")",
4398 : pszC, pszC, pszC, pszC, m_pszTableName, pszC, pszC);
4399 8 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4400 4 : sqlite3_free(pszSQL);
4401 4 : m_poExtent.reset();
4402 8 : if (oResult && oResult->RowCount() == 1 &&
4403 4 : oResult->GetValue(0, 0) != nullptr)
4404 : {
4405 3 : psExtent->MinX = CPLAtof(oResult->GetValue(0, 0));
4406 3 : psExtent->MinY = CPLAtof(oResult->GetValue(1, 0));
4407 3 : psExtent->MaxX = CPLAtof(oResult->GetValue(2, 0));
4408 3 : psExtent->MaxY = CPLAtof(oResult->GetValue(3, 0));
4409 3 : m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
4410 3 : m_bExtentChanged = true;
4411 3 : SaveExtent();
4412 : }
4413 : else
4414 : {
4415 1 : UpdateContentsToNullExtent();
4416 1 : return OGRERR_FAILURE; // we didn't get an extent
4417 : }
4418 3 : return OGRERR_NONE;
4419 : }
4420 :
4421 2 : return OGRERR_FAILURE;
4422 : }
4423 :
4424 : /************************************************************************/
4425 : /* UpdateContentsToNullExtent() */
4426 : /************************************************************************/
4427 :
4428 8 : void OGRGeoPackageTableLayer::UpdateContentsToNullExtent()
4429 : {
4430 8 : if (m_poDS->GetUpdate())
4431 : {
4432 : char *pszSQL =
4433 3 : sqlite3_mprintf("UPDATE gpkg_contents SET "
4434 : "min_x = NULL, min_y = NULL, "
4435 : "max_x = NULL, max_y = NULL "
4436 : "WHERE lower(table_name) = lower('%q') AND "
4437 : "Lower(data_type) = 'features'",
4438 : m_pszTableName);
4439 3 : SQLCommand(m_poDS->GetDB(), pszSQL);
4440 3 : sqlite3_free(pszSQL);
4441 : }
4442 8 : m_bExtentChanged = false;
4443 8 : }
4444 :
4445 : /************************************************************************/
4446 : /* RecomputeExtent() */
4447 : /************************************************************************/
4448 :
4449 4 : void OGRGeoPackageTableLayer::RecomputeExtent()
4450 : {
4451 4 : m_bExtentChanged = true;
4452 4 : m_poExtent.reset();
4453 4 : OGREnvelope sExtent;
4454 4 : CPL_IGNORE_RET_VAL(GetExtent(&sExtent, true));
4455 4 : }
4456 :
4457 : /************************************************************************/
4458 : /* TestCapability() */
4459 : /************************************************************************/
4460 :
4461 2396 : bool OGRGeoPackageTableLayer::TestCapability(const char *pszCap) const
4462 : {
4463 2396 : if (!m_bFeatureDefnCompleted)
4464 17 : GetLayerDefn();
4465 2396 : if (EQUAL(pszCap, OLCSequentialWrite))
4466 : {
4467 32 : return m_poDS->GetUpdate();
4468 : }
4469 2364 : else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
4470 2335 : EQUAL(pszCap, OLCAlterFieldDefn) ||
4471 2328 : EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
4472 2327 : EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCRename))
4473 : {
4474 45 : return m_poDS->GetUpdate() && m_bIsTable;
4475 : }
4476 2319 : else if (EQUAL(pszCap, OLCDeleteFeature) ||
4477 2312 : EQUAL(pszCap, OLCUpsertFeature) ||
4478 2306 : EQUAL(pszCap, OLCUpdateFeature) || EQUAL(pszCap, OLCRandomWrite))
4479 : {
4480 18 : return m_poDS->GetUpdate() && m_pszFidColumn != nullptr;
4481 : }
4482 2301 : else if (EQUAL(pszCap, OLCRandomRead))
4483 : {
4484 6 : return m_pszFidColumn != nullptr;
4485 : }
4486 2295 : else if (EQUAL(pszCap, OLCTransactions))
4487 : {
4488 17 : return TRUE;
4489 : }
4490 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4491 2278 : else if (EQUAL(pszCap, OLCFastFeatureCount))
4492 : {
4493 26 : return m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr &&
4494 26 : m_nTotalFeatureCount >= 0;
4495 : }
4496 : #endif
4497 2265 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
4498 : {
4499 3 : return HasSpatialIndex() || m_bDeferredSpatialIndexCreation;
4500 : }
4501 2262 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
4502 : {
4503 : // Fast may not be that true on large layers, but better than the
4504 : // default implementation for sure...
4505 0 : return TRUE;
4506 : }
4507 2262 : else if (EQUAL(pszCap, OLCFastGetExtent))
4508 : {
4509 5 : return (m_poExtent != nullptr);
4510 : }
4511 2257 : else if (EQUAL(pszCap, OLCCurveGeometries))
4512 1008 : return TRUE;
4513 1249 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
4514 1005 : return TRUE;
4515 244 : else if (EQUAL(pszCap, OLCZGeometries))
4516 166 : return TRUE;
4517 78 : if (EQUAL(pszCap, OLCFastGetExtent3D))
4518 0 : return TRUE;
4519 : else
4520 : {
4521 78 : return OGRGeoPackageLayer::TestCapability(pszCap);
4522 : }
4523 : }
4524 :
4525 : /************************************************************************/
4526 : /* CreateSpatialIndexIfNecessary() */
4527 : /************************************************************************/
4528 :
4529 43622 : void OGRGeoPackageTableLayer::CreateSpatialIndexIfNecessary()
4530 : {
4531 43622 : if (m_bDeferredSpatialIndexCreation)
4532 : {
4533 926 : CreateSpatialIndex();
4534 : }
4535 43622 : }
4536 :
4537 : /************************************************************************/
4538 : /* CreateSpatialIndex() */
4539 : /************************************************************************/
4540 :
4541 936 : bool OGRGeoPackageTableLayer::CreateSpatialIndex(const char *pszTableName)
4542 : {
4543 : OGRErr err;
4544 :
4545 936 : if (!m_bFeatureDefnCompleted)
4546 2 : GetLayerDefn();
4547 :
4548 936 : if (!CheckUpdatableTable("CreateSpatialIndex"))
4549 1 : return false;
4550 :
4551 935 : if (m_bDropRTreeTable)
4552 : {
4553 0 : CPLError(CE_Failure, CPLE_AppDefined,
4554 : "Cannot run CreateSpatialIndex() after non-completed deferred "
4555 : "DropSpatialIndex()");
4556 0 : return false;
4557 : }
4558 :
4559 935 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4560 0 : return false;
4561 :
4562 935 : CancelAsyncNextArrowArray();
4563 :
4564 935 : m_bDeferredSpatialIndexCreation = false;
4565 :
4566 935 : if (m_pszFidColumn == nullptr)
4567 0 : return false;
4568 :
4569 935 : if (HasSpatialIndex())
4570 : {
4571 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index already existing");
4572 1 : return false;
4573 : }
4574 :
4575 934 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
4576 : {
4577 1 : CPLError(CE_Failure, CPLE_AppDefined, "No geometry column");
4578 1 : return false;
4579 : }
4580 933 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
4581 0 : return false;
4582 :
4583 933 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4584 933 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4585 933 : const char *pszI = GetFIDColumn();
4586 :
4587 933 : m_osRTreeName = "rtree_";
4588 933 : m_osRTreeName += pszT;
4589 933 : m_osRTreeName += "_";
4590 933 : m_osRTreeName += pszC;
4591 933 : m_osFIDForRTree = m_pszFidColumn;
4592 :
4593 933 : bool bPopulateFromThreadRTree = false;
4594 933 : if (m_bThreadRTreeStarted)
4595 : {
4596 12 : const bool bThreadHasFinished = m_oQueueRTreeEntries.empty();
4597 12 : if (!m_aoRTreeEntries.empty())
4598 4 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
4599 12 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
4600 12 : m_oQueueRTreeEntries.push(m_aoRTreeEntries);
4601 12 : if (!bThreadHasFinished)
4602 1 : CPLDebug("GPKG", "Waiting for background RTree building to finish");
4603 12 : m_oThreadRTree.join();
4604 12 : if (!bThreadHasFinished)
4605 : {
4606 1 : CPLDebug("GPKG", "Background RTree building finished");
4607 : }
4608 12 : m_bAllowedRTreeThread = false;
4609 12 : m_bThreadRTreeStarted = false;
4610 :
4611 12 : if (m_hAsyncDBHandle)
4612 : {
4613 8 : sqlite3_close(m_hAsyncDBHandle);
4614 8 : m_hAsyncDBHandle = nullptr;
4615 : }
4616 12 : if (m_bErrorDuringRTreeThread)
4617 : {
4618 4 : RemoveAsyncRTreeTempDB();
4619 : }
4620 : else
4621 : {
4622 8 : bPopulateFromThreadRTree = true;
4623 : }
4624 : }
4625 :
4626 933 : m_poDS->SoftStartTransaction();
4627 :
4628 933 : if (m_hRTree)
4629 : {
4630 4 : if (!FlushInMemoryRTree(m_poDS->GetDB(), m_osRTreeName.c_str()))
4631 : {
4632 0 : m_poDS->SoftRollbackTransaction();
4633 0 : return false;
4634 : }
4635 : }
4636 929 : else if (bPopulateFromThreadRTree)
4637 : {
4638 : /* Create virtual table */
4639 4 : char *pszSQL = sqlite3_mprintf("CREATE VIRTUAL TABLE \"%w\" USING "
4640 : "rtree(id, minx, maxx, miny, maxy)",
4641 : m_osRTreeName.c_str());
4642 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4643 4 : sqlite3_free(pszSQL);
4644 4 : if (err != OGRERR_NONE)
4645 : {
4646 0 : m_poDS->SoftRollbackTransaction();
4647 0 : return false;
4648 : }
4649 :
4650 4 : pszSQL = sqlite3_mprintf(
4651 : "DELETE FROM \"%w_node\";\n"
4652 : "INSERT INTO \"%w_node\" SELECT * FROM \"%w\".my_rtree_node;\n"
4653 : "INSERT INTO \"%w_rowid\" SELECT * FROM "
4654 : "\"%w\".my_rtree_rowid;\n"
4655 : "INSERT INTO \"%w_parent\" SELECT * FROM "
4656 : "\"%w\".my_rtree_parent;\n",
4657 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
4658 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4659 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4660 : m_osAsyncDBAttachName.c_str());
4661 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4662 4 : sqlite3_free(pszSQL);
4663 4 : if (err != OGRERR_NONE)
4664 : {
4665 0 : m_poDS->SoftRollbackTransaction();
4666 0 : RemoveAsyncRTreeTempDB();
4667 0 : return false;
4668 : }
4669 : }
4670 : else
4671 : {
4672 : /* Populate the RTree */
4673 925 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
4674 925 : char *pszErrMsg = nullptr;
4675 :
4676 : struct ProgressCbk
4677 : {
4678 602 : static bool progressCbk(const char *pszMessage, void *)
4679 : {
4680 602 : CPLDebug("GPKG", "%s", pszMessage);
4681 602 : return true;
4682 : }
4683 : };
4684 :
4685 1850 : if (!gdal_sqlite_rtree_bl_from_feature_table(
4686 925 : m_poDS->GetDB(), pszT, pszI, pszC, m_osRTreeName.c_str(), "id",
4687 : "minx", "miny", "maxx", "maxy", nMaxRAMUsageAllowed, &pszErrMsg,
4688 : ProgressCbk::progressCbk, nullptr))
4689 : {
4690 0 : CPLError(CE_Failure, CPLE_AppDefined,
4691 : "gdal_sqlite_rtree_bl_from_feature_table() failed "
4692 : "with %s",
4693 0 : pszErrMsg ? pszErrMsg : "(null)");
4694 0 : m_poDS->SoftRollbackTransaction();
4695 0 : sqlite3_free(pszErrMsg);
4696 0 : return false;
4697 : }
4698 : }
4699 :
4700 1866 : CPLString osSQL;
4701 :
4702 : /* Register the table in gpkg_extensions */
4703 933 : char *pszSQL = sqlite3_mprintf(
4704 : "INSERT INTO gpkg_extensions "
4705 : "(table_name,column_name,extension_name,definition,scope) "
4706 : "VALUES ('%q', '%q', 'gpkg_rtree_index', "
4707 : "'http://www.geopackage.org/spec120/#extension_rtree', 'write-only')",
4708 : pszT, pszC);
4709 933 : osSQL += pszSQL;
4710 933 : sqlite3_free(pszSQL);
4711 :
4712 : /* Define Triggers to Maintain Spatial Index Values */
4713 933 : osSQL += ";" + ReturnSQLCreateSpatialIndexTriggers(pszTableName, nullptr);
4714 :
4715 933 : err = SQLCommand(m_poDS->GetDB(), osSQL);
4716 933 : if (err != OGRERR_NONE)
4717 : {
4718 0 : m_poDS->SoftRollbackTransaction();
4719 0 : if (bPopulateFromThreadRTree)
4720 : {
4721 0 : RemoveAsyncRTreeTempDB();
4722 : }
4723 0 : return false;
4724 : }
4725 :
4726 933 : m_poDS->SoftCommitTransaction();
4727 :
4728 933 : if (bPopulateFromThreadRTree)
4729 : {
4730 8 : RemoveAsyncRTreeTempDB();
4731 : }
4732 :
4733 933 : m_bHasSpatialIndex = true;
4734 :
4735 933 : return true;
4736 : }
4737 :
4738 : /************************************************************************/
4739 : /* WorkaroundUpdate1TriggerIssue() */
4740 : /************************************************************************/
4741 :
4742 18 : void OGRGeoPackageTableLayer::WorkaroundUpdate1TriggerIssue()
4743 : {
4744 : // Workaround issue of https://sqlite.org/forum/forumpost/8c8de6ff91
4745 : // Basically the official _update1 spatial index trigger doesn't work
4746 : // with current versions of SQLite when invoked from an UPSERT statement.
4747 : // In GeoPackage 1.4, the update6 and update7 triggers replace update1
4748 :
4749 18 : if (m_bHasUpdate6And7Triggers || m_poFeatureDefn->GetGeomFieldCount() == 0)
4750 12 : return;
4751 :
4752 11 : const char *pszT = m_pszTableName;
4753 11 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4754 11 : const char *pszI = GetFIDColumn();
4755 :
4756 11 : CPLString osRTreeName = "rtree_";
4757 11 : osRTreeName += pszT;
4758 11 : osRTreeName += "_";
4759 11 : osRTreeName += pszC;
4760 :
4761 : // Check if update6 and update7 triggers are there
4762 : {
4763 22 : char *pszSQL = sqlite3_mprintf(
4764 : "SELECT * FROM sqlite_master WHERE type = 'trigger' "
4765 : "AND name IN ('%q', '%q')",
4766 22 : (m_osRTreeName + "_update6").c_str(),
4767 22 : (m_osRTreeName + "_update7").c_str());
4768 11 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4769 11 : sqlite3_free(pszSQL);
4770 11 : if (oResult && oResult->RowCount() == 2)
4771 : {
4772 5 : m_bHasUpdate6And7Triggers = true;
4773 5 : return;
4774 : }
4775 : }
4776 :
4777 : char *pszSQL =
4778 6 : sqlite3_mprintf("SELECT sql FROM sqlite_master WHERE type = 'trigger' "
4779 : "AND name = '%q'",
4780 12 : (m_osRTreeName + "_update1").c_str());
4781 6 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4782 6 : sqlite3_free(pszSQL);
4783 6 : if (oResult && oResult->RowCount() == 1)
4784 : {
4785 6 : const char *pszTriggerSQL = oResult->GetValue(0, 0);
4786 6 : if (pszTriggerSQL)
4787 : {
4788 6 : m_osUpdate1Trigger = pszTriggerSQL;
4789 : }
4790 : }
4791 6 : if (m_osUpdate1Trigger.empty())
4792 0 : return;
4793 :
4794 6 : m_bUpdate1TriggerDisabled = true;
4795 :
4796 : pszSQL =
4797 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update1\"", osRTreeName.c_str());
4798 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4799 6 : sqlite3_free(pszSQL);
4800 :
4801 6 : pszSQL = sqlite3_mprintf(
4802 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4803 : "ON \"%w\" "
4804 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4805 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4806 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4807 : "BEGIN "
4808 : "UPDATE \"%w\" SET "
4809 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4810 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4811 : "WHERE id = NEW.\"%w\";"
4812 : "END",
4813 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4814 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4815 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4816 6 : sqlite3_free(pszSQL);
4817 :
4818 6 : pszSQL = sqlite3_mprintf(
4819 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4820 : "\"%w\" "
4821 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4822 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4823 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4824 : "BEGIN "
4825 : "INSERT INTO \"%w\" VALUES ("
4826 : "NEW.\"%w\","
4827 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4828 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4829 : "); "
4830 : "END",
4831 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4832 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4833 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4834 6 : sqlite3_free(pszSQL);
4835 : }
4836 :
4837 : /************************************************************************/
4838 : /* RevertWorkaroundUpdate1TriggerIssue() */
4839 : /************************************************************************/
4840 :
4841 5977 : void OGRGeoPackageTableLayer::RevertWorkaroundUpdate1TriggerIssue()
4842 : {
4843 5977 : if (!m_bUpdate1TriggerDisabled)
4844 5971 : return;
4845 6 : m_bUpdate1TriggerDisabled = false;
4846 6 : CPLAssert(!m_bHasUpdate6And7Triggers);
4847 :
4848 6 : const char *pszT = m_pszTableName;
4849 6 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4850 :
4851 12 : CPLString osRTreeName = "rtree_";
4852 6 : osRTreeName += pszT;
4853 6 : osRTreeName += "_";
4854 6 : osRTreeName += pszC;
4855 :
4856 : char *pszSQL;
4857 :
4858 6 : SQLCommand(m_poDS->GetDB(), m_osUpdate1Trigger.c_str());
4859 6 : m_osUpdate1Trigger.clear();
4860 :
4861 : pszSQL =
4862 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update6\"", osRTreeName.c_str());
4863 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4864 6 : sqlite3_free(pszSQL);
4865 :
4866 : pszSQL =
4867 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update7\"", osRTreeName.c_str());
4868 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4869 6 : sqlite3_free(pszSQL);
4870 : }
4871 :
4872 : /************************************************************************/
4873 : /* ReturnSQLCreateSpatialIndexTriggers() */
4874 : /************************************************************************/
4875 :
4876 945 : CPLString OGRGeoPackageTableLayer::ReturnSQLCreateSpatialIndexTriggers(
4877 : const char *pszTableName, const char *pszGeomColName)
4878 : {
4879 : char *pszSQL;
4880 945 : CPLString osSQL;
4881 :
4882 945 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4883 : const char *pszC = (pszGeomColName)
4884 945 : ? pszGeomColName
4885 941 : : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4886 945 : const char *pszI = GetFIDColumn();
4887 :
4888 1890 : CPLString osRTreeName = "rtree_";
4889 945 : osRTreeName += pszT;
4890 945 : osRTreeName += "_";
4891 945 : osRTreeName += pszC;
4892 :
4893 : /* Conditions: Insertion of non-empty geometry
4894 : Actions : Insert record into rtree */
4895 945 : pszSQL = sqlite3_mprintf(
4896 : "CREATE TRIGGER \"%w_insert\" AFTER INSERT ON \"%w\" "
4897 : "WHEN (new.\"%w\" NOT NULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4898 : "BEGIN "
4899 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4900 : "NEW.\"%w\","
4901 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4902 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4903 : "); "
4904 : "END",
4905 : osRTreeName.c_str(), pszT, pszC, pszC, osRTreeName.c_str(), pszI, pszC,
4906 : pszC, pszC, pszC);
4907 945 : osSQL += pszSQL;
4908 945 : sqlite3_free(pszSQL);
4909 :
4910 945 : if (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
4911 942 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
4912 : {
4913 : /* Conditions: Update a non-empty geometry with another non-empty geometry
4914 : Actions : Replace record from R-tree
4915 : */
4916 931 : pszSQL = sqlite3_mprintf(
4917 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4918 : "ON \"%w\" "
4919 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4920 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4921 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4922 : "BEGIN "
4923 : "UPDATE \"%w\" SET "
4924 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4925 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4926 : "WHERE id = NEW.\"%w\";"
4927 : "END",
4928 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4929 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4930 931 : osSQL += ";";
4931 931 : osSQL += pszSQL;
4932 931 : sqlite3_free(pszSQL);
4933 :
4934 : /* Conditions: Update a null/empty geometry with a non-empty geometry
4935 : Actions : Insert record into R-tree
4936 : */
4937 931 : pszSQL = sqlite3_mprintf(
4938 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4939 : "\"%w\" "
4940 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4941 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4942 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4943 : "BEGIN "
4944 : "INSERT INTO \"%w\" VALUES ("
4945 : "NEW.\"%w\","
4946 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4947 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4948 : "); "
4949 : "END",
4950 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4951 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4952 931 : osSQL += ";";
4953 931 : osSQL += pszSQL;
4954 931 : sqlite3_free(pszSQL);
4955 : }
4956 : else
4957 : {
4958 : /* Conditions: Update of geometry column to non-empty geometry
4959 : No row ID change
4960 : Actions : Update record in rtree */
4961 14 : pszSQL = sqlite3_mprintf(
4962 : "CREATE TRIGGER \"%w_update1\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4963 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4964 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4965 : "BEGIN "
4966 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4967 : "NEW.\"%w\","
4968 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4969 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4970 : "); "
4971 : "END",
4972 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4973 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4974 14 : osSQL += ";";
4975 14 : osSQL += pszSQL;
4976 14 : sqlite3_free(pszSQL);
4977 : }
4978 :
4979 : /* Conditions: Update of geometry column to empty geometry
4980 : No row ID change
4981 : Actions : Remove record from rtree */
4982 945 : pszSQL = sqlite3_mprintf(
4983 : "CREATE TRIGGER \"%w_update2\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4984 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4985 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
4986 : "BEGIN "
4987 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4988 : "END",
4989 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4990 : osRTreeName.c_str(), pszI);
4991 945 : osSQL += ";";
4992 945 : osSQL += pszSQL;
4993 945 : sqlite3_free(pszSQL);
4994 :
4995 : /* Conditions: Update of any column
4996 : Row ID change
4997 : Non-empty geometry
4998 : Actions : Remove record from rtree for old <i>
4999 : Insert record into rtree for new <i> */
5000 : pszSQL =
5001 1890 : sqlite3_mprintf("CREATE TRIGGER \"%w_%s\" AFTER UPDATE ON \"%w\" "
5002 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
5003 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
5004 : "BEGIN "
5005 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
5006 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
5007 : "NEW.\"%w\","
5008 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
5009 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
5010 : "); "
5011 : "END",
5012 : osRTreeName.c_str(),
5013 945 : (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
5014 942 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
5015 : ? "update5"
5016 : : "update3",
5017 : pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(), pszI,
5018 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
5019 945 : osSQL += ";";
5020 945 : osSQL += pszSQL;
5021 945 : sqlite3_free(pszSQL);
5022 :
5023 : /* Conditions: Update of any column
5024 : Row ID change
5025 : Empty geometry
5026 : Actions : Remove record from rtree for old and new <i> */
5027 945 : pszSQL = sqlite3_mprintf(
5028 : "CREATE TRIGGER \"%w_update4\" AFTER UPDATE ON \"%w\" "
5029 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
5030 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
5031 : "BEGIN "
5032 : "DELETE FROM \"%w\" WHERE id IN (OLD.\"%w\", NEW.\"%w\"); "
5033 : "END",
5034 : osRTreeName.c_str(), pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(),
5035 : pszI, pszI);
5036 945 : osSQL += ";";
5037 945 : osSQL += pszSQL;
5038 945 : sqlite3_free(pszSQL);
5039 :
5040 : /* Conditions: Row deleted
5041 : Actions : Remove record from rtree for old <i> */
5042 945 : pszSQL = sqlite3_mprintf(
5043 : "CREATE TRIGGER \"%w_delete\" AFTER DELETE ON \"%w\" "
5044 : "WHEN old.\"%w\" NOT NULL "
5045 : "BEGIN "
5046 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
5047 : "END",
5048 : osRTreeName.c_str(), pszT, pszC, osRTreeName.c_str(), pszI);
5049 945 : osSQL += ";";
5050 945 : osSQL += pszSQL;
5051 945 : sqlite3_free(pszSQL);
5052 :
5053 1890 : return osSQL;
5054 : }
5055 :
5056 : /************************************************************************/
5057 : /* CheckUnknownExtensions() */
5058 : /************************************************************************/
5059 :
5060 1074 : void OGRGeoPackageTableLayer::CheckUnknownExtensions()
5061 : {
5062 : const std::map<CPLString, std::vector<GPKGExtensionDesc>> &oMap =
5063 1074 : m_poDS->GetUnknownExtensionsTableSpecific();
5064 1074 : const auto oIter = oMap.find(CPLString(m_pszTableName).toupper());
5065 1074 : if (oIter != oMap.end())
5066 : {
5067 12 : for (size_t i = 0; i < oIter->second.size(); i++)
5068 : {
5069 6 : const char *pszExtName = oIter->second[i].osExtensionName.c_str();
5070 6 : const char *pszDefinition = oIter->second[i].osDefinition.c_str();
5071 6 : const char *pszScope = oIter->second[i].osScope.c_str();
5072 6 : if (m_poDS->GetUpdate() && EQUAL(pszScope, "write-only"))
5073 : {
5074 1 : CPLError(
5075 : CE_Warning, CPLE_AppDefined,
5076 : "Layer %s relies on the '%s' (%s) extension that should "
5077 : "be implemented for safe write-support, but is not "
5078 : "currently. "
5079 : "Update of that layer are strongly discouraged to avoid "
5080 : "corruption.",
5081 : GetName(), pszExtName, pszDefinition);
5082 : }
5083 5 : else if (m_poDS->GetUpdate() && EQUAL(pszScope, "read-write"))
5084 : {
5085 1 : CPLError(
5086 : CE_Warning, CPLE_AppDefined,
5087 : "Layer %s relies on the '%s' (%s) extension that should "
5088 : "be implemented in order to read/write it safely, but is "
5089 : "not currently. "
5090 : "Some data may be missing while reading that layer, and "
5091 : "updates are strongly discouraged.",
5092 : GetName(), pszExtName, pszDefinition);
5093 : }
5094 4 : else if (EQUAL(pszScope, "read-write") &&
5095 : // None of the NGA extensions at
5096 : // http://ngageoint.github.io/GeoPackage/docs/extensions/
5097 : // affect read-only scenarios
5098 2 : !STARTS_WITH(pszExtName, "nga_"))
5099 : {
5100 2 : CPLError(
5101 : CE_Warning, CPLE_AppDefined,
5102 : "Layer %s relies on the '%s' (%s) extension that should "
5103 : "be implemented in order to read it safely, but is not "
5104 : "currently. "
5105 : "Some data may be missing while reading that layer.",
5106 : GetName(), pszExtName, pszDefinition);
5107 : }
5108 : }
5109 : }
5110 1074 : }
5111 :
5112 : /************************************************************************/
5113 : /* CreateGeometryExtensionIfNecessary() */
5114 : /************************************************************************/
5115 :
5116 264039 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5117 : const OGRGeometry *poGeom)
5118 : {
5119 264039 : bool bRet = true;
5120 264039 : if (poGeom != nullptr)
5121 : {
5122 264039 : OGRwkbGeometryType eGType = wkbFlatten(poGeom->getGeometryType());
5123 264039 : if (eGType >= wkbGeometryCollection)
5124 : {
5125 24 : if (eGType > wkbGeometryCollection)
5126 12 : CreateGeometryExtensionIfNecessary(eGType);
5127 : const OGRGeometryCollection *poGC =
5128 24 : dynamic_cast<const OGRGeometryCollection *>(poGeom);
5129 24 : if (poGC != nullptr)
5130 : {
5131 13 : const int nSubGeoms = poGC->getNumGeometries();
5132 34 : for (int i = 0; i < nSubGeoms; i++)
5133 : {
5134 21 : bRet &= CreateGeometryExtensionIfNecessary(
5135 21 : poGC->getGeometryRef(i));
5136 : }
5137 : }
5138 : }
5139 : }
5140 264039 : return bRet;
5141 : }
5142 :
5143 : /************************************************************************/
5144 : /* CreateGeometryExtensionIfNecessary() */
5145 : /************************************************************************/
5146 :
5147 19 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5148 : OGRwkbGeometryType eGType)
5149 : {
5150 19 : eGType = wkbFlatten(eGType);
5151 19 : CPLAssert(eGType > wkbGeometryCollection && eGType <= wkbTriangle);
5152 19 : if (m_abHasGeometryExtension[eGType])
5153 3 : return true;
5154 :
5155 16 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
5156 0 : return false;
5157 :
5158 16 : const char *pszT = m_pszTableName;
5159 16 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5160 16 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5161 :
5162 : // Check first if the extension isn't registered
5163 16 : char *pszSQL = sqlite3_mprintf(
5164 : "SELECT 1 FROM gpkg_extensions WHERE lower(table_name) = lower('%q') "
5165 : "AND "
5166 : "lower(column_name) = lower('%q') AND extension_name = 'gpkg_geom_%s'",
5167 : pszT, pszC, pszGeometryType);
5168 16 : const bool bExists = SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5169 16 : sqlite3_free(pszSQL);
5170 :
5171 16 : if (!bExists)
5172 : {
5173 15 : if (eGType == wkbPolyhedralSurface || eGType == wkbTIN ||
5174 : eGType == wkbTriangle)
5175 : {
5176 2 : CPLError(CE_Warning, CPLE_AppDefined,
5177 : "Registering non-standard gpkg_geom_%s extension",
5178 : pszGeometryType);
5179 : }
5180 :
5181 : /* Register the table in gpkg_extensions */
5182 15 : pszSQL = sqlite3_mprintf(
5183 : "INSERT INTO gpkg_extensions "
5184 : "(table_name,column_name,extension_name,definition,scope) "
5185 : "VALUES ('%q', '%q', 'gpkg_geom_%s', "
5186 : "'http://www.geopackage.org/spec120/#extension_geometry_types', "
5187 : "'read-write')",
5188 : pszT, pszC, pszGeometryType);
5189 15 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5190 15 : sqlite3_free(pszSQL);
5191 15 : if (err != OGRERR_NONE)
5192 0 : return false;
5193 : }
5194 :
5195 16 : m_abHasGeometryExtension[eGType] = true;
5196 16 : return true;
5197 : }
5198 :
5199 : /************************************************************************/
5200 : /* HasSpatialIndex() */
5201 : /************************************************************************/
5202 :
5203 49149 : bool OGRGeoPackageTableLayer::HasSpatialIndex() const
5204 : {
5205 49149 : if (!m_bFeatureDefnCompleted)
5206 3 : GetLayerDefn();
5207 49149 : if (m_bHasSpatialIndex >= 0)
5208 47905 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5209 1244 : m_bHasSpatialIndex = false;
5210 :
5211 3732 : if (m_pszFidColumn == nullptr ||
5212 2477 : m_poFeatureDefn->GetGeomFieldCount() == 0 ||
5213 1233 : !m_poDS->HasExtensionsTable())
5214 711 : return false;
5215 :
5216 533 : const char *pszT = m_pszTableName;
5217 533 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5218 : CPLString osRTreeName(
5219 1066 : CPLString("rtree_").append(pszT).append("_").append(pszC));
5220 : const std::map<CPLString, CPLString> &oMap =
5221 533 : m_poDS->GetNameTypeMapFromSQliteMaster();
5222 533 : if (cpl::contains(oMap, CPLString(osRTreeName).toupper()))
5223 : {
5224 281 : m_bHasSpatialIndex = true;
5225 281 : m_osRTreeName = std::move(osRTreeName);
5226 281 : m_osFIDForRTree = m_pszFidColumn;
5227 : }
5228 :
5229 : // Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
5230 : // Cf https://github.com/OSGeo/gdal/pull/6911
5231 533 : if (m_bHasSpatialIndex)
5232 : {
5233 : const auto nFC =
5234 281 : const_cast<OGRGeoPackageTableLayer *>(this)->GetTotalFeatureCount();
5235 281 : if (nFC >= atoi(CPLGetConfigOption(
5236 : "OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")))
5237 : {
5238 2 : CPLString osSQL = "SELECT 1 FROM \"";
5239 1 : osSQL += SQLEscapeName(pszT);
5240 1 : osSQL += "\" WHERE \"";
5241 2 : osSQL += SQLEscapeName(
5242 1 : const_cast<OGRGeoPackageTableLayer *>(this)->GetFIDColumn());
5243 1 : osSQL += "\" = ";
5244 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5245 1 : osSQL += " AND \"";
5246 1 : osSQL += SQLEscapeName(pszC);
5247 1 : osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
5248 1 : osSQL += SQLEscapeName(pszC);
5249 1 : osSQL += "\")";
5250 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1)
5251 : {
5252 1 : osSQL = "SELECT 1 FROM \"";
5253 1 : osSQL += SQLEscapeName(m_osRTreeName);
5254 1 : osSQL += "\" WHERE id = ";
5255 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5256 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0)
5257 : {
5258 1 : CPLError(CE_Warning, CPLE_AppDefined,
5259 : "Spatial index (perhaps created with GDAL 3.6.0) "
5260 : "of table %s is corrupted. Disabling its use. "
5261 : "This file should be recreated or its spatial "
5262 : "index recreated",
5263 1 : m_pszTableName);
5264 1 : m_bHasSpatialIndex = false;
5265 : }
5266 : }
5267 : }
5268 : }
5269 :
5270 533 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5271 : }
5272 :
5273 : /************************************************************************/
5274 : /* DropSpatialIndex() */
5275 : /************************************************************************/
5276 :
5277 43 : bool OGRGeoPackageTableLayer::DropSpatialIndex(bool bCalledFromSQLFunction)
5278 : {
5279 43 : if (!m_bFeatureDefnCompleted)
5280 0 : GetLayerDefn();
5281 43 : if (!CheckUpdatableTable("DropSpatialIndex"))
5282 1 : return false;
5283 :
5284 42 : if (m_bDropRTreeTable)
5285 : {
5286 0 : CPLError(CE_Failure, CPLE_AppDefined,
5287 : "Cannot run DropSpatialIndex() after non-completed deferred "
5288 : "DropSpatialIndex()");
5289 0 : return false;
5290 : }
5291 :
5292 42 : if (!HasSpatialIndex())
5293 : {
5294 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index not existing");
5295 1 : return false;
5296 : }
5297 :
5298 41 : const char *pszT = m_pszTableName;
5299 41 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5300 : {
5301 41 : char *pszSQL = sqlite3_mprintf(
5302 : "DELETE FROM gpkg_extensions WHERE lower(table_name)=lower('%q') "
5303 : "AND lower(column_name)=lower('%q') AND "
5304 : "extension_name='gpkg_rtree_index'",
5305 : pszT, pszC);
5306 41 : SQLCommand(m_poDS->GetDB(), pszSQL);
5307 41 : sqlite3_free(pszSQL);
5308 : }
5309 :
5310 41 : if (bCalledFromSQLFunction)
5311 : {
5312 : /* We cannot drop a table from a SQLite function call, so we just */
5313 : /* memorize that we will have to delete the table later */
5314 6 : m_bDropRTreeTable = true;
5315 : }
5316 : else
5317 : {
5318 : char *pszSQL =
5319 35 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5320 35 : SQLCommand(m_poDS->GetDB(), pszSQL);
5321 35 : sqlite3_free(pszSQL);
5322 : }
5323 :
5324 41 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5325 :
5326 41 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers().c_str());
5327 :
5328 41 : m_bHasSpatialIndex = false;
5329 41 : return true;
5330 : }
5331 :
5332 : /************************************************************************/
5333 : /* RunDeferredDropRTreeTableIfNecessary() */
5334 : /************************************************************************/
5335 :
5336 1680 : bool OGRGeoPackageTableLayer::RunDeferredDropRTreeTableIfNecessary()
5337 : {
5338 1680 : bool ret = true;
5339 1680 : if (m_bDropRTreeTable)
5340 : {
5341 6 : OGRGeoPackageTableLayer::ResetReading();
5342 :
5343 : char *pszSQL =
5344 6 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5345 6 : ret = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
5346 6 : sqlite3_free(pszSQL);
5347 6 : m_bDropRTreeTable = false;
5348 : }
5349 1680 : return ret;
5350 : }
5351 :
5352 : /************************************************************************/
5353 : /* ReturnSQLDropSpatialIndexTriggers() */
5354 : /************************************************************************/
5355 :
5356 63 : CPLString OGRGeoPackageTableLayer::ReturnSQLDropSpatialIndexTriggers()
5357 : {
5358 63 : char *pszSQL = sqlite3_mprintf(
5359 : "DROP TRIGGER \"%w_insert\";"
5360 : "DROP TRIGGER IF EXISTS \"%w_update1\";" // replaced by update6 and update7 in GPKG 1.4
5361 : "DROP TRIGGER \"%w_update2\";"
5362 : "DROP TRIGGER IF EXISTS \"%w_update3\";" // replace by update5 in GPKG 1.4
5363 : "DROP TRIGGER \"%w_update4\";"
5364 : "DROP TRIGGER IF EXISTS \"%w_update5\";" // replace update3 in GPKG 1.4
5365 : "DROP TRIGGER IF EXISTS \"%w_update6\";" // replace update1 in GPKG 1.4
5366 : "DROP TRIGGER IF EXISTS \"%w_update7\";" // replace update1 in GPKG 1.4
5367 : "DROP TRIGGER \"%w_delete\";",
5368 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5369 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5370 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str());
5371 63 : CPLString osSQL(pszSQL);
5372 63 : sqlite3_free(pszSQL);
5373 :
5374 63 : return osSQL;
5375 : }
5376 :
5377 : /************************************************************************/
5378 : /* Rename() */
5379 : /************************************************************************/
5380 :
5381 12 : OGRErr OGRGeoPackageTableLayer::Rename(const char *pszDstTableName)
5382 : {
5383 12 : if (!m_bFeatureDefnCompleted)
5384 2 : GetLayerDefn();
5385 12 : if (!CheckUpdatableTable("Rename"))
5386 0 : return OGRERR_FAILURE;
5387 :
5388 12 : ResetReading();
5389 12 : SyncToDisk();
5390 :
5391 12 : char *pszSQL = sqlite3_mprintf(
5392 : "SELECT 1 FROM sqlite_master WHERE lower(name) = lower('%q') "
5393 : "AND type IN ('table', 'view')",
5394 : pszDstTableName);
5395 : const bool bAlreadyExists =
5396 12 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5397 12 : sqlite3_free(pszSQL);
5398 12 : if (bAlreadyExists)
5399 : {
5400 3 : CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
5401 : pszDstTableName);
5402 3 : return OGRERR_FAILURE;
5403 : }
5404 :
5405 : // Temporary remove foreign key checks
5406 : const GPKGTemporaryForeignKeyCheckDisabler
5407 18 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
5408 :
5409 9 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
5410 : {
5411 0 : return OGRERR_FAILURE;
5412 : }
5413 :
5414 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5415 9 : DisableFeatureCountTriggers(false);
5416 : #endif
5417 :
5418 18 : CPLString osSQL;
5419 :
5420 9 : pszSQL = sqlite3_mprintf(
5421 : "UPDATE gpkg_geometry_columns SET table_name = '%q' WHERE "
5422 : "lower(table_name )= lower('%q');",
5423 : pszDstTableName, m_pszTableName);
5424 9 : osSQL += pszSQL;
5425 9 : sqlite3_free(pszSQL);
5426 :
5427 : // Rename the identifier if it defaulted to the table name
5428 9 : pszSQL = sqlite3_mprintf(
5429 : "UPDATE gpkg_contents SET identifier = '%q' WHERE "
5430 : "lower(table_name) = lower('%q') AND identifier = '%q';",
5431 : pszDstTableName, m_pszTableName, m_pszTableName);
5432 9 : osSQL += pszSQL;
5433 9 : sqlite3_free(pszSQL);
5434 :
5435 9 : pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET table_name = '%q' WHERE "
5436 : "lower(table_name )= lower('%q');",
5437 : pszDstTableName, m_pszTableName);
5438 9 : osSQL += pszSQL;
5439 9 : sqlite3_free(pszSQL);
5440 :
5441 9 : if (m_poDS->HasExtensionsTable())
5442 : {
5443 8 : pszSQL = sqlite3_mprintf(
5444 : "UPDATE gpkg_extensions SET table_name = '%q' WHERE "
5445 : "lower(table_name )= lower('%q');",
5446 : pszDstTableName, m_pszTableName);
5447 8 : osSQL += pszSQL;
5448 8 : sqlite3_free(pszSQL);
5449 : }
5450 :
5451 9 : if (m_poDS->HasMetadataTables())
5452 : {
5453 4 : pszSQL = sqlite3_mprintf(
5454 : "UPDATE gpkg_metadata_reference SET table_name = '%q' WHERE "
5455 : "lower(table_name )= lower('%q');",
5456 : pszDstTableName, m_pszTableName);
5457 4 : osSQL += pszSQL;
5458 4 : sqlite3_free(pszSQL);
5459 : }
5460 :
5461 9 : if (m_poDS->HasDataColumnsTable())
5462 : {
5463 1 : pszSQL = sqlite3_mprintf(
5464 : "UPDATE gpkg_data_columns SET table_name = '%q' WHERE "
5465 : "lower(table_name )= lower('%q');",
5466 : pszDstTableName, m_pszTableName);
5467 1 : osSQL += pszSQL;
5468 1 : sqlite3_free(pszSQL);
5469 : }
5470 :
5471 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5472 9 : if (m_poDS->m_bHasGPKGOGRContents)
5473 : {
5474 9 : pszSQL = sqlite3_mprintf(
5475 : "UPDATE gpkg_ogr_contents SET table_name = '%q' WHERE "
5476 : "lower(table_name )= lower('%q');",
5477 : pszDstTableName, m_pszTableName);
5478 9 : osSQL += pszSQL;
5479 9 : sqlite3_free(pszSQL);
5480 : }
5481 : #endif
5482 :
5483 9 : if (m_poDS->HasGpkgextRelationsTable())
5484 : {
5485 2 : pszSQL = sqlite3_mprintf(
5486 : "UPDATE gpkgext_relations SET base_table_name = '%q' WHERE "
5487 : "lower(base_table_name )= lower('%q');",
5488 : pszDstTableName, m_pszTableName);
5489 2 : osSQL += pszSQL;
5490 2 : sqlite3_free(pszSQL);
5491 :
5492 2 : pszSQL = sqlite3_mprintf(
5493 : "UPDATE gpkgext_relations SET related_table_name = '%q' WHERE "
5494 : "lower(related_table_name )= lower('%q');",
5495 : pszDstTableName, m_pszTableName);
5496 2 : osSQL += pszSQL;
5497 : ;
5498 2 : sqlite3_free(pszSQL);
5499 :
5500 2 : pszSQL = sqlite3_mprintf(
5501 : "UPDATE gpkgext_relations SET mapping_table_name = '%q' WHERE "
5502 : "lower(mapping_table_name )= lower('%q');",
5503 : pszDstTableName, m_pszTableName);
5504 2 : osSQL += pszSQL;
5505 2 : sqlite3_free(pszSQL);
5506 : }
5507 :
5508 9 : if (m_poDS->HasQGISLayerStyles())
5509 : {
5510 : pszSQL =
5511 1 : sqlite3_mprintf("UPDATE layer_styles SET f_table_name = '%q' WHERE "
5512 : "f_table_name = '%q';",
5513 : pszDstTableName, m_pszTableName);
5514 1 : osSQL += pszSQL;
5515 1 : sqlite3_free(pszSQL);
5516 : }
5517 :
5518 9 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5519 : m_pszTableName, pszDstTableName);
5520 9 : osSQL += pszSQL;
5521 9 : sqlite3_free(pszSQL);
5522 :
5523 9 : const bool bHasSpatialIndex = HasSpatialIndex();
5524 9 : CPLString osRTreeNameNew;
5525 9 : if (bHasSpatialIndex)
5526 : {
5527 8 : osRTreeNameNew = "rtree_";
5528 8 : osRTreeNameNew += pszDstTableName;
5529 8 : osRTreeNameNew += "_";
5530 8 : osRTreeNameNew += m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5531 :
5532 8 : osSQL += ReturnSQLDropSpatialIndexTriggers();
5533 8 : osSQL += ';';
5534 :
5535 8 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5536 : m_osRTreeName.c_str(), osRTreeNameNew.c_str());
5537 8 : osSQL += pszSQL;
5538 8 : sqlite3_free(pszSQL);
5539 :
5540 8 : osSQL += ReturnSQLCreateSpatialIndexTriggers(pszDstTableName, nullptr);
5541 : }
5542 :
5543 9 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL);
5544 :
5545 : // Check foreign key integrity
5546 9 : if (eErr == OGRERR_NONE)
5547 : {
5548 9 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
5549 : }
5550 :
5551 9 : if (eErr == OGRERR_NONE)
5552 : {
5553 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5554 9 : CreateFeatureCountTriggers(pszDstTableName);
5555 : #endif
5556 :
5557 9 : eErr = m_poDS->SoftCommitTransaction();
5558 9 : if (eErr == OGRERR_NONE)
5559 : {
5560 9 : m_poDS->RemoveTableFromSQLiteMasterCache(m_pszTableName);
5561 :
5562 9 : CPLFree(m_pszTableName);
5563 9 : m_pszTableName = CPLStrdup(pszDstTableName);
5564 :
5565 9 : if (bHasSpatialIndex)
5566 : {
5567 8 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5568 8 : m_osRTreeName = std::move(osRTreeNameNew);
5569 : }
5570 : }
5571 : }
5572 : else
5573 : {
5574 0 : m_poDS->SoftRollbackTransaction();
5575 : }
5576 :
5577 9 : if (eErr == OGRERR_NONE)
5578 : {
5579 9 : m_poDS->ClearCachedRelationships();
5580 :
5581 9 : SetDescription(pszDstTableName);
5582 9 : whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
5583 : }
5584 :
5585 9 : return eErr;
5586 : }
5587 :
5588 : /************************************************************************/
5589 : /* ISetSpatialFilter() */
5590 : /************************************************************************/
5591 :
5592 23181 : OGRErr OGRGeoPackageTableLayer::ISetSpatialFilter(int /*iGeomField*/,
5593 : const OGRGeometry *poGeomIn)
5594 :
5595 : {
5596 23181 : if (!m_bFeatureDefnCompleted)
5597 0 : GetLayerDefn();
5598 23181 : if (InstallFilter(poGeomIn))
5599 : {
5600 23147 : BuildWhere();
5601 :
5602 23147 : ResetReading();
5603 : }
5604 23181 : return OGRERR_NONE;
5605 : }
5606 :
5607 : /************************************************************************/
5608 : /* HasFastSpatialFilter() */
5609 : /************************************************************************/
5610 :
5611 2 : bool OGRGeoPackageTableLayer::HasFastSpatialFilter(int m_iGeomColIn)
5612 : {
5613 4 : if (m_iGeomColIn < 0 ||
5614 2 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5615 0 : return false;
5616 2 : return HasSpatialIndex();
5617 : }
5618 :
5619 : /************************************************************************/
5620 : /* GetSpatialWhere() */
5621 : /************************************************************************/
5622 :
5623 23256 : CPLString OGRGeoPackageTableLayer::GetSpatialWhere(int m_iGeomColIn,
5624 : OGRGeometry *poFilterGeom)
5625 : {
5626 23256 : CPLString osSpatialWHERE;
5627 :
5628 46512 : if (m_iGeomColIn < 0 ||
5629 23256 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5630 2 : return osSpatialWHERE;
5631 :
5632 23254 : if (poFilterGeom != nullptr)
5633 : {
5634 23161 : OGREnvelope sEnvelope;
5635 :
5636 23161 : poFilterGeom->getEnvelope(&sEnvelope);
5637 :
5638 : const char *pszC =
5639 23161 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomColIn)->GetNameRef();
5640 :
5641 23169 : if (std::isinf(sEnvelope.MinX) && sEnvelope.MinX < 0 &&
5642 12 : std::isinf(sEnvelope.MinY) && sEnvelope.MinY < 0 &&
5643 12 : std::isinf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 &&
5644 23169 : std::isinf(sEnvelope.MaxY) && sEnvelope.MaxY > 0)
5645 : {
5646 : osSpatialWHERE.Printf(
5647 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5648 4 : SQLEscapeName(pszC).c_str(), SQLEscapeName(pszC).c_str());
5649 149 : return osSpatialWHERE;
5650 : }
5651 :
5652 23157 : bool bUseSpatialIndex = true;
5653 46296 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
5654 222 : sEnvelope.MinY <= m_poExtent->MinY &&
5655 46448 : sEnvelope.MaxX >= m_poExtent->MaxX &&
5656 152 : sEnvelope.MaxY >= m_poExtent->MaxY)
5657 : {
5658 : // Selecting from spatial filter on whole extent can be rather
5659 : // slow. So use function based filtering, just in case the
5660 : // advertized global extent might be wrong. Otherwise we might
5661 : // just discard completely the spatial filter.
5662 149 : bUseSpatialIndex = false;
5663 : }
5664 :
5665 23157 : if (bUseSpatialIndex && HasSpatialIndex())
5666 : {
5667 : osSpatialWHERE.Printf(
5668 : "\"%s\" IN ( SELECT id FROM \"%s\" WHERE "
5669 : "maxx >= %.12f AND minx <= %.12f AND "
5670 : "maxy >= %.12f AND miny <= %.12f)",
5671 46010 : SQLEscapeName(m_osFIDForRTree).c_str(),
5672 46010 : SQLEscapeName(m_osRTreeName).c_str(), sEnvelope.MinX - 1e-11,
5673 23005 : sEnvelope.MaxX + 1e-11, sEnvelope.MinY - 1e-11,
5674 69015 : sEnvelope.MaxY + 1e-11);
5675 : }
5676 : else
5677 : {
5678 152 : if (HasSpatialIndex())
5679 : {
5680 : // If we do have a spatial index, and our filter contains the
5681 : // bounding box of the RTree, then just filter on non-null
5682 : // non-empty geometries.
5683 : double minx, miny, maxx, maxy;
5684 146 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, minx,
5685 146 : miny, maxx, maxy) &&
5686 146 : sEnvelope.MinX <= minx && sEnvelope.MinY <= miny &&
5687 292 : sEnvelope.MaxX >= maxx && sEnvelope.MaxY >= maxy)
5688 : {
5689 : osSpatialWHERE.Printf(
5690 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5691 290 : SQLEscapeName(pszC).c_str(),
5692 435 : SQLEscapeName(pszC).c_str());
5693 145 : return osSpatialWHERE;
5694 : }
5695 : }
5696 :
5697 : /* A bit inefficient but still faster than OGR filtering */
5698 : osSpatialWHERE.Printf(
5699 : "ST_EnvelopesIntersects(\"%s\", %.12f, %.12f, %.12f, %.12f)",
5700 7 : SQLEscapeName(pszC).c_str(), sEnvelope.MinX - 1e-11,
5701 7 : sEnvelope.MinY - 1e-11, sEnvelope.MaxX + 1e-11,
5702 7 : sEnvelope.MaxY + 1e-11);
5703 : }
5704 : }
5705 :
5706 23105 : return osSpatialWHERE;
5707 : }
5708 :
5709 : /************************************************************************/
5710 : /* BuildWhere() */
5711 : /* */
5712 : /* Build the WHERE statement appropriate to the current set of */
5713 : /* criteria (spatial and attribute queries). */
5714 : /************************************************************************/
5715 :
5716 23236 : void OGRGeoPackageTableLayer::BuildWhere()
5717 :
5718 : {
5719 23236 : m_soFilter = "";
5720 :
5721 : CPLString osSpatialWHERE =
5722 46472 : GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
5723 23236 : if (!osSpatialWHERE.empty())
5724 : {
5725 23141 : m_soFilter += osSpatialWHERE;
5726 : }
5727 :
5728 23236 : if (!osQuery.empty())
5729 : {
5730 46 : if (m_soFilter.empty())
5731 : {
5732 40 : m_soFilter += osQuery;
5733 : }
5734 : else
5735 : {
5736 6 : m_soFilter += " AND (";
5737 6 : m_soFilter += osQuery;
5738 6 : m_soFilter += ")";
5739 : }
5740 : }
5741 23236 : CPLDebug("GPKG", "Filter: %s", m_soFilter.c_str());
5742 23236 : }
5743 :
5744 : /************************************************************************/
5745 : /* SetOpeningParameters() */
5746 : /************************************************************************/
5747 :
5748 3402 : void OGRGeoPackageTableLayer::SetOpeningParameters(
5749 : const char *pszTableName, const char *pszObjectType, bool bIsInGpkgContents,
5750 : bool bIsSpatial, const char *pszGeomColName, const char *pszGeomType,
5751 : bool bHasZ, bool bHasM)
5752 : {
5753 3402 : CPLFree(m_pszTableName);
5754 3402 : m_pszTableName = CPLStrdup(pszTableName);
5755 3402 : m_bIsTable = EQUAL(pszObjectType, "table");
5756 3402 : m_bIsInGpkgContents = bIsInGpkgContents;
5757 3402 : m_bIsSpatial = bIsSpatial;
5758 3402 : if (pszGeomType)
5759 : {
5760 : OGRwkbGeometryType eType =
5761 1277 : GPkgGeometryTypeToWKB(pszGeomType, bHasZ, bHasM);
5762 1277 : m_poFeatureDefn->SetGeomType(eType);
5763 1277 : if (eType != wkbNone)
5764 : {
5765 1277 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
5766 : }
5767 : }
5768 3402 : }
5769 :
5770 : /************************************************************************/
5771 : /* SetCreationParameters() */
5772 : /************************************************************************/
5773 :
5774 1030 : void OGRGeoPackageTableLayer::SetCreationParameters(
5775 : OGRwkbGeometryType eGType, const char *pszGeomColumnName, int bGeomNullable,
5776 : const OGRSpatialReference *poSRS, const char *pszSRID,
5777 : const OGRGeomCoordinatePrecision &oCoordPrec, bool bDiscardCoordLSB,
5778 : bool bUndoDiscardCoordLSBOnReading, const char *pszFIDColumnName,
5779 : const char *pszIdentifier, const char *pszDescription)
5780 : {
5781 1030 : m_bIsSpatial = eGType != wkbNone;
5782 1030 : m_bIsInGpkgContents =
5783 1112 : m_bIsSpatial ||
5784 82 : !m_poDS->HasNonSpatialTablesNonRegisteredInGpkgContents();
5785 1030 : m_bFeatureDefnCompleted = true;
5786 1030 : m_bDeferredCreation = true;
5787 1030 : m_bTableCreatedInTransaction = m_poDS->IsInTransaction();
5788 1030 : m_bHasTriedDetectingFID64 = true;
5789 1030 : m_pszFidColumn = CPLStrdup(pszFIDColumnName);
5790 1030 : m_bUndoDiscardCoordLSBOnReading = bUndoDiscardCoordLSBOnReading;
5791 :
5792 1030 : if (eGType != wkbNone)
5793 : {
5794 948 : m_nZFlag = wkbHasZ(eGType) ? 1 : 0;
5795 948 : m_nMFlag = wkbHasM(eGType) ? 1 : 0;
5796 :
5797 1896 : OGRSpatialReferenceRefCountedPtr poGotSRS;
5798 1896 : OGRGeomFieldDefn oGeomFieldDefn(pszGeomColumnName, eGType);
5799 :
5800 948 : oGeomFieldDefn.SetSpatialRef(poSRS);
5801 948 : if (pszSRID)
5802 : {
5803 10 : m_iSrs = atoi(pszSRID);
5804 10 : if (m_iSrs == GDALGeoPackageDataset::FIRST_CUSTOM_SRSID - 1)
5805 : {
5806 1 : m_iSrs = m_poDS->GetSrsId(nullptr);
5807 1 : oGeomFieldDefn.SetSpatialRef(nullptr);
5808 : }
5809 : else
5810 : {
5811 : poGotSRS =
5812 18 : m_poDS->GetSpatialRef(m_iSrs, /* bFallbackToEPSG = */ false,
5813 9 : /* bEmitErrorIfNotFound = */ false);
5814 9 : if (poGotSRS)
5815 : {
5816 6 : oGeomFieldDefn.SetSpatialRef(poGotSRS.get());
5817 : }
5818 : else
5819 : {
5820 3 : bool bOK = false;
5821 : auto poSRSTmp =
5822 6 : OGRSpatialReferenceRefCountedPtr::makeInstance();
5823 3 : if (m_iSrs < 32767)
5824 : {
5825 : CPLErrorHandlerPusher oErrorHandler(
5826 4 : CPLQuietErrorHandler);
5827 4 : CPLErrorStateBackuper oBackuper;
5828 2 : if (poSRSTmp->importFromEPSG(m_iSrs) == OGRERR_NONE)
5829 : {
5830 1 : bOK = true;
5831 1 : poSRSTmp->SetAxisMappingStrategy(
5832 : OAMS_TRADITIONAL_GIS_ORDER);
5833 1 : m_iSrs = m_poDS->GetSrsId(poSRSTmp.get());
5834 1 : oGeomFieldDefn.SetSpatialRef(poSRSTmp.get());
5835 : }
5836 : }
5837 3 : if (!bOK)
5838 : {
5839 2 : CPLError(
5840 : CE_Warning, CPLE_AppDefined,
5841 : "No entry in gpkg_spatial_ref_sys matching SRID=%s",
5842 : pszSRID);
5843 : }
5844 : }
5845 : }
5846 : }
5847 : else
5848 : {
5849 938 : m_iSrs = m_poDS->GetSrsId(poSRS);
5850 : }
5851 948 : oGeomFieldDefn.SetNullable(bGeomNullable);
5852 948 : oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
5853 :
5854 948 : if (bDiscardCoordLSB)
5855 2 : m_sBinaryPrecision.SetFrom(oCoordPrec);
5856 :
5857 : // Save coordinate precision in gpkg_metadata/gpkg_metadata_reference
5858 2834 : if ((oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5859 938 : oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5860 1896 : oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN) &&
5861 10 : (m_poDS->HasMetadataTables() || m_poDS->CreateMetadataTables()))
5862 : {
5863 20 : std::string osCoordPrecision = "<CoordinatePrecision ";
5864 10 : if (oCoordPrec.dfXYResolution !=
5865 : OGRGeomCoordinatePrecision::UNKNOWN)
5866 : osCoordPrecision += CPLSPrintf(" xy_resolution=\"%g\"",
5867 10 : oCoordPrec.dfXYResolution);
5868 10 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5869 : osCoordPrecision += CPLSPrintf(" z_resolution=\"%g\"",
5870 7 : oCoordPrec.dfZResolution);
5871 10 : if (oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5872 : osCoordPrecision += CPLSPrintf(" m_resolution=\"%g\"",
5873 7 : oCoordPrec.dfMResolution);
5874 : osCoordPrecision += CPLSPrintf(" discard_coord_lsb=\"%s\"",
5875 10 : bDiscardCoordLSB ? "true" : "false");
5876 : osCoordPrecision +=
5877 : CPLSPrintf(" undo_discard_coord_lsb_on_reading=\"%s\"",
5878 10 : m_bUndoDiscardCoordLSBOnReading ? "true" : "false");
5879 10 : osCoordPrecision += " />";
5880 :
5881 10 : char *pszSQL = sqlite3_mprintf(
5882 : "INSERT INTO gpkg_metadata "
5883 : "(md_scope, md_standard_uri, mime_type, metadata) VALUES "
5884 : "('dataset','http://gdal.org','text/xml','%q')",
5885 : osCoordPrecision.c_str());
5886 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5887 10 : sqlite3_free(pszSQL);
5888 :
5889 : const sqlite_int64 nFID =
5890 10 : sqlite3_last_insert_rowid(m_poDS->GetDB());
5891 10 : pszSQL = sqlite3_mprintf(
5892 : "INSERT INTO gpkg_metadata_reference (reference_scope, "
5893 : "table_name, column_name, timestamp, md_file_id) VALUES "
5894 : "('column', '%q', '%q', %s, %d)",
5895 : m_pszTableName, pszGeomColumnName,
5896 20 : m_poDS->GetCurrentDateEscapedSQL().c_str(),
5897 : static_cast<int>(nFID));
5898 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5899 10 : sqlite3_free(pszSQL);
5900 : }
5901 :
5902 948 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
5903 : }
5904 1030 : if (pszIdentifier)
5905 : {
5906 3 : m_osIdentifierLCO = pszIdentifier;
5907 3 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
5908 : }
5909 1030 : if (pszDescription)
5910 : {
5911 5 : m_osDescriptionLCO = pszDescription;
5912 5 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
5913 : }
5914 :
5915 1030 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
5916 1030 : }
5917 :
5918 : /************************************************************************/
5919 : /* RegisterGeometryColumn() */
5920 : /************************************************************************/
5921 :
5922 951 : OGRErr OGRGeoPackageTableLayer::RegisterGeometryColumn()
5923 : {
5924 951 : OGRwkbGeometryType eGType = GetGeomType();
5925 951 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5926 : /* Requirement 27: The z value in a gpkg_geometry_columns table row */
5927 : /* SHALL be one of 0 (none), 1 (mandatory), or 2 (optional) */
5928 :
5929 : /* Update gpkg_geometry_columns with the table info */
5930 : char *pszSQL =
5931 951 : sqlite3_mprintf("INSERT INTO gpkg_geometry_columns "
5932 : "(table_name,column_name,geometry_type_name,srs_id,z,m)"
5933 : " VALUES "
5934 : "('%q','%q','%q',%d,%d,%d)",
5935 : GetName(), GetGeometryColumn(), pszGeometryType, m_iSrs,
5936 : m_nZFlag, m_nMFlag);
5937 :
5938 951 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5939 951 : sqlite3_free(pszSQL);
5940 951 : if (err != OGRERR_NONE)
5941 0 : return OGRERR_FAILURE;
5942 :
5943 951 : if (wkbFlatten(eGType) > wkbGeometryCollection)
5944 : {
5945 6 : CreateGeometryExtensionIfNecessary(eGType);
5946 : }
5947 :
5948 951 : return OGRERR_NONE;
5949 : }
5950 :
5951 : /************************************************************************/
5952 : /* GetColumnsOfCreateTable() */
5953 : /************************************************************************/
5954 :
5955 1058 : CPLString OGRGeoPackageTableLayer::GetColumnsOfCreateTable(
5956 : const std::vector<OGRFieldDefn *> &apoFields)
5957 : {
5958 1058 : CPLString osSQL;
5959 :
5960 1058 : char *pszSQL = nullptr;
5961 1058 : bool bNeedComma = false;
5962 1058 : if (m_pszFidColumn != nullptr)
5963 : {
5964 : pszSQL =
5965 1058 : sqlite3_mprintf("\"%w\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
5966 : m_pszFidColumn);
5967 1058 : osSQL += pszSQL;
5968 1058 : sqlite3_free(pszSQL);
5969 1058 : bNeedComma = true;
5970 : }
5971 :
5972 1058 : const OGRwkbGeometryType eGType = GetGeomType();
5973 1058 : if (eGType != wkbNone)
5974 : {
5975 977 : if (bNeedComma)
5976 : {
5977 977 : osSQL += ", ";
5978 : }
5979 977 : bNeedComma = true;
5980 :
5981 : /* Requirement 25: The geometry_type_name value in a
5982 : * gpkg_geometry_columns */
5983 : /* row SHALL be one of the uppercase geometry type names specified in */
5984 : /* Geometry Types (Normative). */
5985 977 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5986 :
5987 : pszSQL =
5988 977 : sqlite3_mprintf("\"%w\" %s", GetGeometryColumn(), pszGeometryType);
5989 977 : osSQL += pszSQL;
5990 977 : sqlite3_free(pszSQL);
5991 977 : if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable())
5992 : {
5993 2 : osSQL += " NOT NULL";
5994 : }
5995 : }
5996 :
5997 5609 : for (size_t i = 0; i < apoFields.size(); i++)
5998 : {
5999 4551 : OGRFieldDefn *poFieldDefn = apoFields[i];
6000 : // Eg. when a geometry type is specified + an sql statement returns no
6001 : // or NULL geometry values, the geom column is incorrectly treated as
6002 : // an attribute column as well with the same name. Not ideal, but skip
6003 : // this column here to avoid duplicate column name error. Issue: #6976.
6004 8948 : if ((eGType != wkbNone) &&
6005 4397 : (EQUAL(poFieldDefn->GetNameRef(), GetGeometryColumn())))
6006 : {
6007 0 : continue;
6008 : }
6009 4551 : if (bNeedComma)
6010 : {
6011 4551 : osSQL += ", ";
6012 : }
6013 4551 : bNeedComma = true;
6014 :
6015 4551 : pszSQL = sqlite3_mprintf("\"%w\" %s", poFieldDefn->GetNameRef(),
6016 : GPkgFieldFromOGR(poFieldDefn->GetType(),
6017 : poFieldDefn->GetSubType(),
6018 : poFieldDefn->GetWidth()));
6019 4551 : osSQL += pszSQL;
6020 4551 : sqlite3_free(pszSQL);
6021 4551 : if (!poFieldDefn->IsNullable())
6022 : {
6023 15 : osSQL += " NOT NULL";
6024 : }
6025 4551 : if (poFieldDefn->IsUnique())
6026 : {
6027 18 : osSQL += " UNIQUE";
6028 : }
6029 4551 : const char *pszDefault = poFieldDefn->GetDefault();
6030 4566 : if (pszDefault != nullptr &&
6031 15 : (!poFieldDefn->IsDefaultDriverSpecific() ||
6032 1 : (pszDefault[0] == '(' &&
6033 1 : pszDefault[strlen(pszDefault) - 1] == ')' &&
6034 1 : (STARTS_WITH_CI(pszDefault + 1, "strftime") ||
6035 0 : STARTS_WITH_CI(pszDefault + 1, " strftime")))))
6036 : {
6037 15 : osSQL += " DEFAULT ";
6038 : OGRField sField;
6039 19 : if (poFieldDefn->GetType() == OFTDateTime &&
6040 4 : OGRParseDate(pszDefault, &sField, 0))
6041 : {
6042 : char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
6043 0 : OGRGetISO8601DateTime(&sField, false, szBuffer);
6044 0 : osSQL += szBuffer;
6045 : }
6046 : /* Make sure CURRENT_TIMESTAMP is translated into appropriate format
6047 : * for GeoPackage */
6048 19 : else if (poFieldDefn->GetType() == OFTDateTime &&
6049 4 : EQUAL(pszDefault, "CURRENT_TIMESTAMP"))
6050 : {
6051 1 : osSQL += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
6052 : }
6053 : else
6054 : {
6055 14 : osSQL += poFieldDefn->GetDefault();
6056 : }
6057 : }
6058 : }
6059 :
6060 1058 : return osSQL;
6061 : }
6062 :
6063 : /************************************************************************/
6064 : /* RunDeferredCreationIfNecessary() */
6065 : /************************************************************************/
6066 :
6067 6005 : OGRErr OGRGeoPackageTableLayer::RunDeferredCreationIfNecessary()
6068 : {
6069 6005 : if (!m_bDeferredCreation)
6070 4975 : return OGRERR_NONE;
6071 1030 : m_bDeferredCreation = false;
6072 :
6073 1030 : const char *pszLayerName = m_poFeatureDefn->GetName();
6074 :
6075 : /* Create the table! */
6076 2060 : CPLString osCommand;
6077 :
6078 1030 : char *pszSQL = sqlite3_mprintf("CREATE TABLE \"%w\" ( ", pszLayerName);
6079 1030 : osCommand += pszSQL;
6080 1030 : sqlite3_free(pszSQL);
6081 :
6082 2060 : std::vector<OGRFieldDefn *> apoFields;
6083 5520 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
6084 : {
6085 4490 : if (i == m_iFIDAsRegularColumnIndex)
6086 4 : continue;
6087 4486 : apoFields.push_back(m_poFeatureDefn->GetFieldDefn(i));
6088 : }
6089 :
6090 1030 : osCommand += GetColumnsOfCreateTable(apoFields);
6091 :
6092 1030 : osCommand += ")";
6093 :
6094 : #ifdef DEBUG
6095 1030 : CPLDebug("GPKG", "exec(%s)", osCommand.c_str());
6096 : #endif
6097 1030 : OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
6098 1030 : if (OGRERR_NONE != err)
6099 0 : return OGRERR_FAILURE;
6100 :
6101 5516 : for (auto &poField : apoFields)
6102 : {
6103 4486 : if (!DoSpecialProcessingForColumnCreation(poField))
6104 : {
6105 0 : return OGRERR_FAILURE;
6106 : }
6107 : }
6108 :
6109 : /* Update gpkg_contents with the table info */
6110 1030 : const OGRwkbGeometryType eGType = GetGeomType();
6111 1030 : const bool bIsSpatial = (eGType != wkbNone);
6112 :
6113 1030 : if (bIsSpatial || m_eASpatialVariant == GPKG_ATTRIBUTES)
6114 : {
6115 1018 : const char *pszIdentifier = GetMetadataItem("IDENTIFIER");
6116 1018 : if (pszIdentifier == nullptr)
6117 1015 : pszIdentifier = pszLayerName;
6118 1018 : const char *pszDescription = GetMetadataItem("DESCRIPTION");
6119 1018 : if (pszDescription == nullptr)
6120 1013 : pszDescription = "";
6121 :
6122 1018 : pszSQL = sqlite3_mprintf(
6123 : "INSERT INTO gpkg_contents "
6124 : "(table_name,data_type,identifier,description,last_change,srs_id) "
6125 : "VALUES "
6126 : "('%q','%q','%q','%q',%s,%d)",
6127 : pszLayerName, (bIsSpatial ? "features" : "attributes"),
6128 : pszIdentifier, pszDescription,
6129 2036 : GDALGeoPackageDataset::GetCurrentDateEscapedSQL().c_str(), m_iSrs);
6130 :
6131 1018 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6132 1018 : sqlite3_free(pszSQL);
6133 1018 : if (err != OGRERR_NONE)
6134 0 : return OGRERR_FAILURE;
6135 : }
6136 :
6137 1030 : if (bIsSpatial)
6138 : {
6139 : // Insert into gpkg_geometry_columns after gpkg_contents because of
6140 : // foreign key constraints
6141 949 : err = RegisterGeometryColumn();
6142 949 : if (err != OGRERR_NONE)
6143 0 : return OGRERR_FAILURE;
6144 : }
6145 :
6146 : #ifdef ENABLE_GPKG_OGR_CONTENTS
6147 1030 : if (m_poDS->m_bHasGPKGOGRContents)
6148 : {
6149 1024 : pszSQL = sqlite3_mprintf("DELETE FROM gpkg_ogr_contents WHERE "
6150 : "lower(table_name) = lower('%q')",
6151 : pszLayerName);
6152 1024 : SQLCommand(m_poDS->GetDB(), pszSQL);
6153 1024 : sqlite3_free(pszSQL);
6154 :
6155 1024 : pszSQL = sqlite3_mprintf(
6156 : "INSERT INTO gpkg_ogr_contents (table_name, feature_count) "
6157 : "VALUES ('%q', 0)",
6158 : pszLayerName);
6159 1024 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6160 1024 : sqlite3_free(pszSQL);
6161 1024 : if (err == OGRERR_NONE)
6162 : {
6163 1024 : m_nTotalFeatureCount = 0;
6164 1024 : m_bAddOGRFeatureCountTriggers = true;
6165 : }
6166 : }
6167 : #endif
6168 :
6169 1030 : ResetReading();
6170 :
6171 1030 : return OGRERR_NONE;
6172 : }
6173 :
6174 : /************************************************************************/
6175 : /* GetMetadata() */
6176 : /************************************************************************/
6177 :
6178 15444 : CSLConstList OGRGeoPackageTableLayer::GetMetadata(const char *pszDomain)
6179 :
6180 : {
6181 15444 : if (!m_bFeatureDefnCompleted)
6182 362 : GetLayerDefn();
6183 15444 : if (!m_bHasTriedDetectingFID64 && m_pszFidColumn != nullptr)
6184 : {
6185 450 : m_bHasTriedDetectingFID64 = true;
6186 :
6187 : /* --------------------------------------------------------------------
6188 : */
6189 : /* Find if the FID holds 64bit values */
6190 : /* --------------------------------------------------------------------
6191 : */
6192 :
6193 : // Normally the fid should be AUTOINCREMENT, so check sqlite_sequence
6194 450 : OGRErr err = OGRERR_NONE;
6195 : char *pszSQL =
6196 450 : sqlite3_mprintf("SELECT seq FROM sqlite_sequence WHERE name = '%q'",
6197 : m_pszTableName);
6198 450 : CPLPushErrorHandler(CPLQuietErrorHandler);
6199 450 : GIntBig nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
6200 450 : CPLPopErrorHandler();
6201 450 : sqlite3_free(pszSQL);
6202 450 : if (err != OGRERR_NONE)
6203 : {
6204 47 : CPLErrorReset();
6205 :
6206 : // In case of error, fallback to taking the MAX of the FID
6207 47 : pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
6208 : m_pszFidColumn, m_pszTableName);
6209 :
6210 47 : nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, nullptr);
6211 47 : sqlite3_free(pszSQL);
6212 : }
6213 450 : if (nMaxId > INT_MAX)
6214 1 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
6215 : }
6216 :
6217 15444 : if (m_bHasReadMetadataFromStorage)
6218 13972 : return OGRLayer::GetMetadata(pszDomain);
6219 :
6220 1472 : m_bHasReadMetadataFromStorage = true;
6221 :
6222 1472 : if (!m_poDS->HasMetadataTables())
6223 1139 : return OGRLayer::GetMetadata(pszDomain);
6224 :
6225 333 : char *pszSQL = sqlite3_mprintf(
6226 : "SELECT md.metadata, md.md_standard_uri, md.mime_type, "
6227 : "mdr.reference_scope "
6228 : "FROM gpkg_metadata md "
6229 : "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
6230 : "WHERE lower(mdr.table_name) = lower('%q') ORDER BY md.id "
6231 : "LIMIT 1000", // to avoid denial of service
6232 : m_pszTableName);
6233 :
6234 666 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
6235 333 : sqlite3_free(pszSQL);
6236 333 : if (!oResult)
6237 : {
6238 0 : return OGRLayer::GetMetadata(pszDomain);
6239 : }
6240 :
6241 333 : char **papszMetadata = CSLDuplicate(OGRLayer::GetMetadata());
6242 :
6243 : /* GDAL metadata */
6244 634 : for (int i = 0; i < oResult->RowCount(); i++)
6245 : {
6246 301 : const char *pszMetadata = oResult->GetValue(0, i);
6247 301 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6248 301 : const char *pszMimeType = oResult->GetValue(2, i);
6249 301 : const char *pszReferenceScope = oResult->GetValue(3, i);
6250 301 : if (pszMetadata && pszMDStandardURI && pszMimeType &&
6251 301 : pszReferenceScope && EQUAL(pszMDStandardURI, "http://gdal.org") &&
6252 301 : EQUAL(pszMimeType, "text/xml") && EQUAL(pszReferenceScope, "table"))
6253 : {
6254 282 : CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
6255 282 : if (psXMLNode)
6256 : {
6257 564 : GDALMultiDomainMetadata oLocalMDMD;
6258 282 : oLocalMDMD.XMLInit(psXMLNode, FALSE);
6259 :
6260 : papszMetadata =
6261 282 : CSLMerge(papszMetadata, oLocalMDMD.GetMetadata());
6262 282 : CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
6263 282 : CSLConstList papszIter = papszDomainList;
6264 597 : while (papszIter && *papszIter)
6265 : {
6266 315 : if (!EQUAL(*papszIter, ""))
6267 34 : oMDMD.SetMetadata(oLocalMDMD.GetMetadata(*papszIter),
6268 : *papszIter);
6269 315 : papszIter++;
6270 : }
6271 :
6272 282 : CPLDestroyXMLNode(psXMLNode);
6273 : }
6274 : }
6275 : }
6276 :
6277 333 : OGRLayer::SetMetadata(papszMetadata);
6278 333 : CSLDestroy(papszMetadata);
6279 333 : papszMetadata = nullptr;
6280 :
6281 : /* Add non-GDAL metadata now */
6282 333 : int nNonGDALMDILocal = 1;
6283 634 : for (int i = 0; i < oResult->RowCount(); i++)
6284 : {
6285 301 : const char *pszMetadata = oResult->GetValue(0, i);
6286 301 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6287 301 : const char *pszMimeType = oResult->GetValue(2, i);
6288 : // const char* pszReferenceScope = oResult->GetValue(3, i);
6289 301 : if (pszMetadata == nullptr || pszMDStandardURI == nullptr ||
6290 : pszMimeType == nullptr
6291 : /* || pszReferenceScope == nullptr */)
6292 : {
6293 : // should not happen as there are NOT NULL constraints
6294 : // But a database could lack such NOT NULL constraints or have
6295 : // large values that would cause a memory allocation failure.
6296 0 : continue;
6297 : }
6298 : // int bIsGPKGScope = EQUAL(pszReferenceScope, "geopackage");
6299 301 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6300 301 : EQUAL(pszMimeType, "text/xml"))
6301 301 : continue;
6302 :
6303 0 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6304 0 : EQUAL(pszMimeType, "text/plain"))
6305 : {
6306 0 : if (STARTS_WITH_CI(pszMetadata, "coordinate_epoch="))
6307 : {
6308 0 : continue;
6309 : }
6310 : }
6311 :
6312 : /*if( strcmp( pszMDStandardURI, "http://www.isotc211.org/2005/gmd" ) ==
6313 : 0 && strcmp( pszMimeType, "text/xml" ) == 0 )
6314 : {
6315 : char* apszMD[2];
6316 : apszMD[0] = (char*)pszMetadata;
6317 : apszMD[1] = NULL;
6318 : oMDMD.SetMetadata(apszMD, "xml:MD_Metadata");
6319 : }
6320 : else*/
6321 : {
6322 0 : oMDMD.SetMetadataItem(
6323 : CPLSPrintf("GPKG_METADATA_ITEM_%d", nNonGDALMDILocal),
6324 : pszMetadata);
6325 0 : nNonGDALMDILocal++;
6326 : }
6327 : }
6328 :
6329 333 : return OGRLayer::GetMetadata(pszDomain);
6330 : }
6331 :
6332 : /************************************************************************/
6333 : /* GetMetadataItem() */
6334 : /************************************************************************/
6335 :
6336 13168 : const char *OGRGeoPackageTableLayer::GetMetadataItem(const char *pszName,
6337 : const char *pszDomain)
6338 : {
6339 13168 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
6340 : }
6341 :
6342 : /************************************************************************/
6343 : /* GetMetadataDomainList() */
6344 : /************************************************************************/
6345 :
6346 359 : char **OGRGeoPackageTableLayer::GetMetadataDomainList()
6347 : {
6348 359 : GetMetadata();
6349 359 : return OGRLayer::GetMetadataDomainList();
6350 : }
6351 :
6352 : /************************************************************************/
6353 : /* SetMetadata() */
6354 : /************************************************************************/
6355 :
6356 167 : CPLErr OGRGeoPackageTableLayer::SetMetadata(CSLConstList papszMetadata,
6357 : const char *pszDomain)
6358 : {
6359 167 : GetMetadata(); /* force loading from storage if needed */
6360 167 : CPLErr eErr = OGRLayer::SetMetadata(papszMetadata, pszDomain);
6361 167 : m_poDS->SetMetadataDirty();
6362 167 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
6363 : {
6364 98 : if (!m_osIdentifierLCO.empty())
6365 1 : OGRLayer::SetMetadataItem("IDENTIFIER", m_osIdentifierLCO);
6366 98 : if (!m_osDescriptionLCO.empty())
6367 1 : OGRLayer::SetMetadataItem("DESCRIPTION", m_osDescriptionLCO);
6368 : }
6369 167 : return eErr;
6370 : }
6371 :
6372 : /************************************************************************/
6373 : /* SetMetadataItem() */
6374 : /************************************************************************/
6375 :
6376 682 : CPLErr OGRGeoPackageTableLayer::SetMetadataItem(const char *pszName,
6377 : const char *pszValue,
6378 : const char *pszDomain)
6379 : {
6380 682 : GetMetadata(); /* force loading from storage if needed */
6381 683 : if (!m_osIdentifierLCO.empty() && EQUAL(pszName, "IDENTIFIER") &&
6382 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6383 1 : return CE_None;
6384 682 : if (!m_osDescriptionLCO.empty() && EQUAL(pszName, "DESCRIPTION") &&
6385 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6386 1 : return CE_None;
6387 680 : m_poDS->SetMetadataDirty();
6388 680 : return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
6389 : }
6390 :
6391 : /************************************************************************/
6392 : /* RecreateTable() */
6393 : /************************************************************************/
6394 :
6395 : OGRErr
6396 12 : OGRGeoPackageTableLayer::RecreateTable(const CPLString &osColumnsForCreate,
6397 : const CPLString &osFieldListForSelect)
6398 : {
6399 : /* -------------------------------------------------------------------- */
6400 : /* Save existing related triggers and index */
6401 : /* -------------------------------------------------------------------- */
6402 12 : sqlite3 *hDB = m_poDS->GetDB();
6403 :
6404 12 : char *pszSQL = sqlite3_mprintf(
6405 : "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') "
6406 : "AND lower(tbl_name)=lower('%q') LIMIT 10000",
6407 : m_pszTableName);
6408 12 : OGRErr eErr = OGRERR_NONE;
6409 12 : auto oTriggers = SQLQuery(hDB, pszSQL);
6410 12 : sqlite3_free(pszSQL);
6411 :
6412 : /* -------------------------------------------------------------------- */
6413 : /* Make a temporary table with new content. */
6414 : /* -------------------------------------------------------------------- */
6415 12 : if (oTriggers)
6416 : {
6417 12 : pszSQL = sqlite3_mprintf("CREATE TABLE \"%w_ogr_tmp\" (%s)",
6418 : m_pszTableName, osColumnsForCreate.c_str());
6419 12 : eErr = SQLCommand(hDB, pszSQL);
6420 12 : sqlite3_free(pszSQL);
6421 : }
6422 : else
6423 : {
6424 0 : eErr = OGRERR_FAILURE;
6425 : }
6426 :
6427 12 : if (eErr == OGRERR_NONE)
6428 : {
6429 12 : pszSQL = sqlite3_mprintf(
6430 : "INSERT INTO \"%w_ogr_tmp\" SELECT %s FROM \"%w\"", m_pszTableName,
6431 : osFieldListForSelect.c_str(), m_pszTableName);
6432 12 : eErr = SQLCommand(hDB, pszSQL);
6433 12 : sqlite3_free(pszSQL);
6434 : }
6435 :
6436 : /* -------------------------------------------------------------------- */
6437 : /* Drop the original table */
6438 : /* -------------------------------------------------------------------- */
6439 12 : if (eErr == OGRERR_NONE)
6440 : {
6441 10 : pszSQL = sqlite3_mprintf("DROP TABLE \"%w\"", m_pszTableName);
6442 10 : eErr = SQLCommand(hDB, pszSQL);
6443 10 : sqlite3_free(pszSQL);
6444 : }
6445 :
6446 : /* -------------------------------------------------------------------- */
6447 : /* Rename temporary table as new table */
6448 : /* -------------------------------------------------------------------- */
6449 12 : if (eErr == OGRERR_NONE)
6450 : {
6451 10 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w_ogr_tmp\" RENAME TO \"%w\"",
6452 : m_pszTableName, m_pszTableName);
6453 10 : eErr = SQLCommand(hDB, pszSQL);
6454 10 : sqlite3_free(pszSQL);
6455 : }
6456 :
6457 : /* -------------------------------------------------------------------- */
6458 : /* Recreate existing related tables, triggers and index */
6459 : /* -------------------------------------------------------------------- */
6460 89 : for (int i = 0;
6461 89 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
6462 : {
6463 77 : const char *pszSQLTriggerIdx = oTriggers->GetValue(0, i);
6464 77 : if (pszSQLTriggerIdx != nullptr && *pszSQLTriggerIdx != '\0')
6465 : {
6466 76 : eErr = SQLCommand(hDB, pszSQLTriggerIdx);
6467 : }
6468 : }
6469 :
6470 24 : return eErr;
6471 : }
6472 :
6473 : /************************************************************************/
6474 : /* BuildSelectFieldList() */
6475 : /************************************************************************/
6476 :
6477 12 : CPLString OGRGeoPackageTableLayer::BuildSelectFieldList(
6478 : const std::vector<OGRFieldDefn *> &apoFields)
6479 : {
6480 12 : CPLString osFieldListForSelect;
6481 :
6482 12 : char *pszSQL = nullptr;
6483 12 : bool bNeedComma = false;
6484 :
6485 12 : if (m_pszFidColumn != nullptr)
6486 : {
6487 12 : pszSQL = sqlite3_mprintf("\"%w\"", m_pszFidColumn);
6488 12 : osFieldListForSelect += pszSQL;
6489 12 : sqlite3_free(pszSQL);
6490 12 : bNeedComma = true;
6491 : }
6492 :
6493 12 : if (GetGeomType() != wkbNone)
6494 : {
6495 12 : if (bNeedComma)
6496 : {
6497 12 : osFieldListForSelect += ", ";
6498 : }
6499 12 : bNeedComma = true;
6500 :
6501 12 : pszSQL = sqlite3_mprintf("\"%w\"", GetGeometryColumn());
6502 12 : osFieldListForSelect += pszSQL;
6503 12 : sqlite3_free(pszSQL);
6504 : }
6505 :
6506 33 : for (size_t iField = 0; iField < apoFields.size(); iField++)
6507 : {
6508 21 : if (bNeedComma)
6509 : {
6510 21 : osFieldListForSelect += ", ";
6511 : }
6512 21 : bNeedComma = true;
6513 :
6514 21 : OGRFieldDefn *poFieldDefn = apoFields[iField];
6515 21 : pszSQL = sqlite3_mprintf("\"%w\"", poFieldDefn->GetNameRef());
6516 21 : osFieldListForSelect += pszSQL;
6517 21 : sqlite3_free(pszSQL);
6518 : }
6519 :
6520 12 : return osFieldListForSelect;
6521 : }
6522 :
6523 : /************************************************************************/
6524 : /* DeleteField() */
6525 : /************************************************************************/
6526 :
6527 163 : OGRErr OGRGeoPackageTableLayer::DeleteField(int iFieldToDelete)
6528 : {
6529 163 : if (!m_bFeatureDefnCompleted)
6530 3 : GetLayerDefn();
6531 163 : if (!CheckUpdatableTable("DeleteField"))
6532 2 : return OGRERR_FAILURE;
6533 :
6534 321 : if (iFieldToDelete < 0 ||
6535 160 : iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
6536 : {
6537 2 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6538 2 : return OGRERR_FAILURE;
6539 : }
6540 :
6541 159 : ResetReading();
6542 159 : RunDeferredCreationIfNecessary();
6543 159 : if (!RunDeferredSpatialIndexUpdate())
6544 0 : return OGRERR_FAILURE;
6545 :
6546 : const char *pszFieldName =
6547 159 : m_poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
6548 :
6549 : /* -------------------------------------------------------------------- */
6550 : /* Drop any iterator since we change the DB structure */
6551 : /* -------------------------------------------------------------------- */
6552 159 : m_poDS->ResetReadingAllLayers();
6553 :
6554 : // Temporary remove foreign key checks
6555 : const GPKGTemporaryForeignKeyCheckDisabler
6556 318 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
6557 :
6558 174 : if (m_poDS->GetCurrentSavepoint().empty() &&
6559 15 : m_poDS->SoftStartTransaction() != OGRERR_NONE)
6560 : {
6561 0 : return OGRERR_FAILURE;
6562 : }
6563 :
6564 : // ALTER TABLE ... DROP COLUMN ... was first implemented in 3.35.0 but
6565 : // there was bug fixes related to it until 3.35.5
6566 : #if SQLITE_VERSION_NUMBER >= 3035005L
6567 159 : OGRErr eErr = SQLCommand(
6568 318 : m_poDS->GetDB(), CPLString()
6569 : .Printf("ALTER TABLE \"%s\" DROP COLUMN \"%s\"",
6570 318 : SQLEscapeName(m_pszTableName).c_str(),
6571 477 : SQLEscapeName(pszFieldName).c_str())
6572 : .c_str());
6573 : #else
6574 : /* -------------------------------------------------------------------- */
6575 : /* Recreate table in a transaction */
6576 : /* Build list of old fields, and the list of new fields. */
6577 : /* -------------------------------------------------------------------- */
6578 : std::vector<OGRFieldDefn *> apoFields;
6579 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6580 : {
6581 : if (iField == iFieldToDelete)
6582 : continue;
6583 :
6584 : OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6585 : apoFields.push_back(poFieldDefn);
6586 : }
6587 :
6588 : CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
6589 : CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6590 :
6591 : OGRErr eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
6592 : #endif
6593 :
6594 : /* -------------------------------------------------------------------- */
6595 : /* Update gpkg_extensions if needed. */
6596 : /* -------------------------------------------------------------------- */
6597 159 : if (eErr == OGRERR_NONE && m_poDS->HasExtensionsTable())
6598 : {
6599 154 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_extensions WHERE "
6600 : "lower(table_name) = lower('%q') AND "
6601 : "lower(column_name) = lower('%q')",
6602 : m_pszTableName, pszFieldName);
6603 154 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6604 154 : sqlite3_free(pszSQL);
6605 : }
6606 :
6607 : /* -------------------------------------------------------------------- */
6608 : /* Update gpkg_data_columns if needed. */
6609 : /* -------------------------------------------------------------------- */
6610 159 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6611 : {
6612 4 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_data_columns WHERE "
6613 : "lower(table_name) = lower('%q') AND "
6614 : "lower(column_name) = lower('%q')",
6615 : m_pszTableName, pszFieldName);
6616 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6617 4 : sqlite3_free(pszSQL);
6618 : }
6619 :
6620 : /* -------------------------------------------------------------------- */
6621 : /* Update gpkg_metadata_reference if needed. */
6622 : /* -------------------------------------------------------------------- */
6623 159 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6624 : {
6625 : {
6626 : // Delete from gpkg_metadata metadata records that are only
6627 : // referenced by the column we are about to drop
6628 3 : char *pszSQL = sqlite3_mprintf(
6629 : "DELETE FROM gpkg_metadata WHERE id IN ("
6630 : "SELECT DISTINCT md_file_id FROM "
6631 : "gpkg_metadata_reference WHERE "
6632 : "lower(table_name) = lower('%q') "
6633 : "AND lower(column_name) = lower('%q') "
6634 : "AND md_parent_id is NULL) "
6635 : "AND id NOT IN ("
6636 : "SELECT DISTINCT md_file_id FROM gpkg_metadata_reference WHERE "
6637 : "md_file_id IN ("
6638 : "SELECT DISTINCT md_file_id FROM "
6639 : "gpkg_metadata_reference WHERE "
6640 : "lower(table_name) = lower('%q') "
6641 : "AND lower(column_name) = lower('%q') "
6642 : "AND md_parent_id is NULL) "
6643 : "AND ("
6644 : "lower(table_name) <> lower('%q') "
6645 : "OR column_name IS NULL "
6646 : "OR lower(column_name) <> lower('%q')))",
6647 : m_pszTableName, pszFieldName, m_pszTableName, pszFieldName,
6648 : m_pszTableName, pszFieldName);
6649 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6650 3 : sqlite3_free(pszSQL);
6651 : }
6652 :
6653 3 : if (eErr == OGRERR_NONE)
6654 : {
6655 : char *pszSQL =
6656 3 : sqlite3_mprintf("DELETE FROM gpkg_metadata_reference WHERE "
6657 : "lower(table_name) = lower('%q') AND "
6658 : "lower(column_name) = lower('%q')",
6659 : m_pszTableName, pszFieldName);
6660 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6661 3 : sqlite3_free(pszSQL);
6662 : }
6663 : }
6664 :
6665 : /* -------------------------------------------------------------------- */
6666 : /* Check foreign key integrity if enforcement of foreign keys */
6667 : /* constraint is enabled. */
6668 : /* -------------------------------------------------------------------- */
6669 318 : if (eErr == OGRERR_NONE &&
6670 159 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
6671 : {
6672 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
6673 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
6674 : }
6675 :
6676 : /* -------------------------------------------------------------------- */
6677 : /* Finish */
6678 : /* -------------------------------------------------------------------- */
6679 159 : if (eErr == OGRERR_NONE)
6680 : {
6681 :
6682 159 : if (m_poDS->GetCurrentSavepoint().empty())
6683 15 : eErr = m_poDS->SoftCommitTransaction();
6684 :
6685 159 : if (eErr == OGRERR_NONE)
6686 : {
6687 :
6688 159 : if (m_poDS->IsInTransaction())
6689 : {
6690 153 : auto poFieldDefn{whileUnsealing(m_poFeatureDefn)
6691 306 : ->StealFieldDefn(iFieldToDelete)};
6692 153 : if (poFieldDefn)
6693 : {
6694 : m_apoFieldDefnChanges.emplace_back(
6695 153 : std::move(poFieldDefn), iFieldToDelete,
6696 153 : FieldChangeType::DELETE_FIELD,
6697 306 : m_poDS->GetCurrentSavepoint());
6698 : }
6699 : else
6700 : {
6701 0 : eErr = OGRERR_FAILURE;
6702 : }
6703 : }
6704 : else
6705 : {
6706 12 : eErr = whileUnsealing(m_poFeatureDefn)
6707 6 : ->DeleteFieldDefn(iFieldToDelete);
6708 : }
6709 159 : ResetReading();
6710 : }
6711 : }
6712 0 : else if (m_poDS->GetCurrentSavepoint().empty())
6713 : {
6714 0 : m_poDS->SoftRollbackTransaction();
6715 : }
6716 :
6717 159 : return eErr;
6718 : }
6719 :
6720 : /************************************************************************/
6721 : /* RenameFieldInAuxiliaryTables() */
6722 : /************************************************************************/
6723 :
6724 : OGRErr
6725 17 : OGRGeoPackageTableLayer::RenameFieldInAuxiliaryTables(const char *pszOldName,
6726 : const char *pszNewName)
6727 : {
6728 17 : OGRErr eErr = OGRERR_NONE;
6729 17 : sqlite3 *hDB = m_poDS->GetDB();
6730 :
6731 : /* -------------------------------------------------------------------- */
6732 : /* Update gpkg_extensions if needed. */
6733 : /* -------------------------------------------------------------------- */
6734 17 : if (/* eErr == OGRERR_NONE && */ m_poDS->HasExtensionsTable())
6735 : {
6736 15 : char *pszSQL = sqlite3_mprintf(
6737 : "UPDATE gpkg_extensions SET column_name = '%q' WHERE "
6738 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6739 : "lower('%q')",
6740 : pszNewName, m_pszTableName, pszOldName);
6741 15 : eErr = SQLCommand(hDB, pszSQL);
6742 15 : sqlite3_free(pszSQL);
6743 : }
6744 :
6745 : /* -------------------------------------------------------------------- */
6746 : /* Update gpkg_data_columns if needed. */
6747 : /* -------------------------------------------------------------------- */
6748 17 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6749 : {
6750 6 : char *pszSQL = sqlite3_mprintf(
6751 : "UPDATE gpkg_data_columns SET column_name = '%q' WHERE "
6752 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6753 : "lower('%q')",
6754 : pszNewName, m_pszTableName, pszOldName);
6755 6 : eErr = SQLCommand(hDB, pszSQL);
6756 6 : sqlite3_free(pszSQL);
6757 : }
6758 :
6759 : /* -------------------------------------------------------------------- */
6760 : /* Update gpkg_metadata_reference if needed. */
6761 : /* -------------------------------------------------------------------- */
6762 17 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6763 : {
6764 7 : char *pszSQL = sqlite3_mprintf(
6765 : "UPDATE gpkg_metadata_reference SET column_name = '%q' WHERE "
6766 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6767 : "lower('%q')",
6768 : pszNewName, m_pszTableName, pszOldName);
6769 7 : eErr = SQLCommand(hDB, pszSQL);
6770 7 : sqlite3_free(pszSQL);
6771 : }
6772 :
6773 17 : return eErr;
6774 : }
6775 :
6776 : /************************************************************************/
6777 : /* AlterFieldDefn() */
6778 : /************************************************************************/
6779 :
6780 34 : OGRErr OGRGeoPackageTableLayer::AlterFieldDefn(int iFieldToAlter,
6781 : OGRFieldDefn *poNewFieldDefn,
6782 : int nFlagsIn)
6783 : {
6784 34 : if (!m_bFeatureDefnCompleted)
6785 1 : GetLayerDefn();
6786 34 : if (!CheckUpdatableTable("AlterFieldDefn"))
6787 2 : return OGRERR_FAILURE;
6788 :
6789 32 : if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
6790 : {
6791 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6792 1 : return OGRERR_FAILURE;
6793 : }
6794 :
6795 : /* -------------------------------------------------------------------- */
6796 : /* Deferred actions, reset state. */
6797 : /* -------------------------------------------------------------------- */
6798 31 : ResetReading();
6799 31 : RunDeferredCreationIfNecessary();
6800 31 : if (m_bThreadRTreeStarted)
6801 0 : CancelAsyncRTree();
6802 31 : if (!RunDeferredSpatialIndexUpdate())
6803 0 : return OGRERR_FAILURE;
6804 :
6805 : /* -------------------------------------------------------------------- */
6806 : /* Check that the new column name is not a duplicate. */
6807 : /* -------------------------------------------------------------------- */
6808 :
6809 : OGRFieldDefn *poFieldDefnToAlter =
6810 31 : m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
6811 62 : const CPLString osOldColName(poFieldDefnToAlter->GetNameRef());
6812 31 : const CPLString osNewColName((nFlagsIn & ALTER_NAME_FLAG)
6813 : ? CPLString(poNewFieldDefn->GetNameRef())
6814 62 : : osOldColName);
6815 :
6816 : const bool bRenameCol =
6817 60 : (nFlagsIn & ALTER_NAME_FLAG) &&
6818 29 : strcmp(poNewFieldDefn->GetNameRef(), osOldColName) != 0;
6819 31 : if (bRenameCol)
6820 : {
6821 54 : if ((m_pszFidColumn &&
6822 18 : strcmp(poNewFieldDefn->GetNameRef(), m_pszFidColumn) == 0) ||
6823 17 : (GetGeomType() != wkbNone &&
6824 17 : strcmp(poNewFieldDefn->GetNameRef(),
6825 53 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0) ||
6826 16 : m_poFeatureDefn->GetFieldIndex(poNewFieldDefn->GetNameRef()) >= 0)
6827 : {
6828 4 : CPLError(CE_Failure, CPLE_AppDefined,
6829 : "Field name %s is already used for another field",
6830 : poNewFieldDefn->GetNameRef());
6831 4 : return OGRERR_FAILURE;
6832 : }
6833 : }
6834 :
6835 : /* -------------------------------------------------------------------- */
6836 : /* Build the modified field definition from the flags. */
6837 : /* -------------------------------------------------------------------- */
6838 54 : OGRFieldDefn oTmpFieldDefn(poFieldDefnToAlter);
6839 27 : bool bUseRewriteSchemaMethod = (m_poDS->m_nSoftTransactionLevel == 0);
6840 27 : int nActualFlags = 0;
6841 27 : if (bRenameCol)
6842 : {
6843 14 : nActualFlags |= ALTER_NAME_FLAG;
6844 14 : oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef());
6845 : }
6846 50 : if ((nFlagsIn & ALTER_TYPE_FLAG) != 0 &&
6847 23 : (poFieldDefnToAlter->GetType() != poNewFieldDefn->GetType() ||
6848 17 : poFieldDefnToAlter->GetSubType() != poNewFieldDefn->GetSubType()))
6849 : {
6850 8 : nActualFlags |= ALTER_TYPE_FLAG;
6851 8 : oTmpFieldDefn.SetSubType(OFSTNone);
6852 8 : oTmpFieldDefn.SetType(poNewFieldDefn->GetType());
6853 8 : oTmpFieldDefn.SetSubType(poNewFieldDefn->GetSubType());
6854 : }
6855 71 : if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) != 0 &&
6856 44 : (poFieldDefnToAlter->GetWidth() != poNewFieldDefn->GetWidth() ||
6857 21 : poFieldDefnToAlter->GetPrecision() != poNewFieldDefn->GetPrecision()))
6858 : {
6859 2 : nActualFlags |= ALTER_WIDTH_PRECISION_FLAG;
6860 2 : oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth());
6861 2 : oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision());
6862 : }
6863 50 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) != 0 &&
6864 23 : poFieldDefnToAlter->IsNullable() != poNewFieldDefn->IsNullable())
6865 : {
6866 7 : nActualFlags |= ALTER_NULLABLE_FLAG;
6867 7 : if (!poNewFieldDefn->IsNullable())
6868 5 : bUseRewriteSchemaMethod = false;
6869 7 : oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable());
6870 : }
6871 50 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) != 0 &&
6872 23 : !((poFieldDefnToAlter->GetDefault() == nullptr &&
6873 20 : poNewFieldDefn->GetDefault() == nullptr) ||
6874 4 : (poFieldDefnToAlter->GetDefault() != nullptr &&
6875 3 : poNewFieldDefn->GetDefault() != nullptr &&
6876 2 : strcmp(poFieldDefnToAlter->GetDefault(),
6877 : poNewFieldDefn->GetDefault()) == 0)))
6878 : {
6879 2 : nActualFlags |= ALTER_DEFAULT_FLAG;
6880 2 : oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault());
6881 : }
6882 50 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) != 0 &&
6883 23 : poFieldDefnToAlter->IsUnique() != poNewFieldDefn->IsUnique())
6884 : {
6885 2 : nActualFlags |= ALTER_UNIQUE_FLAG;
6886 2 : if (poNewFieldDefn->IsUnique())
6887 1 : bUseRewriteSchemaMethod = false;
6888 2 : oTmpFieldDefn.SetUnique(poNewFieldDefn->IsUnique());
6889 : }
6890 50 : if ((nFlagsIn & ALTER_DOMAIN_FLAG) != 0 &&
6891 23 : poFieldDefnToAlter->GetDomainName() != poNewFieldDefn->GetDomainName())
6892 : {
6893 3 : nActualFlags |= ALTER_DOMAIN_FLAG;
6894 3 : oTmpFieldDefn.SetDomainName(poNewFieldDefn->GetDomainName());
6895 : }
6896 51 : if ((nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG) != 0 &&
6897 24 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
6898 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
6899 : {
6900 5 : nActualFlags |= ALTER_ALTERNATIVE_NAME_FLAG;
6901 5 : oTmpFieldDefn.SetAlternativeName(
6902 : poNewFieldDefn->GetAlternativeNameRef());
6903 : }
6904 51 : if ((nFlagsIn & ALTER_COMMENT_FLAG) != 0 &&
6905 24 : poFieldDefnToAlter->GetComment() != poNewFieldDefn->GetComment())
6906 : {
6907 3 : nActualFlags |= ALTER_COMMENT_FLAG;
6908 3 : oTmpFieldDefn.SetComment(poNewFieldDefn->GetComment());
6909 : }
6910 :
6911 : /* -------------------------------------------------------------------- */
6912 : /* Build list of old fields, and the list of new fields. */
6913 : /* -------------------------------------------------------------------- */
6914 54 : std::vector<OGRFieldDefn *> apoFields;
6915 54 : std::vector<OGRFieldDefn *> apoFieldsOld;
6916 90 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6917 : {
6918 : OGRFieldDefn *poFieldDefn;
6919 63 : if (iField == iFieldToAlter)
6920 : {
6921 27 : poFieldDefn = &oTmpFieldDefn;
6922 : }
6923 : else
6924 : {
6925 36 : poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6926 : }
6927 63 : apoFields.push_back(poFieldDefn);
6928 63 : apoFieldsOld.push_back(m_poFeatureDefn->GetFieldDefn(iField));
6929 : }
6930 :
6931 54 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6932 :
6933 : /* -------------------------------------------------------------------- */
6934 : /* Drop any iterator since we change the DB structure */
6935 : /* -------------------------------------------------------------------- */
6936 27 : m_poDS->ResetReadingAllLayers();
6937 :
6938 : #if SQLITE_VERSION_NUMBER >= 3053000L
6939 : const bool bUseRenameColumn =
6940 : (nActualFlags == ALTER_NAME_FLAG ||
6941 : nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG));
6942 : #else
6943 27 : const bool bUseRenameColumn = (nActualFlags == ALTER_NAME_FLAG);
6944 : #endif
6945 27 : if (bUseRenameColumn)
6946 4 : bUseRewriteSchemaMethod = false;
6947 :
6948 27 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
6949 0 : return OGRERR_FAILURE;
6950 :
6951 27 : sqlite3 *hDB = m_poDS->GetDB();
6952 27 : OGRErr eErr = OGRERR_NONE;
6953 :
6954 : /* -------------------------------------------------------------------- */
6955 : /* Drop triggers and index that look like to be related to the */
6956 : /* column if renaming. We re-install some indexes afterwards. */
6957 : /* -------------------------------------------------------------------- */
6958 0 : std::unique_ptr<SQLResult> oTriggers;
6959 : // cppcheck-suppress knownConditionTrueFalse
6960 27 : if (bRenameCol && !bUseRenameColumn)
6961 : {
6962 10 : char *pszSQL = sqlite3_mprintf(
6963 : "SELECT name, type, sql FROM sqlite_master WHERE "
6964 : "type IN ('trigger','index') "
6965 : "AND lower(tbl_name)=lower('%q') AND sql LIKE '%%%q%%' LIMIT 10000",
6966 20 : m_pszTableName, SQLEscapeName(osOldColName).c_str());
6967 10 : oTriggers = SQLQuery(hDB, pszSQL);
6968 10 : sqlite3_free(pszSQL);
6969 :
6970 10 : if (!oTriggers)
6971 : {
6972 0 : eErr = OGRERR_FAILURE;
6973 : }
6974 :
6975 12 : for (int i = 0; oTriggers && i < oTriggers->RowCount(); i++)
6976 : {
6977 : pszSQL =
6978 2 : sqlite3_mprintf("DROP %s \"%w\"", oTriggers->GetValue(1, i),
6979 : oTriggers->GetValue(0, i));
6980 2 : eErr = SQLCommand(hDB, pszSQL);
6981 2 : sqlite3_free(pszSQL);
6982 : }
6983 : }
6984 :
6985 27 : bool bUseRecreateTableMethod =
6986 27 : !bUseRenameColumn && !bUseRewriteSchemaMethod;
6987 :
6988 : #if SQLITE_VERSION_NUMBER >= 3053000L
6989 : if (eErr == OGRERR_NONE &&
6990 : (nActualFlags == ALTER_NULLABLE_FLAG ||
6991 : nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG)))
6992 : {
6993 : eErr =
6994 : SQLCommand(m_poDS->GetDB(),
6995 : CPLString().Printf(
6996 : "ALTER TABLE \"%s\" ALTER COLUMN \"%s\" %s NOT NULL",
6997 : SQLEscapeName(m_pszTableName).c_str(),
6998 : SQLEscapeName(osOldColName).c_str(),
6999 : poNewFieldDefn->IsNullable() ? "DROP" : "SET"));
7000 : bUseRecreateTableMethod = false;
7001 : bUseRewriteSchemaMethod = false;
7002 : }
7003 : #endif
7004 :
7005 27 : if (bUseRenameColumn)
7006 : {
7007 4 : if (eErr == OGRERR_NONE)
7008 : {
7009 4 : CPLDebug("GPKG", "Running ALTER TABLE RENAME COLUMN");
7010 4 : eErr = SQLCommand(
7011 4 : m_poDS->GetDB(),
7012 8 : CPLString()
7013 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
7014 8 : SQLEscapeName(m_pszTableName).c_str(),
7015 8 : SQLEscapeName(osOldColName).c_str(),
7016 16 : SQLEscapeName(osNewColName).c_str())
7017 : .c_str());
7018 : }
7019 : }
7020 23 : else if (bUseRecreateTableMethod)
7021 : {
7022 : /* --------------------------------------------------------------------
7023 : */
7024 : /* If we are within a transaction, we cannot use the method */
7025 : /* that consists in altering the database in a raw way. */
7026 : /* --------------------------------------------------------------------
7027 : */
7028 : const CPLString osFieldListForSelect(
7029 22 : BuildSelectFieldList(apoFieldsOld));
7030 :
7031 11 : if (eErr == OGRERR_NONE)
7032 : {
7033 11 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
7034 : }
7035 : }
7036 12 : else if (bUseRewriteSchemaMethod)
7037 : {
7038 : /* --------------------------------------------------------------------
7039 : */
7040 : /* Rewrite schema in a transaction by altering the database */
7041 : /* schema in a rather raw way, as described at bottom of */
7042 : /* https://www.sqlite.org/lang_altertable.html */
7043 : /* --------------------------------------------------------------------
7044 : */
7045 :
7046 : /* --------------------------------------------------------------------
7047 : */
7048 : /* Collect schema version number. */
7049 : /* --------------------------------------------------------------------
7050 : */
7051 12 : int nSchemaVersion = SQLGetInteger(hDB, "PRAGMA schema_version", &eErr);
7052 :
7053 : /* --------------------------------------------------------------------
7054 : */
7055 : /* Turn on writable schema. */
7056 : /* --------------------------------------------------------------------
7057 : */
7058 12 : if (eErr == OGRERR_NONE)
7059 : {
7060 12 : eErr = m_poDS->PragmaCheck("writable_schema=ON", "", 0);
7061 : }
7062 :
7063 : /* --------------------------------------------------------------------
7064 : */
7065 : /* Rewrite CREATE TABLE statement. */
7066 : /* --------------------------------------------------------------------
7067 : */
7068 12 : if (eErr == OGRERR_NONE)
7069 : {
7070 : char *psSQLCreateTable =
7071 12 : sqlite3_mprintf("CREATE TABLE \"%w\" (%s)", m_pszTableName,
7072 : osColumnsForCreate.c_str());
7073 12 : char *pszSQL = sqlite3_mprintf("UPDATE sqlite_master SET sql='%q' "
7074 : "WHERE type='table' AND name='%q'",
7075 : psSQLCreateTable, m_pszTableName);
7076 12 : eErr = SQLCommand(hDB, pszSQL);
7077 12 : sqlite3_free(psSQLCreateTable);
7078 12 : sqlite3_free(pszSQL);
7079 : }
7080 :
7081 : /* --------------------------------------------------------------------
7082 : */
7083 : /* Increment schema number. */
7084 : /* --------------------------------------------------------------------
7085 : */
7086 12 : if (eErr == OGRERR_NONE)
7087 : {
7088 12 : char *pszSQL = sqlite3_mprintf("PRAGMA schema_version = %d",
7089 : nSchemaVersion + 1);
7090 12 : eErr = SQLCommand(hDB, pszSQL);
7091 12 : sqlite3_free(pszSQL);
7092 : }
7093 :
7094 : /* --------------------------------------------------------------------
7095 : */
7096 : /* Turn off writable schema. */
7097 : /* --------------------------------------------------------------------
7098 : */
7099 12 : if (eErr == OGRERR_NONE)
7100 : {
7101 12 : eErr = m_poDS->PragmaCheck("writable_schema=OFF", "", 0);
7102 : }
7103 : }
7104 :
7105 : /* -------------------------------------------------------------------- */
7106 : /* Update auxiliary tables */
7107 : /* -------------------------------------------------------------------- */
7108 27 : if (bRenameCol && eErr == OGRERR_NONE)
7109 : {
7110 13 : eErr = RenameFieldInAuxiliaryTables(osOldColName.c_str(),
7111 : poNewFieldDefn->GetNameRef());
7112 : }
7113 :
7114 : /* -------------------------------------------------------------------- */
7115 : /* Update gpkgext_relations if needed. */
7116 : /* -------------------------------------------------------------------- */
7117 27 : if (bRenameCol && eErr == OGRERR_NONE && m_poDS->HasGpkgextRelationsTable())
7118 : {
7119 2 : char *pszSQL = sqlite3_mprintf(
7120 : "UPDATE gpkgext_relations SET base_primary_column = '%q' WHERE "
7121 : "lower(base_table_name) = lower('%q') AND "
7122 : "lower(base_primary_column) = lower('%q')",
7123 : poNewFieldDefn->GetNameRef(), m_pszTableName, osOldColName.c_str());
7124 2 : eErr = SQLCommand(hDB, pszSQL);
7125 2 : sqlite3_free(pszSQL);
7126 :
7127 2 : if (eErr == OGRERR_NONE)
7128 : {
7129 : pszSQL =
7130 2 : sqlite3_mprintf("UPDATE gpkgext_relations SET "
7131 : "related_primary_column = '%q' WHERE "
7132 : "lower(related_table_name) = lower('%q') AND "
7133 : "lower(related_primary_column) = lower('%q')",
7134 : poNewFieldDefn->GetNameRef(), m_pszTableName,
7135 : osOldColName.c_str());
7136 2 : eErr = SQLCommand(hDB, pszSQL);
7137 2 : sqlite3_free(pszSQL);
7138 : }
7139 2 : m_poDS->ClearCachedRelationships();
7140 : }
7141 :
7142 : /* -------------------------------------------------------------------- */
7143 : /* Run integrity check only if explicitly required. */
7144 : /* -------------------------------------------------------------------- */
7145 52 : if (eErr == OGRERR_NONE &&
7146 25 : CPLTestBool(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")))
7147 : {
7148 0 : CPLDebug("GPKG", "Running PRAGMA integrity_check");
7149 0 : eErr = m_poDS->PragmaCheck("integrity_check", "ok", 1);
7150 : }
7151 :
7152 : /* -------------------------------------------------------------------- */
7153 : /* Otherwise check foreign key integrity if enforcement of foreign */
7154 : /* kets constraint is enabled. */
7155 : /* -------------------------------------------------------------------- */
7156 52 : else if (eErr == OGRERR_NONE &&
7157 25 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
7158 : {
7159 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
7160 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
7161 : }
7162 :
7163 : /* -------------------------------------------------------------------- */
7164 : /* Finish */
7165 : /* -------------------------------------------------------------------- */
7166 27 : if (eErr == OGRERR_NONE)
7167 : {
7168 :
7169 25 : eErr = m_poDS->SoftCommitTransaction();
7170 :
7171 : // We need to force database reopening due to schema change
7172 37 : if (eErr == OGRERR_NONE && bUseRewriteSchemaMethod &&
7173 12 : !m_poDS->ReOpenDB())
7174 : {
7175 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen database");
7176 0 : eErr = OGRERR_FAILURE;
7177 : }
7178 25 : hDB = m_poDS->GetDB();
7179 :
7180 : /* --------------------------------------------------------------------
7181 : */
7182 : /* Recreate indices. */
7183 : /* --------------------------------------------------------------------
7184 : */
7185 27 : for (int i = 0;
7186 27 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
7187 : {
7188 2 : if (EQUAL(oTriggers->GetValue(1, i), "index"))
7189 : {
7190 4 : CPLString osSQL(oTriggers->GetValue(2, i));
7191 : // CREATE INDEX idx_name ON table_name(column_name)
7192 2 : char **papszTokens = SQLTokenize(osSQL);
7193 2 : if (CSLCount(papszTokens) == 8 &&
7194 2 : EQUAL(papszTokens[0], "CREATE") &&
7195 2 : EQUAL(papszTokens[1], "INDEX") &&
7196 6 : EQUAL(papszTokens[3], "ON") && EQUAL(papszTokens[5], "(") &&
7197 2 : EQUAL(papszTokens[7], ")"))
7198 : {
7199 2 : osSQL = "CREATE INDEX ";
7200 2 : osSQL += papszTokens[2];
7201 2 : osSQL += " ON ";
7202 2 : osSQL += papszTokens[4];
7203 2 : osSQL += "(\"";
7204 2 : osSQL += SQLEscapeName(osNewColName);
7205 2 : osSQL += "\")";
7206 2 : eErr = SQLCommand(hDB, osSQL);
7207 : }
7208 2 : CSLDestroy(papszTokens);
7209 : }
7210 : }
7211 :
7212 25 : if (eErr == OGRERR_NONE)
7213 : {
7214 :
7215 25 : if (m_poDS->IsInTransaction())
7216 : {
7217 : m_apoFieldDefnChanges.emplace_back(
7218 6 : std::make_unique<OGRFieldDefn>(poFieldDefnToAlter),
7219 6 : iFieldToAlter, FieldChangeType::ALTER_FIELD,
7220 18 : m_poDS->GetCurrentSavepoint());
7221 : }
7222 :
7223 50 : auto oTemporaryUnsealer(poFieldDefnToAlter->GetTemporaryUnsealer());
7224 25 : bool bNeedsEntryInGpkgDataColumns = false;
7225 :
7226 : // field type
7227 25 : if (nActualFlags & ALTER_TYPE_FLAG)
7228 : {
7229 8 : poFieldDefnToAlter->SetSubType(OFSTNone);
7230 8 : poFieldDefnToAlter->SetType(poNewFieldDefn->GetType());
7231 8 : poFieldDefnToAlter->SetSubType(poNewFieldDefn->GetSubType());
7232 : }
7233 37 : if (poFieldDefnToAlter->GetType() == OFTString &&
7234 12 : poFieldDefnToAlter->GetSubType() == OFSTJSON)
7235 : {
7236 1 : bNeedsEntryInGpkgDataColumns = true;
7237 : }
7238 :
7239 : // name
7240 25 : if (nActualFlags & ALTER_NAME_FLAG)
7241 : {
7242 13 : poFieldDefnToAlter->SetName(poNewFieldDefn->GetNameRef());
7243 : }
7244 :
7245 : // width/precision
7246 25 : if (nActualFlags & ALTER_WIDTH_PRECISION_FLAG)
7247 : {
7248 2 : poFieldDefnToAlter->SetWidth(poNewFieldDefn->GetWidth());
7249 2 : poFieldDefnToAlter->SetPrecision(
7250 : poNewFieldDefn->GetPrecision());
7251 : }
7252 :
7253 : // constraints
7254 25 : if (nActualFlags & ALTER_NULLABLE_FLAG)
7255 5 : poFieldDefnToAlter->SetNullable(poNewFieldDefn->IsNullable());
7256 25 : if (nActualFlags & ALTER_DEFAULT_FLAG)
7257 2 : poFieldDefnToAlter->SetDefault(poNewFieldDefn->GetDefault());
7258 25 : if (nActualFlags & ALTER_UNIQUE_FLAG)
7259 2 : poFieldDefnToAlter->SetUnique(poNewFieldDefn->IsUnique());
7260 :
7261 : // domain
7262 28 : if ((nActualFlags & ALTER_DOMAIN_FLAG) &&
7263 3 : poFieldDefnToAlter->GetDomainName() !=
7264 3 : poNewFieldDefn->GetDomainName())
7265 : {
7266 3 : poFieldDefnToAlter->SetDomainName(
7267 : poNewFieldDefn->GetDomainName());
7268 : }
7269 25 : if (!poFieldDefnToAlter->GetDomainName().empty())
7270 : {
7271 3 : bNeedsEntryInGpkgDataColumns = true;
7272 : }
7273 :
7274 : // alternative name
7275 30 : if ((nActualFlags & ALTER_ALTERNATIVE_NAME_FLAG) &&
7276 5 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
7277 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
7278 : {
7279 5 : poFieldDefnToAlter->SetAlternativeName(
7280 : poNewFieldDefn->GetAlternativeNameRef());
7281 : }
7282 50 : if (!std::string(poFieldDefnToAlter->GetAlternativeNameRef())
7283 25 : .empty())
7284 : {
7285 7 : bNeedsEntryInGpkgDataColumns = true;
7286 : }
7287 :
7288 : // comment
7289 28 : if ((nActualFlags & ALTER_COMMENT_FLAG) &&
7290 3 : poFieldDefnToAlter->GetComment() !=
7291 3 : poNewFieldDefn->GetComment())
7292 : {
7293 3 : poFieldDefnToAlter->SetComment(poNewFieldDefn->GetComment());
7294 : }
7295 25 : if (!poFieldDefnToAlter->GetComment().empty())
7296 : {
7297 3 : bNeedsEntryInGpkgDataColumns = true;
7298 : }
7299 :
7300 25 : if (m_poDS->HasDataColumnsTable())
7301 : {
7302 14 : char *pszSQL = sqlite3_mprintf(
7303 : "DELETE FROM gpkg_data_columns WHERE "
7304 : "lower(table_name) = lower('%q') AND "
7305 : "lower(column_name) = lower('%q')",
7306 : m_pszTableName, poFieldDefnToAlter->GetNameRef());
7307 14 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7308 14 : sqlite3_free(pszSQL);
7309 : }
7310 :
7311 25 : if (bNeedsEntryInGpkgDataColumns)
7312 : {
7313 12 : if (!DoSpecialProcessingForColumnCreation(poFieldDefnToAlter))
7314 0 : eErr = OGRERR_FAILURE;
7315 : }
7316 :
7317 25 : ResetReading();
7318 : }
7319 : }
7320 : else
7321 : {
7322 2 : m_poDS->SoftRollbackTransaction();
7323 : }
7324 :
7325 27 : return eErr;
7326 : }
7327 :
7328 : /************************************************************************/
7329 : /* AlterGeomFieldDefn() */
7330 : /************************************************************************/
7331 :
7332 8 : OGRErr OGRGeoPackageTableLayer::AlterGeomFieldDefn(
7333 : int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
7334 : int nFlagsIn)
7335 : {
7336 8 : if (!m_bFeatureDefnCompleted)
7337 1 : GetLayerDefn();
7338 8 : if (!CheckUpdatableTable("AlterGeomFieldDefn"))
7339 0 : return OGRERR_FAILURE;
7340 :
7341 16 : if (iGeomFieldToAlter < 0 ||
7342 8 : iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
7343 : {
7344 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
7345 0 : return OGRERR_FAILURE;
7346 : }
7347 :
7348 : /* -------------------------------------------------------------------- */
7349 : /* Deferred actions, reset state. */
7350 : /* -------------------------------------------------------------------- */
7351 8 : ResetReading();
7352 8 : RunDeferredCreationIfNecessary();
7353 8 : if (m_bThreadRTreeStarted)
7354 0 : CancelAsyncRTree();
7355 8 : if (!RunDeferredSpatialIndexUpdate())
7356 0 : return OGRERR_FAILURE;
7357 8 : RevertWorkaroundUpdate1TriggerIssue();
7358 :
7359 : /* -------------------------------------------------------------------- */
7360 : /* Drop any iterator since we change the DB structure */
7361 : /* -------------------------------------------------------------------- */
7362 8 : m_poDS->ResetReadingAllLayers();
7363 :
7364 8 : auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
7365 16 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
7366 :
7367 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
7368 : {
7369 : // could be potentially done. Requires rewriting the CREATE TABLE
7370 : // statement
7371 0 : if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
7372 : {
7373 0 : CPLError(CE_Failure, CPLE_NotSupported,
7374 : "Altering the geometry field type is not currently "
7375 : "supported for "
7376 : "GeoPackage");
7377 0 : return OGRERR_FAILURE;
7378 : }
7379 : }
7380 :
7381 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
7382 : {
7383 : // could be potentially done. Requires rewriting the CREATE TABLE
7384 : // statement
7385 0 : if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
7386 : {
7387 0 : CPLError(CE_Failure, CPLE_NotSupported,
7388 : "Altering the nullable state of the geometry field "
7389 : "is not currently supported for GeoPackage");
7390 0 : return OGRERR_FAILURE;
7391 : }
7392 : }
7393 :
7394 12 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
7395 4 : strcmp(poGeomFieldDefn->GetNameRef(),
7396 : poNewGeomFieldDefn->GetNameRef()) != 0)
7397 : {
7398 4 : const bool bHasSpatialIndex = HasSpatialIndex();
7399 :
7400 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7401 0 : return OGRERR_FAILURE;
7402 :
7403 : // Rename geometry field
7404 4 : auto eErr = SQLCommand(
7405 4 : m_poDS->GetDB(),
7406 4 : CPLString()
7407 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
7408 8 : SQLEscapeName(m_pszTableName).c_str(),
7409 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7410 16 : SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str())
7411 : .c_str());
7412 4 : if (eErr != OGRERR_NONE)
7413 : {
7414 0 : m_poDS->SoftRollbackTransaction();
7415 0 : return OGRERR_FAILURE;
7416 : }
7417 :
7418 : // Update gpkg_geometry_columns
7419 4 : eErr = SQLCommand(
7420 4 : m_poDS->GetDB(),
7421 4 : CPLString()
7422 : .Printf(
7423 : "UPDATE gpkg_geometry_columns SET column_name = '%s' "
7424 : "WHERE lower(table_name) = lower('%s') "
7425 : "AND lower(column_name) = lower('%s')",
7426 8 : SQLEscapeLiteral(poNewGeomFieldDefn->GetNameRef()).c_str(),
7427 8 : SQLEscapeLiteral(m_pszTableName).c_str(),
7428 16 : SQLEscapeLiteral(poGeomFieldDefn->GetNameRef()).c_str())
7429 : .c_str());
7430 4 : if (eErr != OGRERR_NONE)
7431 : {
7432 0 : m_poDS->SoftRollbackTransaction();
7433 0 : return OGRERR_FAILURE;
7434 : }
7435 :
7436 : // Update auxiliary tables
7437 4 : eErr = RenameFieldInAuxiliaryTables(poGeomFieldDefn->GetNameRef(),
7438 : poNewGeomFieldDefn->GetNameRef());
7439 4 : if (eErr != OGRERR_NONE)
7440 : {
7441 0 : m_poDS->SoftRollbackTransaction();
7442 0 : return OGRERR_FAILURE;
7443 : }
7444 :
7445 4 : std::string osNewRTreeName;
7446 4 : if (bHasSpatialIndex)
7447 : {
7448 4 : osNewRTreeName = "rtree_";
7449 4 : osNewRTreeName += m_pszTableName;
7450 4 : osNewRTreeName += "_";
7451 4 : osNewRTreeName += poNewGeomFieldDefn->GetNameRef();
7452 :
7453 : // Rename spatial index tables (not strictly needed, but for
7454 : // consistency)
7455 : eErr =
7456 4 : SQLCommand(m_poDS->GetDB(),
7457 4 : CPLString().Printf(
7458 : "ALTER TABLE \"%s\" RENAME TO \"%s\"",
7459 8 : SQLEscapeName(m_osRTreeName.c_str()).c_str(),
7460 12 : SQLEscapeName(osNewRTreeName.c_str()).c_str()));
7461 4 : if (eErr != OGRERR_NONE)
7462 : {
7463 0 : m_poDS->SoftRollbackTransaction();
7464 0 : return OGRERR_FAILURE;
7465 : }
7466 :
7467 : // Finally rename triggers (not strictly needed, but for
7468 : // consistency)
7469 4 : std::string osTriggerSQL;
7470 4 : osTriggerSQL = ReturnSQLDropSpatialIndexTriggers();
7471 4 : osTriggerSQL += ";";
7472 8 : osTriggerSQL += ReturnSQLCreateSpatialIndexTriggers(
7473 4 : nullptr, poNewGeomFieldDefn->GetNameRef());
7474 4 : eErr = SQLCommand(m_poDS->GetDB(), osTriggerSQL.c_str());
7475 4 : if (eErr != OGRERR_NONE)
7476 : {
7477 0 : m_poDS->SoftRollbackTransaction();
7478 0 : return OGRERR_FAILURE;
7479 : }
7480 : }
7481 :
7482 4 : eErr = m_poDS->SoftCommitTransaction();
7483 4 : if (eErr != OGRERR_NONE)
7484 : {
7485 0 : return OGRERR_FAILURE;
7486 : }
7487 :
7488 4 : poGeomFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
7489 :
7490 4 : if (bHasSpatialIndex)
7491 : {
7492 4 : m_osRTreeName = osNewRTreeName;
7493 : }
7494 : }
7495 :
7496 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0 ||
7497 5 : (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7498 : {
7499 4 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
7500 4 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
7501 :
7502 4 : OGRSpatialReferenceRefCountedPtr poNewSRS;
7503 4 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
7504 : {
7505 3 : if (poNewSRSRef != nullptr)
7506 : {
7507 : poNewSRS =
7508 2 : OGRSpatialReferenceRefCountedPtr::makeClone(poNewSRSRef);
7509 2 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) ==
7510 : 0)
7511 : {
7512 2 : if (poOldSRS)
7513 1 : poNewSRS->SetCoordinateEpoch(
7514 : poOldSRS->GetCoordinateEpoch());
7515 : }
7516 : }
7517 : }
7518 1 : else if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7519 : {
7520 1 : if (poOldSRS != nullptr)
7521 : {
7522 : poNewSRS =
7523 1 : OGRSpatialReferenceRefCountedPtr::makeClone(poOldSRS);
7524 1 : if (poNewSRSRef)
7525 1 : poNewSRS->SetCoordinateEpoch(
7526 : poNewSRSRef->GetCoordinateEpoch());
7527 : }
7528 : }
7529 :
7530 4 : const char *const apszOptions[] = {
7531 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
7532 4 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
7533 10 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
7534 2 : (poOldSRS != nullptr && poNewSRS != nullptr &&
7535 2 : !poOldSRS->IsSame(poNewSRS.get(), apszOptions)))
7536 : {
7537 : // Temporary remove foreign key checks
7538 : const GPKGTemporaryForeignKeyCheckDisabler
7539 4 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
7540 :
7541 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7542 0 : return OGRERR_FAILURE;
7543 :
7544 4 : const int nNewSRID = m_poDS->GetSrsId(poNewSRS.get());
7545 :
7546 : // Replace the old SRID by the new ones in geometry blobs
7547 4 : int32_t nNewSRID_LSB = nNewSRID;
7548 4 : CPL_LSBPTR32(&nNewSRID_LSB);
7549 4 : GByte abySRID_LSB[5] = {0, 0, 0, 0};
7550 4 : memcpy(abySRID_LSB, &nNewSRID_LSB, 4);
7551 4 : char *pszSRID_LSB_HEX = CPLBinaryToHex(4, abySRID_LSB);
7552 :
7553 4 : int32_t nNewSRID_MSB = nNewSRID;
7554 4 : CPL_MSBPTR32(&nNewSRID_MSB);
7555 4 : GByte abySRID_MSB[5] = {0, 0, 0, 0};
7556 4 : memcpy(abySRID_MSB, &nNewSRID_MSB, 4);
7557 4 : char *pszSRID_MSB_HEX = CPLBinaryToHex(4, abySRID_MSB);
7558 :
7559 : // Black magic below...
7560 : // the substr(hex(...) IN ('0','2',...'E') checks if bit 0 of the
7561 : // 4th byte is 0 and use that to decide how to replace the old SRID
7562 : // by the new one.
7563 4 : CPLString osSQL;
7564 : osSQL.Printf(
7565 : "UPDATE \"%s\" SET \"%s\" = "
7566 : "CAST(substr(\"%s\", 1, 4) || "
7567 : "(CASE WHEN substr(hex(substr(\"%s\", 4, 1)),2) IN "
7568 : "('0','2','4','6','8','A','C','E') "
7569 : "THEN x'%s' ELSE x'%s' END) || substr(\"%s\", 9) AS BLOB) "
7570 : "WHERE \"%s\" IS NOT NULL",
7571 8 : SQLEscapeName(m_pszTableName).c_str(),
7572 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7573 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7574 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7575 : pszSRID_MSB_HEX, pszSRID_LSB_HEX,
7576 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7577 28 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str());
7578 4 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL.c_str());
7579 4 : CPLFree(pszSRID_MSB_HEX);
7580 4 : CPLFree(pszSRID_LSB_HEX);
7581 4 : if (eErr != OGRERR_NONE)
7582 : {
7583 0 : m_poDS->SoftRollbackTransaction();
7584 0 : return OGRERR_FAILURE;
7585 : }
7586 :
7587 4 : char *pszSQL = sqlite3_mprintf(
7588 : "UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'",
7589 : nNewSRID, m_pszTableName);
7590 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7591 4 : sqlite3_free(pszSQL);
7592 4 : if (eErr != OGRERR_NONE)
7593 : {
7594 0 : m_poDS->SoftRollbackTransaction();
7595 0 : return OGRERR_FAILURE;
7596 : }
7597 :
7598 4 : pszSQL = sqlite3_mprintf(
7599 : "UPDATE gpkg_geometry_columns SET srs_id = %d WHERE "
7600 : "table_name = '%q' AND column_name = '%q'",
7601 : nNewSRID, m_pszTableName, poGeomFieldDefn->GetNameRef());
7602 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7603 4 : sqlite3_free(pszSQL);
7604 4 : if (eErr != OGRERR_NONE)
7605 : {
7606 0 : m_poDS->SoftRollbackTransaction();
7607 0 : return OGRERR_FAILURE;
7608 : }
7609 :
7610 4 : if (m_poDS->SoftCommitTransaction() != OGRERR_NONE)
7611 : {
7612 0 : return OGRERR_FAILURE;
7613 : }
7614 :
7615 4 : m_iSrs = nNewSRID;
7616 4 : poGeomFieldDefn->SetSpatialRef(poNewSRS.get());
7617 : }
7618 : }
7619 :
7620 8 : return OGRERR_NONE;
7621 : }
7622 :
7623 : /************************************************************************/
7624 : /* ReorderFields() */
7625 : /************************************************************************/
7626 :
7627 4 : OGRErr OGRGeoPackageTableLayer::ReorderFields(int *panMap)
7628 : {
7629 4 : if (!m_bFeatureDefnCompleted)
7630 0 : GetLayerDefn();
7631 4 : if (!CheckUpdatableTable("ReorderFields"))
7632 2 : return OGRERR_FAILURE;
7633 :
7634 2 : if (m_poFeatureDefn->GetFieldCount() == 0)
7635 0 : return OGRERR_NONE;
7636 :
7637 2 : OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
7638 2 : if (eErr != OGRERR_NONE)
7639 1 : return eErr;
7640 :
7641 : /* -------------------------------------------------------------------- */
7642 : /* Deferred actions, reset state. */
7643 : /* -------------------------------------------------------------------- */
7644 1 : ResetReading();
7645 1 : RunDeferredCreationIfNecessary();
7646 1 : if (m_bThreadRTreeStarted)
7647 0 : CancelAsyncRTree();
7648 1 : if (!RunDeferredSpatialIndexUpdate())
7649 0 : return OGRERR_FAILURE;
7650 :
7651 : /* -------------------------------------------------------------------- */
7652 : /* Drop any iterator since we change the DB structure */
7653 : /* -------------------------------------------------------------------- */
7654 1 : m_poDS->ResetReadingAllLayers();
7655 :
7656 : /* -------------------------------------------------------------------- */
7657 : /* Build list of old fields, and the list of new fields. */
7658 : /* -------------------------------------------------------------------- */
7659 2 : std::vector<OGRFieldDefn *> apoFields;
7660 3 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
7661 : {
7662 : OGRFieldDefn *poFieldDefn =
7663 2 : m_poFeatureDefn->GetFieldDefn(panMap[iField]);
7664 2 : apoFields.push_back(poFieldDefn);
7665 : }
7666 :
7667 2 : const CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
7668 2 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
7669 :
7670 : /* -------------------------------------------------------------------- */
7671 : /* Recreate table in a transaction */
7672 : /* -------------------------------------------------------------------- */
7673 1 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7674 0 : return OGRERR_FAILURE;
7675 :
7676 1 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
7677 :
7678 : /* -------------------------------------------------------------------- */
7679 : /* Finish */
7680 : /* -------------------------------------------------------------------- */
7681 1 : if (eErr == OGRERR_NONE)
7682 : {
7683 1 : eErr = m_poDS->SoftCommitTransaction();
7684 :
7685 1 : if (eErr == OGRERR_NONE)
7686 : {
7687 1 : eErr = whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
7688 : }
7689 :
7690 1 : ResetReading();
7691 : }
7692 : else
7693 : {
7694 0 : m_poDS->SoftRollbackTransaction();
7695 : }
7696 :
7697 1 : return eErr;
7698 : }
7699 :
7700 : /************************************************************************/
7701 : /* OGR_GPKG_GeometryTypeAggregate() */
7702 : /************************************************************************/
7703 :
7704 : namespace
7705 : {
7706 : struct GeometryTypeAggregateContext
7707 : {
7708 : sqlite3 *m_hDB = nullptr;
7709 : int m_nFlags = 0;
7710 : bool m_bIsGeometryTypeAggregateInterrupted = false;
7711 : std::map<OGRwkbGeometryType, int64_t> m_oMapCount{};
7712 : std::set<OGRwkbGeometryType> m_oSetNotNull{};
7713 :
7714 14 : explicit GeometryTypeAggregateContext(sqlite3 *hDB, int nFlags)
7715 14 : : m_hDB(hDB), m_nFlags(nFlags)
7716 : {
7717 14 : }
7718 :
7719 : GeometryTypeAggregateContext(const GeometryTypeAggregateContext &) = delete;
7720 : GeometryTypeAggregateContext &
7721 : operator=(const GeometryTypeAggregateContext &) = delete;
7722 :
7723 2 : void SetGeometryTypeAggregateInterrupted(bool b)
7724 : {
7725 2 : m_bIsGeometryTypeAggregateInterrupted = b;
7726 2 : if (b)
7727 2 : sqlite3_interrupt(m_hDB);
7728 2 : }
7729 : };
7730 :
7731 : } // namespace
7732 :
7733 1376 : static void OGR_GPKG_GeometryTypeAggregate_Step(sqlite3_context *pContext,
7734 : int /*argc*/,
7735 : sqlite3_value **argv)
7736 : {
7737 : const GByte *pabyBLOB =
7738 1376 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
7739 :
7740 : auto poContext = static_cast<GeometryTypeAggregateContext *>(
7741 1376 : sqlite3_user_data(pContext));
7742 :
7743 1376 : OGRwkbGeometryType eGeometryType = wkbNone;
7744 1376 : OGRErr err = OGRERR_FAILURE;
7745 1376 : if (pabyBLOB != nullptr)
7746 : {
7747 : GPkgHeader sHeader;
7748 1353 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
7749 2706 : if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, &sHeader) == OGRERR_NONE &&
7750 1353 : static_cast<size_t>(nBLOBLen) >= sHeader.nHeaderLen + 5)
7751 : {
7752 1353 : err = OGRReadWKBGeometryType(pabyBLOB + sHeader.nHeaderLen,
7753 : wkbVariantIso, &eGeometryType);
7754 1353 : if (eGeometryType == wkbGeometryCollection25D &&
7755 4 : (poContext->m_nFlags & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
7756 : {
7757 : auto poGeom = std::unique_ptr<OGRGeometry>(
7758 2 : GPkgGeometryToOGR(pabyBLOB, nBLOBLen, nullptr));
7759 1 : if (poGeom)
7760 : {
7761 1 : const auto poGC = poGeom->toGeometryCollection();
7762 1 : if (poGC->getNumGeometries() > 0)
7763 : {
7764 : auto eSubGeomType =
7765 1 : poGC->getGeometryRef(0)->getGeometryType();
7766 1 : if (eSubGeomType == wkbTINZ)
7767 1 : eGeometryType = wkbTINZ;
7768 : }
7769 : }
7770 : }
7771 : }
7772 : }
7773 : else
7774 : {
7775 : // NULL geometry
7776 23 : err = OGRERR_NONE;
7777 : }
7778 1376 : if (err == OGRERR_NONE)
7779 : {
7780 1376 : ++poContext->m_oMapCount[eGeometryType];
7781 1376 : if (eGeometryType != wkbNone &&
7782 1353 : (poContext->m_nFlags & OGR_GGT_STOP_IF_MIXED) != 0)
7783 : {
7784 4 : poContext->m_oSetNotNull.insert(eGeometryType);
7785 4 : if (poContext->m_oSetNotNull.size() == 2)
7786 : {
7787 2 : poContext->SetGeometryTypeAggregateInterrupted(true);
7788 : }
7789 : }
7790 : }
7791 1376 : }
7792 :
7793 11 : static void OGR_GPKG_GeometryTypeAggregate_Finalize(sqlite3_context *)
7794 : {
7795 11 : }
7796 :
7797 : /************************************************************************/
7798 : /* GetGeometryTypes() */
7799 : /************************************************************************/
7800 :
7801 15 : OGRGeometryTypeCounter *OGRGeoPackageTableLayer::GetGeometryTypes(
7802 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
7803 : GDALProgressFunc pfnProgress, void *pProgressData)
7804 : {
7805 15 : const OGRFeatureDefn *poDefn = GetLayerDefn();
7806 :
7807 : /* -------------------------------------------------------------------- */
7808 : /* Deferred actions, reset state. */
7809 : /* -------------------------------------------------------------------- */
7810 15 : RunDeferredCreationIfNecessary();
7811 15 : if (!RunDeferredSpatialIndexUpdate())
7812 : {
7813 0 : nEntryCountOut = 0;
7814 0 : return nullptr;
7815 : }
7816 :
7817 15 : const int nGeomFieldCount = poDefn->GetGeomFieldCount();
7818 15 : if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
7819 : {
7820 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
7821 1 : nEntryCountOut = 0;
7822 1 : return nullptr;
7823 : }
7824 :
7825 : #ifdef SQLITE_HAS_PROGRESS_HANDLER
7826 : struct CancelCallback
7827 : {
7828 : sqlite3 *m_hDB = nullptr;
7829 : GDALProgressFunc m_pfnProgress = nullptr;
7830 : void *m_pProgressData = nullptr;
7831 :
7832 14 : CancelCallback(sqlite3 *hDB, GDALProgressFunc pfnProgressIn,
7833 : void *pProgressDataIn)
7834 14 : : m_hDB(hDB),
7835 14 : m_pfnProgress(pfnProgressIn != GDALDummyProgress ? pfnProgressIn
7836 : : nullptr),
7837 14 : m_pProgressData(pProgressDataIn)
7838 : {
7839 14 : if (m_pfnProgress)
7840 : {
7841 : // If changing that value, update
7842 : // ogr_gpkg.py::test_ogr_gpkg_get_geometry_types
7843 2 : constexpr int COUNT_VM_INSTRUCTIONS = 1000;
7844 2 : sqlite3_progress_handler(m_hDB, COUNT_VM_INSTRUCTIONS,
7845 : ProgressHandler, this);
7846 : }
7847 14 : }
7848 :
7849 14 : ~CancelCallback()
7850 14 : {
7851 14 : if (m_pfnProgress)
7852 : {
7853 2 : sqlite3_progress_handler(m_hDB, 0, nullptr, nullptr);
7854 : }
7855 14 : }
7856 :
7857 : CancelCallback(const CancelCallback &) = delete;
7858 : CancelCallback &operator=(const CancelCallback &) = delete;
7859 :
7860 1 : static int ProgressHandler(void *pData)
7861 : {
7862 1 : CancelCallback *psCancelCallback =
7863 : static_cast<CancelCallback *>(pData);
7864 1 : return psCancelCallback->m_pfnProgress != nullptr &&
7865 1 : psCancelCallback->m_pfnProgress(
7866 : 0.0, "", psCancelCallback->m_pProgressData)
7867 2 : ? 0
7868 1 : : 1;
7869 : }
7870 : };
7871 :
7872 28 : CancelCallback oCancelCallback(m_poDS->hDB, pfnProgress, pProgressData);
7873 : #else
7874 : CPL_IGNORE_RET_VAL(pfnProgress);
7875 : CPL_IGNORE_RET_VAL(pProgressData);
7876 : #endif
7877 :
7878 : // For internal use only
7879 :
7880 28 : GeometryTypeAggregateContext sContext(m_poDS->hDB, nFlagsGGT);
7881 :
7882 28 : CPLString osFuncName;
7883 14 : osFuncName.Printf("OGR_GPKG_GeometryTypeAggregate_INTERNAL_%p", &sContext);
7884 :
7885 14 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
7886 : &sContext, nullptr,
7887 : OGR_GPKG_GeometryTypeAggregate_Step,
7888 : OGR_GPKG_GeometryTypeAggregate_Finalize);
7889 :
7890 : // Using this aggregate function is slightly faster than using
7891 : // sqlite3_step() to loop over each geometry blob (650 ms vs 750ms on a 1.6
7892 : // GB db with 3.3 million features)
7893 28 : char *pszSQL = sqlite3_mprintf(
7894 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
7895 14 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
7896 30 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
7897 14 : char *pszErrMsg = nullptr;
7898 : const int rc =
7899 14 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
7900 :
7901 : // Delete function
7902 14 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
7903 : nullptr, nullptr, nullptr, nullptr);
7904 :
7905 14 : if (rc != SQLITE_OK && !sContext.m_bIsGeometryTypeAggregateInterrupted)
7906 : {
7907 1 : if (rc != SQLITE_INTERRUPT)
7908 : {
7909 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
7910 : pszSQL, pszErrMsg);
7911 : }
7912 1 : sqlite3_free(pszErrMsg);
7913 1 : sqlite3_free(pszSQL);
7914 1 : nEntryCountOut = 0;
7915 1 : return nullptr;
7916 : }
7917 13 : sqlite3_free(pszErrMsg);
7918 13 : sqlite3_free(pszSQL);
7919 :
7920 : // Format result
7921 13 : nEntryCountOut = static_cast<int>(sContext.m_oMapCount.size());
7922 : OGRGeometryTypeCounter *pasRet = static_cast<OGRGeometryTypeCounter *>(
7923 13 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
7924 13 : int i = 0;
7925 47 : for (const auto &sEntry : sContext.m_oMapCount)
7926 : {
7927 34 : pasRet[i].eGeomType = sEntry.first;
7928 34 : pasRet[i].nCount = sEntry.second;
7929 34 : ++i;
7930 : }
7931 13 : return pasRet;
7932 : }
7933 :
7934 : /************************************************************************/
7935 : /* OGR_GPKG_FillArrowArray_Step() */
7936 : /************************************************************************/
7937 :
7938 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7939 : sqlite3_value **argv);
7940 :
7941 64974 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7942 : sqlite3_value **argv)
7943 : {
7944 : auto psFillArrowArray = static_cast<OGRGPKGTableLayerFillArrowArray *>(
7945 64974 : sqlite3_user_data(pContext));
7946 :
7947 : // To be called under psFillArrowArray->oMutex
7948 : const auto SubmitBatchToCaller =
7949 762 : [psFillArrowArray](std::unique_lock<std::mutex> &oLock)
7950 : {
7951 127 : psFillArrowArray->psHelper->Shrink(psFillArrowArray->nCountRows);
7952 127 : psFillArrowArray->bThreadReady = true;
7953 127 : psFillArrowArray->oCV.notify_one();
7954 254 : while (psFillArrowArray->nCountRows > 0)
7955 : {
7956 127 : psFillArrowArray->oCV.wait(oLock);
7957 : }
7958 65101 : };
7959 :
7960 : const auto DealWithTooBigArray =
7961 549 : [&SubmitBatchToCaller, psFillArrowArray](int iFeat)
7962 : {
7963 134 : CPLDebug("GPKG",
7964 : "OGR_GPKG_FillArrowArray_Step(): premature "
7965 : "notification of %d features to consumer due "
7966 : "to too big array",
7967 : iFeat);
7968 134 : if (psFillArrowArray->bAsynchronousMode)
7969 : {
7970 242 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7971 121 : psFillArrowArray->bMemoryLimitReached = true;
7972 121 : SubmitBatchToCaller(oLock);
7973 : }
7974 : else
7975 : {
7976 : {
7977 13 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7978 13 : psFillArrowArray->bMemoryLimitReached = true;
7979 : }
7980 13 : sqlite3_interrupt(psFillArrowArray->hDB);
7981 : }
7982 134 : };
7983 :
7984 : {
7985 64974 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7986 129948 : if (psFillArrowArray->nCountRows >=
7987 64974 : psFillArrowArray->psHelper->m_nMaxBatchSize)
7988 : {
7989 6 : if (psFillArrowArray->bAsynchronousMode)
7990 : {
7991 6 : SubmitBatchToCaller(oLock);
7992 : // Note that psFillArrowArray->psHelper.get() will generally now be
7993 : // different from before the wait()
7994 : }
7995 : else
7996 : {
7997 : // should not happen !
7998 : psFillArrowArray->osErrorMsg = "OGR_GPKG_FillArrowArray_Step() "
7999 0 : "got more rows than expected!";
8000 0 : sqlite3_interrupt(psFillArrowArray->hDB);
8001 0 : psFillArrowArray->bErrorOccurred = true;
8002 0 : return;
8003 : }
8004 : }
8005 64974 : if (psFillArrowArray->nCountRows < 0)
8006 2 : return;
8007 : }
8008 :
8009 64972 : if (psFillArrowArray->nMemLimit == 0)
8010 148 : psFillArrowArray->nMemLimit = OGRArrowArrayHelper::GetMemLimit();
8011 64972 : const auto nMemLimit = psFillArrowArray->nMemLimit;
8012 : const int SQLITE_MAX_FUNCTION_ARG =
8013 64972 : sqlite3_limit(psFillArrowArray->hDB, SQLITE_LIMIT_FUNCTION_ARG, -1);
8014 64972 : const bool bDateTimeAsString = psFillArrowArray->bDateTimeAsString;
8015 65093 : begin:
8016 : int iFeat;
8017 : OGRArrowArrayHelper *psHelper;
8018 : {
8019 130186 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
8020 65093 : iFeat = psFillArrowArray->nCountRows;
8021 65093 : psHelper = psFillArrowArray->psHelper.get();
8022 : }
8023 65093 : int iCol = 0;
8024 65093 : const int iFieldStart = sqlite3_value_int(argv[iCol]);
8025 65093 : ++iCol;
8026 65093 : int iField = std::max(0, iFieldStart);
8027 :
8028 : GIntBig nFID;
8029 65093 : if (iFieldStart < 0)
8030 : {
8031 65087 : nFID = sqlite3_value_int64(argv[iCol]);
8032 65087 : iCol++;
8033 65087 : if (psHelper->m_panFIDValues)
8034 : {
8035 65068 : psHelper->m_panFIDValues[iFeat] = nFID;
8036 : }
8037 65087 : psFillArrowArray->nCurFID = nFID;
8038 : }
8039 : else
8040 : {
8041 6 : nFID = psFillArrowArray->nCurFID;
8042 : }
8043 :
8044 130175 : if (iFieldStart < 0 && !psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
8045 65082 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8046 : {
8047 65079 : const int iArrowField = psHelper->m_mapOGRGeomFieldToArrowField[0];
8048 65079 : auto psArray = psHelper->m_out_array->children[iArrowField];
8049 65079 : size_t nWKBSize = 0;
8050 65079 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
8051 65079 : if (nSqlite3ColType == SQLITE_BLOB)
8052 : {
8053 : GPkgHeader oHeader;
8054 64695 : memset(&oHeader, 0, sizeof(oHeader));
8055 :
8056 64695 : const GByte *pabyWkb = nullptr;
8057 64695 : const int nBlobSize = sqlite3_value_bytes(argv[iCol]);
8058 : // coverity[tainted_data_return]
8059 : const GByte *pabyBlob =
8060 64695 : static_cast<const GByte *>(sqlite3_value_blob(argv[iCol]));
8061 64695 : std::vector<GByte> abyWkb;
8062 64695 : if (nBlobSize >= 8 && pabyBlob && pabyBlob[0] == 'G' &&
8063 64694 : pabyBlob[1] == 'P')
8064 : {
8065 64694 : if (psFillArrowArray->poLayer->m_bUndoDiscardCoordLSBOnReading)
8066 : {
8067 : OGRGeometry *poGeomPtr =
8068 1 : GPkgGeometryToOGR(pabyBlob, nBlobSize, nullptr);
8069 1 : if (poGeomPtr)
8070 : {
8071 1 : poGeomPtr->roundCoordinates(
8072 1 : psFillArrowArray->poFeatureDefn->GetGeomFieldDefn(0)
8073 : ->GetCoordinatePrecision());
8074 1 : nWKBSize = poGeomPtr->WkbSize();
8075 1 : abyWkb.resize(nWKBSize);
8076 1 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
8077 1 : wkbVariantIso) !=
8078 : OGRERR_NONE)
8079 : {
8080 0 : nWKBSize = 0;
8081 : }
8082 : else
8083 : {
8084 1 : pabyWkb = abyWkb.data();
8085 : }
8086 1 : delete poGeomPtr;
8087 : }
8088 : }
8089 : else
8090 : {
8091 : /* Read header */
8092 : OGRErr err =
8093 64693 : GPkgHeaderFromWKB(pabyBlob, nBlobSize, &oHeader);
8094 64693 : if (err == OGRERR_NONE)
8095 : {
8096 : /* WKB pointer */
8097 64693 : pabyWkb = pabyBlob + oHeader.nHeaderLen;
8098 64693 : nWKBSize = nBlobSize - oHeader.nHeaderLen;
8099 : }
8100 64694 : }
8101 : }
8102 1 : else if (nBlobSize > 0 && pabyBlob)
8103 : {
8104 : // Try also spatialite geometry blobs, although that is
8105 : // not really expected...
8106 1 : OGRGeometry *poGeomPtr = nullptr;
8107 1 : if (OGRSQLiteImportSpatiaLiteGeometry(
8108 1 : pabyBlob, nBlobSize, &poGeomPtr) != OGRERR_NONE)
8109 : {
8110 1 : CPLError(CE_Failure, CPLE_AppDefined,
8111 : "Unable to read geometry");
8112 : }
8113 : else
8114 : {
8115 0 : nWKBSize = poGeomPtr->WkbSize();
8116 0 : abyWkb.resize(nWKBSize);
8117 0 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
8118 0 : wkbVariantIso) != OGRERR_NONE)
8119 : {
8120 0 : nWKBSize = 0;
8121 : }
8122 : else
8123 : {
8124 0 : pabyWkb = abyWkb.data();
8125 : }
8126 : }
8127 1 : delete poGeomPtr;
8128 : }
8129 :
8130 64695 : if (nWKBSize != 0)
8131 : {
8132 : // Deal with spatial filter
8133 64694 : if (psFillArrowArray->poLayerForFilterGeom)
8134 : {
8135 33 : OGREnvelope sEnvelope;
8136 33 : bool bEnvelopeAlreadySet = false;
8137 33 : if (oHeader.bEmpty)
8138 : {
8139 0 : bEnvelopeAlreadySet = true;
8140 : }
8141 33 : else if (oHeader.bExtentHasXY)
8142 : {
8143 31 : bEnvelopeAlreadySet = true;
8144 31 : sEnvelope.MinX = oHeader.MinX;
8145 31 : sEnvelope.MinY = oHeader.MinY;
8146 31 : sEnvelope.MaxX = oHeader.MaxX;
8147 31 : sEnvelope.MaxY = oHeader.MaxY;
8148 : }
8149 :
8150 66 : if (!psFillArrowArray->poLayerForFilterGeom
8151 33 : ->FilterWKBGeometry(pabyWkb, nWKBSize,
8152 : bEnvelopeAlreadySet,
8153 : sEnvelope))
8154 : {
8155 2 : return;
8156 : }
8157 : }
8158 :
8159 64692 : if (iFeat > 0)
8160 : {
8161 64526 : auto panOffsets = static_cast<int32_t *>(
8162 64526 : const_cast<void *>(psArray->buffers[1]));
8163 64526 : const uint32_t nCurLength =
8164 64526 : static_cast<uint32_t>(panOffsets[iFeat]);
8165 64526 : if (nWKBSize <= nMemLimit &&
8166 64526 : nWKBSize > nMemLimit - nCurLength)
8167 : {
8168 52 : DealWithTooBigArray(iFeat);
8169 52 : if (psFillArrowArray->bAsynchronousMode)
8170 : {
8171 47 : goto begin;
8172 : }
8173 : else
8174 : {
8175 5 : return;
8176 : }
8177 : }
8178 : }
8179 :
8180 64640 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8181 : iArrowField, iFeat, nWKBSize);
8182 64640 : if (outPtr == nullptr)
8183 : {
8184 0 : goto error;
8185 : }
8186 64640 : memcpy(outPtr, pabyWkb, nWKBSize);
8187 : }
8188 : else
8189 : {
8190 1 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8191 : }
8192 : }
8193 :
8194 65025 : if (nWKBSize == 0)
8195 : {
8196 385 : if (!psHelper->SetNull(iArrowField, iFeat))
8197 : {
8198 0 : goto error;
8199 : }
8200 : }
8201 65025 : iCol++;
8202 : }
8203 :
8204 1268260 : for (; iField < psHelper->m_nFieldCount; iField++)
8205 : {
8206 1203310 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8207 1203310 : if (iArrowField < 0)
8208 73 : continue;
8209 1203240 : if (iCol == SQLITE_MAX_FUNCTION_ARG)
8210 6 : break;
8211 :
8212 : const OGRFieldDefn *poFieldDefn =
8213 1203240 : psFillArrowArray->poFeatureDefn->GetFieldDefnUnsafe(iField);
8214 :
8215 1203240 : auto psArray = psHelper->m_out_array->children[iArrowField];
8216 :
8217 1203240 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
8218 1203240 : if (nSqlite3ColType == SQLITE_NULL)
8219 : {
8220 784 : if (!psHelper->SetNull(iArrowField, iFeat))
8221 : {
8222 0 : goto error;
8223 : }
8224 784 : iCol++;
8225 784 : continue;
8226 : }
8227 :
8228 1202450 : switch (poFieldDefn->GetType())
8229 : {
8230 1245 : case OFTInteger:
8231 : {
8232 1245 : const int nVal = sqlite3_value_int(argv[iCol]);
8233 1245 : if (poFieldDefn->GetSubType() == OFSTBoolean)
8234 : {
8235 122 : if (nVal != 0)
8236 : {
8237 91 : psHelper->SetBoolOn(psArray, iFeat);
8238 : }
8239 : }
8240 1123 : else if (poFieldDefn->GetSubType() == OFSTInt16)
8241 : {
8242 59 : psHelper->SetInt16(psArray, iFeat,
8243 : static_cast<int16_t>(nVal));
8244 : }
8245 : else
8246 : {
8247 1064 : psHelper->SetInt32(psArray, iFeat, nVal);
8248 : }
8249 1245 : break;
8250 : }
8251 :
8252 220 : case OFTInteger64:
8253 : {
8254 220 : psHelper->SetInt64(psArray, iFeat,
8255 220 : sqlite3_value_int64(argv[iCol]));
8256 220 : break;
8257 : }
8258 :
8259 301 : case OFTReal:
8260 : {
8261 301 : const double dfVal = sqlite3_value_double(argv[iCol]);
8262 301 : if (poFieldDefn->GetSubType() == OFSTFloat32)
8263 : {
8264 51 : psHelper->SetFloat(psArray, iFeat,
8265 : static_cast<float>(dfVal));
8266 : }
8267 : else
8268 : {
8269 250 : psHelper->SetDouble(psArray, iFeat, dfVal);
8270 : }
8271 301 : break;
8272 : }
8273 :
8274 176 : case OFTBinary:
8275 : {
8276 : const uint32_t nBytes =
8277 176 : static_cast<uint32_t>(sqlite3_value_bytes(argv[iCol]));
8278 : // coverity[tainted_data_return]
8279 176 : const void *pabyData = sqlite3_value_blob(argv[iCol]);
8280 176 : if (pabyData != nullptr || nBytes == 0)
8281 : {
8282 176 : if (iFeat > 0)
8283 : {
8284 119 : auto panOffsets = static_cast<int32_t *>(
8285 119 : const_cast<void *>(psArray->buffers[1]));
8286 119 : const uint32_t nCurLength =
8287 119 : static_cast<uint32_t>(panOffsets[iFeat]);
8288 119 : if (nBytes <= nMemLimit &&
8289 119 : nBytes > nMemLimit - nCurLength)
8290 : {
8291 41 : DealWithTooBigArray(iFeat);
8292 41 : if (psFillArrowArray->bAsynchronousMode)
8293 : {
8294 37 : goto begin;
8295 : }
8296 : else
8297 : {
8298 4 : return;
8299 : }
8300 : }
8301 : }
8302 :
8303 135 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8304 : iArrowField, iFeat, nBytes);
8305 135 : if (outPtr == nullptr)
8306 : {
8307 0 : goto error;
8308 : }
8309 135 : if (nBytes)
8310 135 : memcpy(outPtr, pabyData, nBytes);
8311 : }
8312 : else
8313 : {
8314 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8315 : }
8316 135 : break;
8317 : }
8318 :
8319 51 : case OFTDate:
8320 : {
8321 : OGRField ogrField;
8322 : const auto pszTxt = reinterpret_cast<const char *>(
8323 51 : sqlite3_value_text(argv[iCol]));
8324 102 : if (pszTxt != nullptr &&
8325 51 : psFillArrowArray->poLayer->ParseDateField(
8326 : pszTxt, &ogrField, poFieldDefn, nFID))
8327 : {
8328 51 : psHelper->SetDate(psArray, iFeat,
8329 51 : psFillArrowArray->brokenDown, ogrField);
8330 : }
8331 51 : break;
8332 : }
8333 :
8334 65 : case OFTDateTime:
8335 : {
8336 65 : if (!bDateTimeAsString)
8337 : {
8338 : OGRField ogrField;
8339 : const auto pszTxt = reinterpret_cast<const char *>(
8340 55 : sqlite3_value_text(argv[iCol]));
8341 110 : if (pszTxt != nullptr &&
8342 55 : psFillArrowArray->poLayer->ParseDateTimeField(
8343 : pszTxt, &ogrField, poFieldDefn, nFID))
8344 : {
8345 55 : psHelper->SetDateTime(
8346 55 : psArray, iFeat, psFillArrowArray->brokenDown,
8347 55 : psHelper->m_anTZFlags[iField], ogrField);
8348 : }
8349 55 : break;
8350 : }
8351 : else
8352 : {
8353 : [[fallthrough]];
8354 : }
8355 : }
8356 :
8357 : case OFTString:
8358 : {
8359 : const auto pszTxt = reinterpret_cast<const char *>(
8360 1200400 : sqlite3_value_text(argv[iCol]));
8361 1200400 : if (pszTxt != nullptr)
8362 : {
8363 1200400 : const size_t nBytes = strlen(pszTxt);
8364 1200400 : if (iFeat > 0)
8365 : {
8366 1200280 : auto panOffsets = static_cast<int32_t *>(
8367 1200280 : const_cast<void *>(psArray->buffers[1]));
8368 1200280 : const uint32_t nCurLength =
8369 1200280 : static_cast<uint32_t>(panOffsets[iFeat]);
8370 1200280 : if (nBytes <= nMemLimit &&
8371 1200280 : nBytes > nMemLimit - nCurLength)
8372 : {
8373 41 : DealWithTooBigArray(iFeat);
8374 41 : if (psFillArrowArray->bAsynchronousMode)
8375 : {
8376 37 : goto begin;
8377 : }
8378 : else
8379 : {
8380 4 : return;
8381 : }
8382 : }
8383 : }
8384 :
8385 1200360 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8386 : iArrowField, iFeat, nBytes);
8387 1200360 : if (outPtr == nullptr)
8388 : {
8389 0 : goto error;
8390 : }
8391 1200360 : if (nBytes)
8392 1200360 : memcpy(outPtr, pszTxt, nBytes);
8393 : }
8394 : else
8395 : {
8396 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8397 : }
8398 1200360 : break;
8399 : }
8400 :
8401 0 : default:
8402 0 : break;
8403 : }
8404 :
8405 1202370 : iCol++;
8406 : }
8407 :
8408 64957 : if (iField == psHelper->m_nFieldCount)
8409 : {
8410 64951 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
8411 64951 : psFillArrowArray->nCountRows++;
8412 : }
8413 64957 : return;
8414 :
8415 0 : error:
8416 0 : sqlite3_interrupt(psFillArrowArray->hDB);
8417 : }
8418 :
8419 : /************************************************************************/
8420 : /* OGR_GPKG_FillArrowArray_Finalize() */
8421 : /************************************************************************/
8422 :
8423 157 : static void OGR_GPKG_FillArrowArray_Finalize(sqlite3_context * /*pContext*/)
8424 : {
8425 157 : }
8426 :
8427 : /************************************************************************/
8428 : /* GetNextArrowArrayAsynchronous() */
8429 : /************************************************************************/
8430 :
8431 202 : int OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronous(
8432 : struct ArrowArrayStream *stream, struct ArrowArray *out_array)
8433 : {
8434 202 : memset(out_array, 0, sizeof(*out_array));
8435 :
8436 202 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8437 :
8438 202 : if (m_poFillArrowArray)
8439 : {
8440 150 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8441 150 : if (m_poFillArrowArray->bIsFinished)
8442 : {
8443 25 : return 0;
8444 : }
8445 : }
8446 :
8447 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
8448 354 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
8449 177 : if (out_array->release == nullptr)
8450 : {
8451 0 : return ENOMEM;
8452 : }
8453 :
8454 177 : if (m_poFillArrowArray == nullptr)
8455 : {
8456 : // Check that the total number of arguments passed to
8457 : // OGR_GPKG_FillArrowArray_INTERNAL() doesn't exceed SQLITE_MAX_FUNCTION_ARG
8458 : // If it does, we cannot reliably use GetNextArrowArrayAsynchronous() in
8459 : // the situation where the ArrowArray would exceed the nMemLimit.
8460 : // So be on the safe side, and rely on the base OGRGeoPackageLayer
8461 : // implementation
8462 : const int SQLITE_MAX_FUNCTION_ARG =
8463 52 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
8464 52 : int nCountArgs = 1 // field index
8465 : + 1; // FID column
8466 101 : if (!psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
8467 49 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8468 : {
8469 49 : ++nCountArgs;
8470 : }
8471 472 : for (int iField = 0; iField < psHelper->m_nFieldCount; iField++)
8472 : {
8473 422 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8474 422 : if (iArrowField >= 0)
8475 : {
8476 422 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
8477 : {
8478 2 : psHelper.reset();
8479 2 : if (out_array->release)
8480 2 : out_array->release(out_array);
8481 2 : return OGRGeoPackageLayer::GetNextArrowArray(stream,
8482 2 : out_array);
8483 : }
8484 420 : ++nCountArgs;
8485 : }
8486 : }
8487 :
8488 : m_poFillArrowArray =
8489 50 : std::make_unique<OGRGPKGTableLayerFillArrowArray>();
8490 50 : m_poFillArrowArray->psHelper = std::move(psHelper);
8491 100 : m_poFillArrowArray->bDateTimeAsString =
8492 50 : m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
8493 : false);
8494 50 : m_poFillArrowArray->poFeatureDefn = m_poFeatureDefn;
8495 50 : m_poFillArrowArray->poLayer = this;
8496 50 : m_poFillArrowArray->hDB = m_poDS->GetDB();
8497 50 : memset(&m_poFillArrowArray->brokenDown, 0,
8498 : sizeof(m_poFillArrowArray->brokenDown));
8499 50 : m_poFillArrowArray->bAsynchronousMode = true;
8500 50 : if (m_poFilterGeom)
8501 10 : m_poFillArrowArray->poLayerForFilterGeom = this;
8502 :
8503 : try
8504 : {
8505 100 : m_oThreadNextArrowArray = std::thread(
8506 100 : [this]() { GetNextArrowArrayAsynchronousWorker(); });
8507 : }
8508 0 : catch (const std::exception &e)
8509 : {
8510 0 : m_poFillArrowArray.reset();
8511 0 : CPLError(CE_Failure, CPLE_AppDefined,
8512 0 : "Cannot start worker thread: %s", e.what());
8513 0 : out_array->release(out_array);
8514 0 : return ENOMEM;
8515 : }
8516 : }
8517 : else
8518 : {
8519 125 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8520 125 : if (m_poFillArrowArray->bErrorOccurred)
8521 : {
8522 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8523 0 : m_poFillArrowArray->osErrorMsg.c_str());
8524 0 : out_array->release(out_array);
8525 0 : return EIO;
8526 : }
8527 :
8528 : // Resume worker thread
8529 125 : m_poFillArrowArray->psHelper = std::move(psHelper);
8530 125 : m_poFillArrowArray->nCountRows = 0;
8531 125 : m_poFillArrowArray->bThreadReady = false;
8532 125 : m_poFillArrowArray->bMemoryLimitReached = false;
8533 125 : m_poFillArrowArray->oCV.notify_one();
8534 : }
8535 :
8536 : // Wait for GetNextArrowArrayAsynchronousWorker() /
8537 : // OGR_GPKG_FillArrowArray_Step() to have generated a result set (or an
8538 : // error)
8539 : bool bIsFinished;
8540 350 : std::string osErrorMsg;
8541 : {
8542 350 : std::unique_lock<std::mutex> oLock(m_poFillArrowArray->oMutex);
8543 343 : while (!m_poFillArrowArray->bThreadReady)
8544 : {
8545 168 : m_poFillArrowArray->oCV.wait(oLock);
8546 : }
8547 175 : CPLAssert(m_poFillArrowArray->bErrorOccurred ||
8548 : m_poFillArrowArray->bMemoryLimitReached ||
8549 : m_poFillArrowArray->bIsFinished ||
8550 : m_poFillArrowArray->nCountRows ==
8551 : m_poFillArrowArray->psHelper->m_nMaxBatchSize);
8552 175 : bIsFinished = m_poFillArrowArray->bIsFinished;
8553 175 : osErrorMsg = m_poFillArrowArray->osErrorMsg;
8554 : }
8555 :
8556 175 : if (m_poFillArrowArray->bErrorOccurred)
8557 : {
8558 2 : m_oThreadNextArrowArray.join();
8559 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
8560 2 : m_poFillArrowArray->psHelper->ClearArray();
8561 2 : return EIO;
8562 : }
8563 173 : else if (bIsFinished)
8564 : {
8565 46 : m_oThreadNextArrowArray.join();
8566 : }
8567 :
8568 173 : return 0;
8569 : }
8570 :
8571 : /************************************************************************/
8572 : /* GetNextArrowArrayAsynchronousWorker() */
8573 : /************************************************************************/
8574 :
8575 50 : void OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronousWorker()
8576 : {
8577 50 : sqlite3_create_function(
8578 50 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
8579 50 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, m_poFillArrowArray.get(), nullptr,
8580 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
8581 :
8582 100 : std::string osSQL;
8583 50 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
8584 :
8585 1752 : const auto AddFields = [this, &osSQL]()
8586 : {
8587 54 : if (m_pszFidColumn)
8588 : {
8589 51 : osSQL += "m.\"";
8590 51 : osSQL += SQLEscapeName(m_pszFidColumn);
8591 51 : osSQL += '"';
8592 : }
8593 : else
8594 : {
8595 3 : osSQL += "NULL";
8596 : }
8597 :
8598 54 : if (!m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField
8599 105 : .empty() &&
8600 51 : m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8601 : {
8602 51 : osSQL += ",m.\"";
8603 51 : osSQL += SQLEscapeName(GetGeometryColumn());
8604 51 : osSQL += '"';
8605 : }
8606 242 : for (int iField = 0;
8607 242 : iField < m_poFillArrowArray->psHelper->m_nFieldCount; iField++)
8608 : {
8609 : const int iArrowField =
8610 188 : m_poFillArrowArray->psHelper->m_mapOGRFieldToArrowField[iField];
8611 188 : if (iArrowField >= 0)
8612 : {
8613 : const OGRFieldDefn *poFieldDefn =
8614 188 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
8615 188 : osSQL += ",m.\"";
8616 188 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
8617 188 : osSQL += '"';
8618 : }
8619 : }
8620 54 : };
8621 :
8622 50 : AddFields();
8623 :
8624 50 : osSQL += ") FROM ";
8625 50 : if (m_iNextShapeId > 0)
8626 : {
8627 4 : osSQL += "(SELECT ";
8628 4 : AddFields();
8629 4 : osSQL += " FROM ";
8630 : }
8631 50 : osSQL += '\"';
8632 50 : osSQL += SQLEscapeName(m_pszTableName);
8633 50 : osSQL += "\" m";
8634 50 : if (!m_soFilter.empty())
8635 : {
8636 35 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
8637 10 : HasSpatialIndex())
8638 : {
8639 10 : OGREnvelope sEnvelope;
8640 :
8641 10 : m_poFilterGeom->getEnvelope(&sEnvelope);
8642 :
8643 10 : bool bUseSpatialIndex = true;
8644 20 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
8645 1 : sEnvelope.MinY <= m_poExtent->MinY &&
8646 21 : sEnvelope.MaxX >= m_poExtent->MaxX &&
8647 1 : sEnvelope.MaxY >= m_poExtent->MaxY)
8648 : {
8649 : // Selecting from spatial filter on whole extent can be rather
8650 : // slow. So use function based filtering, just in case the
8651 : // advertized global extent might be wrong. Otherwise we might
8652 : // just discard completely the spatial filter.
8653 1 : bUseSpatialIndex = false;
8654 : }
8655 :
8656 9 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
8657 28 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
8658 9 : !std::isinf(sEnvelope.MaxY))
8659 : {
8660 : osSQL +=
8661 : CPLSPrintf(" JOIN \"%s\" r "
8662 : "ON m.\"%s\" = r.id WHERE "
8663 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
8664 : "r.maxy >= %.12f AND r.miny <= %.12f",
8665 18 : SQLEscapeName(m_osRTreeName).c_str(),
8666 18 : SQLEscapeName(m_osFIDForRTree).c_str(),
8667 9 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
8668 27 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
8669 : }
8670 : }
8671 : else
8672 : {
8673 15 : osSQL += " WHERE ";
8674 15 : osSQL += m_soFilter;
8675 : }
8676 : }
8677 :
8678 50 : if (m_iNextShapeId > 0)
8679 : osSQL +=
8680 4 : CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB ") m", m_iNextShapeId);
8681 :
8682 : // CPLDebug("GPKG", "%s", osSQL.c_str());
8683 :
8684 50 : char *pszErrMsg = nullptr;
8685 100 : std::string osErrorMsg;
8686 50 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
8687 50 : &pszErrMsg) != SQLITE_OK)
8688 : {
8689 2 : osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
8690 : }
8691 50 : sqlite3_free(pszErrMsg);
8692 :
8693 : // Delete function
8694 50 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
8695 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
8696 : nullptr, nullptr, nullptr);
8697 :
8698 100 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8699 50 : m_poFillArrowArray->bIsFinished = true;
8700 50 : m_poFillArrowArray->bErrorOccurred = !osErrorMsg.empty();
8701 50 : m_poFillArrowArray->osErrorMsg = std::move(osErrorMsg);
8702 :
8703 50 : if (m_poFillArrowArray->nCountRows >= 0)
8704 : {
8705 48 : m_poFillArrowArray->psHelper->Shrink(m_poFillArrowArray->nCountRows);
8706 48 : if (m_poFillArrowArray->nCountRows == 0)
8707 : {
8708 22 : m_poFillArrowArray->psHelper->ClearArray();
8709 : }
8710 : }
8711 50 : m_poFillArrowArray->bThreadReady = true;
8712 50 : m_poFillArrowArray->oCV.notify_one();
8713 50 : }
8714 :
8715 : /************************************************************************/
8716 : /* GetNextArrowArray() */
8717 : /************************************************************************/
8718 :
8719 370 : int OGRGeoPackageTableLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
8720 : struct ArrowArray *out_array)
8721 : {
8722 370 : if (!m_bFeatureDefnCompleted)
8723 1 : GetLayerDefn();
8724 370 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
8725 : {
8726 0 : memset(out_array, 0, sizeof(*out_array));
8727 0 : return EIO;
8728 : }
8729 :
8730 370 : if (m_poFilterGeom != nullptr)
8731 : {
8732 : // Both are exclusive
8733 18 : CreateSpatialIndexIfNecessary();
8734 18 : if (!RunDeferredSpatialIndexUpdate())
8735 : {
8736 0 : memset(out_array, 0, sizeof(*out_array));
8737 0 : return EIO;
8738 : }
8739 : }
8740 :
8741 370 : if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
8742 : {
8743 6 : return OGRGeoPackageLayer::GetNextArrowArray(stream, out_array);
8744 : }
8745 :
8746 959 : if (m_nIsCompatOfOptimizedGetNextArrowArray == FALSE ||
8747 413 : m_pszFidColumn == nullptr || !m_soFilter.empty() ||
8748 777 : m_poFillArrowArray ||
8749 181 : (!m_bGetNextArrowArrayCalledSinceResetReading && m_iNextShapeId > 0))
8750 : {
8751 184 : return GetNextArrowArrayAsynchronous(stream, out_array);
8752 : }
8753 :
8754 : // We can use this optimized version only if there is no hole in FID
8755 : // numbering. That is min(fid) == 1 and max(fid) == m_nTotalFeatureCount
8756 180 : if (m_nIsCompatOfOptimizedGetNextArrowArray < 0)
8757 : {
8758 72 : m_nIsCompatOfOptimizedGetNextArrowArray = FALSE;
8759 72 : const auto nTotalFeatureCount = GetTotalFeatureCount();
8760 72 : if (nTotalFeatureCount < 0)
8761 3 : return GetNextArrowArrayAsynchronous(stream, out_array);
8762 : {
8763 69 : char *pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
8764 : m_pszFidColumn, m_pszTableName);
8765 : OGRErr err;
8766 69 : const auto nMaxFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8767 69 : sqlite3_free(pszSQL);
8768 69 : if (nMaxFID != nTotalFeatureCount)
8769 3 : return GetNextArrowArrayAsynchronous(stream, out_array);
8770 : }
8771 : {
8772 66 : char *pszSQL = sqlite3_mprintf("SELECT MIN(\"%w\") FROM \"%w\"",
8773 : m_pszFidColumn, m_pszTableName);
8774 : OGRErr err;
8775 66 : const auto nMinFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8776 66 : sqlite3_free(pszSQL);
8777 66 : if (nMinFID != 1)
8778 12 : return GetNextArrowArrayAsynchronous(stream, out_array);
8779 : }
8780 54 : m_nIsCompatOfOptimizedGetNextArrowArray = TRUE;
8781 : }
8782 :
8783 162 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8784 :
8785 : // CPLDebug("GPKG", "m_iNextShapeId = " CPL_FRMT_GIB, m_iNextShapeId);
8786 :
8787 324 : const int nMaxBatchSize = OGRArrowArrayHelper::GetMaxFeaturesInBatch(
8788 162 : m_aosArrowArrayStreamOptions);
8789 :
8790 : // Fetch the answer from a potentially queued asynchronous task
8791 162 : if (!m_oQueueArrowArrayPrefetchTasks.empty())
8792 : {
8793 44 : const size_t nTasks = m_oQueueArrowArrayPrefetchTasks.size();
8794 44 : auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
8795 44 : m_oQueueArrowArrayPrefetchTasks.pop();
8796 :
8797 : // Wait for thread to be ready
8798 : {
8799 44 : std::unique_lock<std::mutex> oLock(task->m_oMutex);
8800 53 : while (!task->m_bArrayReady)
8801 : {
8802 9 : task->m_oCV.wait(oLock);
8803 : }
8804 44 : task->m_bArrayReady = false;
8805 : }
8806 44 : if (!task->m_osErrorMsg.empty())
8807 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8808 0 : task->m_osErrorMsg.c_str());
8809 :
8810 145 : const auto stopThread = [&task]()
8811 : {
8812 : {
8813 58 : std::lock_guard oLock(task->m_oMutex);
8814 29 : task->m_bStop = true;
8815 29 : task->m_oCV.notify_one();
8816 : }
8817 29 : if (task->m_oThread.joinable())
8818 29 : task->m_oThread.join();
8819 29 : };
8820 :
8821 44 : if (task->m_iStartShapeId != m_iNextShapeId)
8822 : {
8823 : // Should not normally happen, unless the user messes with
8824 : // GetNextFeature()
8825 0 : CPLError(CE_Failure, CPLE_AppDefined,
8826 : "Worker thread task has not expected m_iStartShapeId "
8827 : "value. Got " CPL_FRMT_GIB ", expected " CPL_FRMT_GIB,
8828 0 : task->m_iStartShapeId, m_iNextShapeId);
8829 0 : if (task->m_psArrowArray->release)
8830 0 : task->m_psArrowArray->release(task->m_psArrowArray.get());
8831 :
8832 0 : stopThread();
8833 : }
8834 44 : else if (task->m_psArrowArray->release)
8835 : {
8836 40 : m_iNextShapeId += task->m_psArrowArray->length;
8837 :
8838 : // Transfer the task ArrowArray to the client array
8839 40 : memcpy(out_array, task->m_psArrowArray.get(),
8840 : sizeof(struct ArrowArray));
8841 40 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8842 :
8843 80 : const bool bMemoryLimitReached = [&task]()
8844 : {
8845 40 : std::unique_lock oLock(task->m_oMutex);
8846 80 : return task->m_bMemoryLimitReached;
8847 40 : }();
8848 :
8849 40 : if (bMemoryLimitReached)
8850 : {
8851 2 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
8852 2 : stopThread();
8853 2 : CancelAsyncNextArrowArray();
8854 2 : return 0;
8855 : }
8856 : // Are the records still available for reading beyond the current
8857 : // queued tasks ? If so, recycle this task to read them
8858 38 : else if (task->m_iStartShapeId +
8859 38 : static_cast<GIntBig>(nTasks) * nMaxBatchSize <=
8860 38 : m_nTotalFeatureCount)
8861 : {
8862 15 : task->m_iStartShapeId +=
8863 15 : static_cast<GIntBig>(nTasks) * nMaxBatchSize;
8864 15 : task->m_poLayer->m_iNextShapeId = task->m_iStartShapeId;
8865 : try
8866 : {
8867 : // Wake-up thread with new task
8868 : {
8869 30 : std::lock_guard oLock(task->m_oMutex);
8870 15 : task->m_bFetchRows = true;
8871 15 : task->m_oCV.notify_one();
8872 : }
8873 15 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
8874 15 : return 0;
8875 : }
8876 0 : catch (const std::exception &e)
8877 : {
8878 0 : CPLError(CE_Failure, CPLE_AppDefined,
8879 0 : "Cannot start worker thread: %s", e.what());
8880 : }
8881 : }
8882 : else
8883 : {
8884 23 : stopThread();
8885 23 : return 0;
8886 : }
8887 : }
8888 :
8889 4 : stopThread();
8890 : }
8891 :
8892 32 : const auto GetThreadsAvailable = []()
8893 : {
8894 : const char *pszMaxThreads =
8895 32 : CPLGetConfigOption("OGR_GPKG_NUM_THREADS", nullptr);
8896 32 : if (pszMaxThreads == nullptr)
8897 32 : return std::min(4, CPLGetNumCPUs());
8898 0 : else if (EQUAL(pszMaxThreads, "ALL_CPUS"))
8899 0 : return CPLGetNumCPUs();
8900 : else
8901 0 : return atoi(pszMaxThreads);
8902 : };
8903 :
8904 : // Start asynchronous tasks to prefetch the next ArrowArray
8905 211 : if (m_poDS->GetAccess() == GA_ReadOnly &&
8906 89 : m_oQueueArrowArrayPrefetchTasks.empty() &&
8907 89 : m_iNextShapeId + 2 * static_cast<GIntBig>(nMaxBatchSize) <=
8908 89 : m_nTotalFeatureCount &&
8909 227 : sqlite3_threadsafe() != 0 && GetThreadsAvailable() >= 2 &&
8910 16 : CPLGetUsablePhysicalRAM() > 1024 * 1024 * 1024)
8911 : {
8912 16 : const int nMaxTasks = static_cast<int>(std::min<GIntBig>(
8913 16 : DIV_ROUND_UP(m_nTotalFeatureCount - nMaxBatchSize - m_iNextShapeId,
8914 : nMaxBatchSize),
8915 32 : GetThreadsAvailable()));
8916 16 : CPLDebug("GPKG", "Using %d threads", nMaxTasks);
8917 32 : GDALOpenInfo oOpenInfo(m_poDS->GetDescription(), GA_ReadOnly);
8918 16 : oOpenInfo.papszOpenOptions = m_poDS->GetOpenOptions();
8919 16 : oOpenInfo.nOpenFlags = GDAL_OF_VECTOR;
8920 59 : for (int iTask = 0; iTask < nMaxTasks; ++iTask)
8921 : {
8922 43 : auto task = std::make_unique<ArrowArrayPrefetchTask>();
8923 86 : task->m_iStartShapeId =
8924 43 : m_iNextShapeId +
8925 43 : static_cast<GIntBig>(iTask + 1) * nMaxBatchSize;
8926 43 : task->m_poDS = std::make_unique<GDALGeoPackageDataset>();
8927 43 : if (!task->m_poDS->Open(&oOpenInfo, m_poDS->m_osFilenameInZip))
8928 : {
8929 0 : break;
8930 : }
8931 0 : auto poOtherLayer = dynamic_cast<OGRGeoPackageTableLayer *>(
8932 43 : task->m_poDS->GetLayerByName(GetName()));
8933 86 : if (poOtherLayer == nullptr ||
8934 43 : poOtherLayer->GetLayerDefn()->GetFieldCount() !=
8935 43 : m_poFeatureDefn->GetFieldCount())
8936 : {
8937 0 : break;
8938 : }
8939 :
8940 : // Install query logging callback
8941 43 : if (m_poDS->pfnQueryLoggerFunc)
8942 : {
8943 0 : task->m_poDS->SetQueryLoggerFunc(m_poDS->pfnQueryLoggerFunc,
8944 0 : m_poDS->poQueryLoggerArg);
8945 : }
8946 :
8947 43 : task->m_poLayer = poOtherLayer;
8948 43 : task->m_psArrowArray = std::make_unique<struct ArrowArray>();
8949 43 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8950 :
8951 43 : poOtherLayer->m_nTotalFeatureCount = m_nTotalFeatureCount;
8952 : poOtherLayer->m_aosArrowArrayStreamOptions =
8953 43 : m_aosArrowArrayStreamOptions;
8954 43 : OGRLayer *poOtherLayerAsLayer = poOtherLayer;
8955 43 : auto poOtherFDefn = poOtherLayerAsLayer->GetLayerDefn();
8956 86 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
8957 : {
8958 86 : poOtherFDefn->GetGeomFieldDefn(i)->SetIgnored(
8959 43 : m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored());
8960 : }
8961 127 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
8962 : {
8963 168 : poOtherFDefn->GetFieldDefn(i)->SetIgnored(
8964 84 : m_poFeatureDefn->GetFieldDefn(i)->IsIgnored());
8965 : }
8966 :
8967 43 : poOtherLayer->m_iNextShapeId = task->m_iStartShapeId;
8968 :
8969 43 : auto taskPtr = task.get();
8970 456 : auto taskRunner = [taskPtr]()
8971 : {
8972 86 : std::unique_lock oLock(taskPtr->m_oMutex);
8973 15 : do
8974 : {
8975 58 : taskPtr->m_bFetchRows = false;
8976 58 : taskPtr->m_poLayer->GetNextArrowArrayInternal(
8977 58 : taskPtr->m_psArrowArray.get(), taskPtr->m_osErrorMsg,
8978 58 : taskPtr->m_bMemoryLimitReached);
8979 58 : taskPtr->m_bArrayReady = true;
8980 58 : taskPtr->m_oCV.notify_one();
8981 58 : if (taskPtr->m_bMemoryLimitReached)
8982 12 : break;
8983 : // cppcheck-suppress knownConditionTrueFalse
8984 : // Coverity apparently is confused by the fact that we
8985 : // use unique_lock here to guard access for m_bStop whereas
8986 : // in other places we use a lock_guard, but there's nothing
8987 : // wrong.
8988 : // coverity[missing_lock:FALSE]
8989 90 : while (!taskPtr->m_bStop && !taskPtr->m_bFetchRows)
8990 : {
8991 44 : taskPtr->m_oCV.wait(oLock);
8992 : }
8993 46 : } while (!taskPtr->m_bStop);
8994 43 : };
8995 :
8996 43 : task->m_bFetchRows = true;
8997 : try
8998 : {
8999 43 : task->m_oThread = std::thread(taskRunner);
9000 : }
9001 0 : catch (const std::exception &e)
9002 : {
9003 0 : CPLError(CE_Failure, CPLE_AppDefined,
9004 0 : "Cannot start worker thread: %s", e.what());
9005 0 : break;
9006 : }
9007 43 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
9008 : }
9009 : }
9010 :
9011 122 : std::string osErrorMsg;
9012 122 : bool bMemoryLimitReached = false;
9013 : int ret =
9014 122 : GetNextArrowArrayInternal(out_array, osErrorMsg, bMemoryLimitReached);
9015 122 : if (!osErrorMsg.empty())
9016 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
9017 122 : if (bMemoryLimitReached)
9018 : {
9019 1 : CancelAsyncNextArrowArray();
9020 1 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
9021 : }
9022 122 : return ret;
9023 : }
9024 :
9025 : /************************************************************************/
9026 : /* GetNextArrowArrayInternal() */
9027 : /************************************************************************/
9028 :
9029 180 : int OGRGeoPackageTableLayer::GetNextArrowArrayInternal(
9030 : struct ArrowArray *out_array, std::string &osErrorMsg,
9031 : bool &bMemoryLimitReached)
9032 : {
9033 180 : bMemoryLimitReached = false;
9034 180 : memset(out_array, 0, sizeof(*out_array));
9035 :
9036 180 : if (m_iNextShapeId >= m_nTotalFeatureCount)
9037 : {
9038 61 : return 0;
9039 : }
9040 :
9041 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
9042 238 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
9043 119 : if (out_array->release == nullptr)
9044 : {
9045 0 : return ENOMEM;
9046 : }
9047 :
9048 238 : OGRGPKGTableLayerFillArrowArray sFillArrowArray;
9049 119 : sFillArrowArray.psHelper = std::move(psHelper);
9050 119 : sFillArrowArray.bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
9051 : GAS_OPT_DATETIME_AS_STRING, false);
9052 119 : sFillArrowArray.poFeatureDefn = m_poFeatureDefn;
9053 119 : sFillArrowArray.poLayer = this;
9054 119 : sFillArrowArray.hDB = m_poDS->GetDB();
9055 119 : memset(&sFillArrowArray.brokenDown, 0, sizeof(sFillArrowArray.brokenDown));
9056 :
9057 119 : sqlite3_create_function(
9058 119 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
9059 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, &sFillArrowArray, nullptr,
9060 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
9061 :
9062 238 : std::string osSQL;
9063 119 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
9064 119 : int nCountArgs = 1;
9065 :
9066 119 : osSQL += '"';
9067 119 : osSQL += SQLEscapeName(m_pszFidColumn);
9068 119 : osSQL += '"';
9069 119 : ++nCountArgs;
9070 :
9071 236 : if (!sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
9072 117 : sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
9073 : {
9074 116 : osSQL += ',';
9075 116 : osSQL += '"';
9076 116 : osSQL += SQLEscapeName(GetGeometryColumn());
9077 116 : osSQL += '"';
9078 116 : ++nCountArgs;
9079 : }
9080 : const int SQLITE_MAX_FUNCTION_ARG =
9081 119 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
9082 798 : for (int iField = 0; iField < sFillArrowArray.psHelper->m_nFieldCount;
9083 : iField++)
9084 : {
9085 : const int iArrowField =
9086 679 : sFillArrowArray.psHelper->m_mapOGRFieldToArrowField[iField];
9087 679 : if (iArrowField >= 0)
9088 : {
9089 664 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
9090 : {
9091 : // We cannot pass more than SQLITE_MAX_FUNCTION_ARG args
9092 : // to a function... So we have to split in several calls...
9093 3 : osSQL += "), OGR_GPKG_FillArrowArray_INTERNAL(";
9094 3 : osSQL += CPLSPrintf("%d", iField);
9095 3 : nCountArgs = 1;
9096 : }
9097 : const OGRFieldDefn *poFieldDefn =
9098 664 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
9099 664 : osSQL += ',';
9100 664 : osSQL += '"';
9101 664 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
9102 664 : osSQL += '"';
9103 664 : ++nCountArgs;
9104 : }
9105 : }
9106 119 : osSQL += ") FROM \"";
9107 119 : osSQL += SQLEscapeName(m_pszTableName);
9108 119 : osSQL += "\" WHERE \"";
9109 119 : osSQL += SQLEscapeName(m_pszFidColumn);
9110 119 : osSQL += "\" BETWEEN ";
9111 119 : osSQL += std::to_string(m_iNextShapeId + 1);
9112 119 : osSQL += " AND ";
9113 119 : osSQL += std::to_string(m_iNextShapeId +
9114 119 : sFillArrowArray.psHelper->m_nMaxBatchSize);
9115 :
9116 : // CPLDebug("GPKG", "%s", osSQL.c_str());
9117 :
9118 119 : char *pszErrMsg = nullptr;
9119 119 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
9120 119 : &pszErrMsg) != SQLITE_OK)
9121 : {
9122 13 : if (!sFillArrowArray.bErrorOccurred &&
9123 13 : !sFillArrowArray.bMemoryLimitReached)
9124 : {
9125 0 : osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
9126 : }
9127 : }
9128 119 : sqlite3_free(pszErrMsg);
9129 :
9130 119 : bMemoryLimitReached = sFillArrowArray.bMemoryLimitReached;
9131 :
9132 : // Delete function
9133 119 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
9134 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
9135 : nullptr, nullptr, nullptr);
9136 :
9137 119 : if (sFillArrowArray.bErrorOccurred)
9138 : {
9139 0 : sFillArrowArray.psHelper->ClearArray();
9140 0 : return ENOMEM;
9141 : }
9142 :
9143 119 : sFillArrowArray.psHelper->Shrink(sFillArrowArray.nCountRows);
9144 119 : if (sFillArrowArray.nCountRows == 0)
9145 : {
9146 0 : sFillArrowArray.psHelper->ClearArray();
9147 : }
9148 :
9149 119 : m_iNextShapeId += sFillArrowArray.nCountRows;
9150 :
9151 119 : return 0;
9152 : }
9153 :
9154 : /************************************************************************/
9155 : /* OGR_GPKG_GeometryExtent3DAggregate() */
9156 : /************************************************************************/
9157 :
9158 : namespace
9159 : {
9160 : struct GeometryExtent3DAggregateContext
9161 : {
9162 : sqlite3 *m_hDB = nullptr;
9163 : OGREnvelope3D m_oExtent3D;
9164 :
9165 18 : explicit GeometryExtent3DAggregateContext(sqlite3 *hDB)
9166 18 : : m_hDB(hDB), m_oExtent3D()
9167 : {
9168 18 : }
9169 :
9170 : GeometryExtent3DAggregateContext(const GeometryExtent3DAggregateContext &) =
9171 : delete;
9172 : GeometryExtent3DAggregateContext &
9173 : operator=(const GeometryExtent3DAggregateContext &) = delete;
9174 : };
9175 :
9176 : } // namespace
9177 :
9178 26 : static void OGR_GPKG_GeometryExtent3DAggregate_Step(sqlite3_context *pContext,
9179 : int /*argc*/,
9180 : sqlite3_value **argv)
9181 : {
9182 : const GByte *pabyBLOB =
9183 26 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
9184 :
9185 : auto poContext = static_cast<GeometryExtent3DAggregateContext *>(
9186 26 : sqlite3_user_data(pContext));
9187 :
9188 26 : if (pabyBLOB != nullptr)
9189 : {
9190 : GPkgHeader sHeader;
9191 26 : if (OGRGeoPackageGetHeader(pContext, 0, argv, &sHeader, true, true))
9192 : {
9193 26 : OGREnvelope3D extent3D;
9194 26 : extent3D.MinX = sHeader.MinX;
9195 26 : extent3D.MaxX = sHeader.MaxX;
9196 26 : extent3D.MinY = sHeader.MinY;
9197 26 : extent3D.MaxY = sHeader.MaxY;
9198 26 : extent3D.MinZ = sHeader.MinZ;
9199 26 : extent3D.MaxZ = sHeader.MaxZ;
9200 26 : poContext->m_oExtent3D.Merge(extent3D);
9201 : }
9202 0 : else if (!sHeader.bEmpty)
9203 : {
9204 : // Try also spatialite geometry blobs
9205 0 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
9206 0 : OGRGeometry *poGeom = nullptr;
9207 0 : if (OGRSQLiteImportSpatiaLiteGeometry(pabyBLOB, nBLOBLen,
9208 0 : &poGeom) == OGRERR_NONE &&
9209 0 : poGeom && !poGeom->IsEmpty())
9210 : {
9211 0 : OGREnvelope3D extent3D;
9212 0 : poGeom->getEnvelope(&extent3D);
9213 0 : poContext->m_oExtent3D.Merge(extent3D);
9214 : }
9215 0 : delete poGeom;
9216 : }
9217 : }
9218 26 : }
9219 :
9220 18 : static void OGR_GPKG_GeometryExtent3DAggregate_Finalize(sqlite3_context *)
9221 : {
9222 18 : }
9223 :
9224 : /************************************************************************/
9225 : /* IGetExtent3D */
9226 : /************************************************************************/
9227 20 : OGRErr OGRGeoPackageTableLayer::IGetExtent3D(int iGeomField,
9228 : OGREnvelope3D *psExtent3D,
9229 : bool bForce)
9230 : {
9231 :
9232 20 : const OGRFeatureDefn *poDefn = GetLayerDefn();
9233 :
9234 : /* -------------------------------------------------------------------- */
9235 : /* Deferred actions, reset state. */
9236 : /* -------------------------------------------------------------------- */
9237 20 : RunDeferredCreationIfNecessary();
9238 20 : if (!RunDeferredSpatialIndexUpdate())
9239 : {
9240 0 : return OGRERR_FAILURE;
9241 : }
9242 :
9243 20 : if (m_nZFlag == 0 && m_soFilter.empty())
9244 : {
9245 : // If the layer doesn't contain any 3D geometry and no filter is set,
9246 : // we can fallback to the fast 2D GetExtent()
9247 2 : const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
9248 2 : psExtent3D->MinZ = std::numeric_limits<double>::infinity();
9249 2 : psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
9250 2 : return retVal;
9251 : }
9252 : else
9253 : {
9254 18 : *psExtent3D = OGREnvelope3D();
9255 : }
9256 :
9257 : // For internal use only
9258 :
9259 18 : GeometryExtent3DAggregateContext sContext(m_poDS->hDB);
9260 :
9261 36 : CPLString osFuncName;
9262 : osFuncName.Printf("OGR_GPKG_GeometryExtent3DAggregate_INTERNAL_%p",
9263 18 : &sContext);
9264 :
9265 18 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
9266 : &sContext, nullptr,
9267 : OGR_GPKG_GeometryExtent3DAggregate_Step,
9268 : OGR_GPKG_GeometryExtent3DAggregate_Finalize);
9269 :
9270 36 : char *pszSQL = sqlite3_mprintf(
9271 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
9272 18 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
9273 45 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
9274 18 : char *pszErrMsg = nullptr;
9275 : const int rc =
9276 18 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
9277 :
9278 : // Delete function
9279 18 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
9280 : nullptr, nullptr, nullptr, nullptr);
9281 :
9282 18 : if (rc != SQLITE_OK)
9283 : {
9284 0 : if (rc != SQLITE_INTERRUPT)
9285 : {
9286 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
9287 : pszSQL, pszErrMsg);
9288 : }
9289 0 : sqlite3_free(pszErrMsg);
9290 0 : sqlite3_free(pszSQL);
9291 0 : return OGRERR_FAILURE;
9292 : }
9293 18 : sqlite3_free(pszErrMsg);
9294 18 : sqlite3_free(pszSQL);
9295 :
9296 18 : *psExtent3D = sContext.m_oExtent3D;
9297 :
9298 18 : return OGRERR_NONE;
9299 : }
9300 :
9301 : /************************************************************************/
9302 : /* Truncate() */
9303 : /************************************************************************/
9304 :
9305 : /** Implements "DELETE FROM {table_name}" in an optimized way.
9306 : *
9307 : * Disable triggers if we detect that the only triggers on the table are ones
9308 : * under our control (i.e. the ones for the gpkg_ogr_contents table and the
9309 : * ones updating the RTree)
9310 : * And even if we cannot disable triggers, truncate the RTree before the main
9311 : * table, as this dramatically speeds up truncating the main table.
9312 : */
9313 8 : OGRErr OGRGeoPackageTableLayer::Truncate()
9314 : {
9315 8 : if (!m_poDS->GetUpdate())
9316 : {
9317 2 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
9318 : "Truncate");
9319 2 : return OGRERR_FAILURE;
9320 : }
9321 :
9322 6 : ResetReading();
9323 6 : SyncToDisk();
9324 :
9325 6 : bool bOK = (m_poDS->SoftStartTransaction() == OGRERR_NONE);
9326 :
9327 : struct ReenableTriggers
9328 : {
9329 : sqlite3 *m_hDB = nullptr;
9330 :
9331 4 : explicit ReenableTriggers(sqlite3 *hDB) : m_hDB(hDB)
9332 : {
9333 4 : }
9334 :
9335 4 : ~ReenableTriggers()
9336 4 : {
9337 4 : sqlite3_db_config(m_hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 1,
9338 : nullptr);
9339 4 : }
9340 : CPL_DISALLOW_COPY_ASSIGN(ReenableTriggers)
9341 : };
9342 :
9343 : // to keep in top level scope!
9344 0 : std::unique_ptr<ReenableTriggers> reenableTriggers;
9345 :
9346 : // Temporarily disable triggers for greater speed if we detect that the
9347 : // only triggers on the table are the RTree ones and the ones for the
9348 : // gpkg_ogr_contents table
9349 6 : if (bOK && m_bIsTable)
9350 : {
9351 6 : char *pszSQL = sqlite3_mprintf(
9352 : "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' "
9353 : "AND tbl_name = '%q' "
9354 : "AND name NOT IN ('trigger_insert_feature_count_%q',"
9355 : "'trigger_delete_feature_count_%q') "
9356 : "AND name NOT LIKE 'rtree_%q_%%'",
9357 : m_pszTableName, m_pszTableName, m_pszTableName, m_pszTableName);
9358 6 : OGRErr eErr = OGRERR_NONE;
9359 10 : if (SQLGetInteger(m_poDS->GetDB(), pszSQL, &eErr) == 0 &&
9360 4 : eErr == OGRERR_NONE)
9361 : {
9362 4 : int nEnableTriggerOldVal = -1;
9363 4 : sqlite3_db_config(m_poDS->GetDB(), SQLITE_DBCONFIG_ENABLE_TRIGGER,
9364 : -1, &nEnableTriggerOldVal);
9365 4 : if (nEnableTriggerOldVal == 1)
9366 : {
9367 4 : int nNewVal = -1;
9368 4 : sqlite3_db_config(m_poDS->GetDB(),
9369 : SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &nNewVal);
9370 4 : if (nNewVal == 0)
9371 : {
9372 4 : CPLDebugOnly("GPKG",
9373 : "Disabling triggers during truncation of %s",
9374 : m_pszTableName);
9375 : reenableTriggers =
9376 4 : std::make_unique<ReenableTriggers>(m_poDS->GetDB());
9377 4 : CPL_IGNORE_RET_VAL(reenableTriggers); // to please cppcheck
9378 : }
9379 : }
9380 : }
9381 6 : sqlite3_free(pszSQL);
9382 : }
9383 :
9384 6 : char *pszErrMsg = nullptr;
9385 6 : if (bOK && m_bIsTable && HasSpatialIndex())
9386 : {
9387 : // Manually clean the 3 tables that are used by the RTree:
9388 : // - rtree_{tablename}_{geom}_node: all rows, but nodeno = 1 for which
9389 : // we reset the 'data' field to a zero blob of the same size
9390 : // - rtree_{tablename}_{geom}_parent: all rows
9391 : // - rtree_{tablename}_{geom}_rowid: all rows
9392 :
9393 2 : const char *pszT = m_pszTableName;
9394 2 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
9395 :
9396 2 : m_osRTreeName = "rtree_";
9397 2 : m_osRTreeName += pszT;
9398 2 : m_osRTreeName += "_";
9399 2 : m_osRTreeName += pszC;
9400 :
9401 : {
9402 : char *pszSQL =
9403 2 : sqlite3_mprintf("DELETE FROM \"%w_node\" WHERE nodeno > 1;"
9404 : "DELETE FROM \"%w_parent\"; "
9405 : "DELETE FROM \"%w_rowid\"",
9406 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
9407 : m_osRTreeName.c_str());
9408 2 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9409 : &pszErrMsg) == SQLITE_OK;
9410 2 : sqlite3_free(pszSQL);
9411 : }
9412 :
9413 2 : if (bOK)
9414 : {
9415 2 : char *pszSQL = sqlite3_mprintf(
9416 : "SELECT length(data) FROM \"%w_node\" WHERE nodeno = 1",
9417 : m_osRTreeName.c_str());
9418 : const int nBlobSize =
9419 2 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr);
9420 2 : sqlite3_free(pszSQL);
9421 :
9422 2 : pszSQL = sqlite3_mprintf(
9423 : "UPDATE \"%w_node\" SET data = zeroblob(%d) WHERE nodeno = 1",
9424 : m_osRTreeName.c_str(), nBlobSize);
9425 2 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9426 : &pszErrMsg) == SQLITE_OK;
9427 2 : sqlite3_free(pszSQL);
9428 : }
9429 : }
9430 :
9431 6 : if (bOK)
9432 : {
9433 : // Truncate main table
9434 6 : char *pszSQL = sqlite3_mprintf("DELETE FROM \"%w\"", m_pszTableName);
9435 6 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9436 : SQLITE_OK;
9437 6 : sqlite3_free(pszSQL);
9438 : }
9439 :
9440 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9441 : // Reset feature count
9442 6 : if (bOK && m_poDS->m_bHasGPKGOGRContents)
9443 : {
9444 : char *pszSQL =
9445 5 : sqlite3_mprintf("UPDATE gpkg_ogr_contents SET feature_count = 0 "
9446 : "WHERE lower(table_name) = lower('%q')",
9447 : m_pszTableName);
9448 5 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9449 : SQLITE_OK;
9450 5 : sqlite3_free(pszSQL);
9451 : }
9452 :
9453 6 : if (bOK)
9454 : {
9455 5 : m_nTotalFeatureCount = 0;
9456 : }
9457 : #endif
9458 :
9459 6 : if (bOK)
9460 : {
9461 5 : m_poDS->SoftCommitTransaction();
9462 : }
9463 : else
9464 : {
9465 1 : m_poDS->SoftRollbackTransaction();
9466 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9467 1 : DisableFeatureCount();
9468 : #endif
9469 1 : CPLError(CE_Failure, CPLE_AppDefined, "Truncate(%s) failed: %s",
9470 1 : m_pszTableName, pszErrMsg ? pszErrMsg : "(unknown reason)");
9471 : }
9472 6 : sqlite3_free(pszErrMsg);
9473 :
9474 6 : return bOK ? OGRERR_NONE : OGRERR_FAILURE;
9475 : }
|