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 5948 : OGRErr OGRGeoPackageTableLayer::SaveExtent()
50 : {
51 5948 : if (!m_poDS->GetUpdate() || !m_bExtentChanged || !m_poExtent)
52 5293 : return OGRERR_NONE;
53 :
54 655 : sqlite3 *poDb = m_poDS->GetDB();
55 :
56 655 : if (!poDb)
57 0 : return OGRERR_FAILURE;
58 :
59 : char *pszSQL =
60 655 : 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 655 : m_poExtent->MinX, m_poExtent->MinY, m_poExtent->MaxX,
66 655 : m_poExtent->MaxY, m_pszTableName);
67 :
68 655 : OGRErr err = SQLCommand(poDb, pszSQL);
69 655 : sqlite3_free(pszSQL);
70 655 : m_bExtentChanged = false;
71 :
72 655 : return err;
73 : }
74 :
75 : //----------------------------------------------------------------------
76 : // SaveTimestamp()
77 : //
78 : // Update the last_change column of the gpkg_contents metadata table.
79 : //
80 5943 : OGRErr OGRGeoPackageTableLayer::SaveTimestamp()
81 : {
82 5943 : if (!m_poDS->GetUpdate() || !m_bContentChanged)
83 5065 : return OGRERR_NONE;
84 :
85 878 : m_bContentChanged = false;
86 :
87 878 : OGRErr err = m_poDS->UpdateGpkgContentsLastChange(m_pszTableName);
88 :
89 : #ifdef ENABLE_GPKG_OGR_CONTENTS
90 878 : if (m_bIsTable && err == OGRERR_NONE && m_poDS->m_bHasGPKGOGRContents &&
91 869 : !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 878 : 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 262970 : OGRErr OGRGeoPackageTableLayer::UpdateExtent(const OGREnvelope *poExtent)
114 : {
115 262970 : if (!m_poExtent)
116 : {
117 616 : m_poExtent = std::make_unique<OGREnvelope>(*poExtent);
118 : }
119 262970 : m_poExtent->Merge(*poExtent);
120 262970 : m_bExtentChanged = true;
121 262970 : 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 26885 : OGRErr OGRGeoPackageTableLayer::BuildColumns()
131 : {
132 26885 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
133 0 : return OGRERR_FAILURE;
134 :
135 26885 : if (!m_bFeatureDefnCompleted)
136 27 : GetLayerDefn();
137 :
138 26885 : m_anFieldOrdinals.resize(m_poFeatureDefn->GetFieldCount());
139 26885 : int iCurCol = 0;
140 :
141 : /* Always start with a primary key */
142 26885 : CPLString soColumns;
143 26885 : if (m_bIsTable || m_pszFidColumn != nullptr)
144 : {
145 26871 : soColumns += "m.";
146 26871 : soColumns += m_pszFidColumn
147 53742 : ? "\"" + SQLEscapeName(m_pszFidColumn) + "\""
148 26871 : : "_rowid_";
149 26871 : m_iFIDCol = iCurCol;
150 26871 : iCurCol++;
151 : }
152 :
153 : /* Add a geometry column if there is one (just one) */
154 26885 : if (m_poFeatureDefn->GetGeomFieldCount())
155 : {
156 26661 : const auto poFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
157 26661 : if (poFieldDefn->IsIgnored())
158 : {
159 10 : m_iGeomCol = -1;
160 : }
161 : else
162 : {
163 26651 : if (!soColumns.empty())
164 26640 : soColumns += ", ";
165 26651 : soColumns += "m.\"";
166 26651 : soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
167 26651 : soColumns += "\"";
168 26651 : m_iGeomCol = iCurCol;
169 26651 : iCurCol++;
170 : }
171 : }
172 :
173 : /* Add all the attribute columns */
174 38928 : 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 26885 : if (soColumns.empty())
194 : {
195 : // Can happen if ignoring all fields on a view...
196 2 : soColumns = "NULL";
197 : }
198 26885 : m_soColumns = std::move(soColumns);
199 26885 : 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 268816 : bool OGRGeoPackageTableLayer::IsGeomFieldSet(OGRFeature *poFeature)
209 : {
210 534249 : return poFeature->GetDefnRef()->GetGeomFieldCount() &&
211 534249 : poFeature->GetGeomFieldRef(0);
212 : }
213 :
214 268838 : 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 268838 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
221 :
222 268838 : int nColCount = 1;
223 268838 : 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 537668 : if ((nUpdatedGeomFieldsCount < 0 || nUpdatedGeomFieldsCount == 1) &&
242 268830 : poFeatureDefn->GetGeomFieldCount())
243 : {
244 : // Non-NULL geometry.
245 265445 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
246 265445 : if (poGeom)
247 : {
248 264016 : size_t szWkb = 0;
249 528032 : GByte *pabyWkb = GPkgGeometryFromOGR(poGeom, m_iSrs,
250 264016 : &m_sBinaryPrecision, &szWkb);
251 264016 : if (!pabyWkb)
252 0 : return OGRERR_FAILURE;
253 264016 : int err = sqlite3_bind_blob(poStmt, nColCount++, pabyWkb,
254 : static_cast<int>(szWkb), CPLFree);
255 264016 : 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 264016 : 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 268838 : const int nFieldCount = poFeatureDefn->GetFieldCount();
286 :
287 268838 : size_t nInsertionBufferPos = 0;
288 268838 : if (m_osInsertionBuffer.empty())
289 29501 : m_osInsertionBuffer.resize(OGR_SIZEOF_ISO8601_DATETIME_BUFFER *
290 : nFieldCount);
291 :
292 4739550 : for (int idx = 0;
293 4739550 : idx < (nUpdatedFieldsCount < 0 ? nFieldCount : nUpdatedFieldsCount);
294 : idx++)
295 : {
296 4470710 : const int iField =
297 4470710 : nUpdatedFieldsCount < 0 ? idx : panUpdatedFieldsIdx[idx];
298 4470710 : assert(iField >= 0);
299 4470710 : const auto &oFieldDefn = poFeatureDefn->GetFieldDefn(iField);
300 4470710 : if (iField == m_iFIDAsRegularColumnIndex || oFieldDefn->IsGenerated())
301 1287 : continue;
302 4470680 : 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 4469420 : poFeatureDefn->GetFieldDefnUnsafe(iField);
319 4469420 : int err = SQLITE_OK;
320 4469420 : bool bBound = false;
321 4469420 : char *pszValToFree = nullptr;
322 :
323 4469420 : if (!poFeature->IsFieldNullUnsafe(iField))
324 : {
325 4469390 : const auto eType = poFieldDefn->GetType();
326 4469390 : switch (eType)
327 : {
328 10530 : case OFTInteger:
329 : {
330 10530 : err = sqlite3_bind_int(
331 : poStmt, nColCount++,
332 : poFeature->GetFieldAsIntegerUnsafe(iField));
333 10530 : 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 22882 : case OFTReal:
343 : {
344 22882 : err = sqlite3_bind_double(
345 : poStmt, nColCount++,
346 : poFeature->GetFieldAsDoubleUnsafe(iField));
347 22882 : 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 4469420 : 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 268838 : if (pnColCount != nullptr)
597 66 : *pnColCount = nColCount;
598 268838 : 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 268772 : OGRErr OGRGeoPackageTableLayer::FeatureBindInsertParameters(
643 : OGRFeature *poFeature, sqlite3_stmt *poStmt, bool bAddFID,
644 : bool bBindUnsetFields)
645 : {
646 268772 : return FeatureBindParameters(poFeature, poStmt, nullptr, bAddFID,
647 268772 : 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 856 : CPLString OGRGeoPackageTableLayer::FeatureGenerateInsertSQL(
661 : OGRFeature *poFeature, bool bAddFID, bool bBindUnsetFields, bool bUpsert,
662 : const std::string &osUpsertUniqueColumnName)
663 : {
664 856 : bool bNeedComma = false;
665 856 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
666 :
667 856 : if (poFeatureDefn->GetFieldCount() ==
668 856 : ((m_iFIDAsRegularColumnIndex >= 0) ? 1 : 0) &&
669 856 : 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 1704 : CPLString osSQLFront("INSERT");
675 852 : if (bUpsert && osUpsertUniqueColumnName.empty())
676 8 : osSQLFront += " OR REPLACE";
677 : osSQLFront +=
678 852 : CPLSPrintf(" INTO \"%s\" ( ", SQLEscapeName(m_pszTableName).c_str());
679 :
680 1704 : CPLString osSQLBack;
681 852 : osSQLBack = ") VALUES (";
682 :
683 1704 : CPLString osSQLColumn;
684 :
685 852 : 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 852 : if (poFeatureDefn->GetGeomFieldCount())
694 : {
695 793 : if (bNeedComma)
696 : {
697 105 : osSQLFront += ", ";
698 105 : osSQLBack += ", ";
699 : }
700 :
701 : osSQLColumn.Printf(
702 : "\"%s\"",
703 1586 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
704 793 : .c_str());
705 793 : osSQLFront += osSQLColumn;
706 793 : osSQLBack += "?";
707 793 : bNeedComma = true;
708 : }
709 :
710 : /* Add attribute column names (except FID) to the SQL */
711 3342 : 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 852 : osSQLBack += ")";
738 :
739 852 : if (!bNeedComma)
740 : return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
741 0 : SQLEscapeName(m_pszTableName).c_str());
742 :
743 852 : 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 1704 : 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 67641 : const OGRFeatureDefn *OGRGeoPackageTableLayer::GetLayerDefn() const
860 : {
861 67641 : if (!m_bFeatureDefnCompleted)
862 : {
863 1073 : m_bFeatureDefnCompleted = true;
864 1073 : const_cast<OGRGeoPackageTableLayer *>(this)->ReadTableDefinition();
865 1073 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
866 : }
867 67641 : return m_poFeatureDefn;
868 : }
869 :
870 : /************************************************************************/
871 : /* GetFIDColumn() */
872 : /************************************************************************/
873 :
874 3476 : const char *OGRGeoPackageTableLayer::GetFIDColumn() const
875 : {
876 3476 : if (!m_bFeatureDefnCompleted)
877 25 : GetLayerDefn();
878 3476 : return OGRGeoPackageLayer::GetFIDColumn();
879 : }
880 :
881 : /************************************************************************/
882 : /* GetGeomType() */
883 : /************************************************************************/
884 :
885 273085 : OGRwkbGeometryType OGRGeoPackageTableLayer::GetGeomType() const
886 : {
887 273085 : return m_poFeatureDefn->GetGeomType();
888 : }
889 :
890 : /************************************************************************/
891 : /* GetGeometryColumn() */
892 : /************************************************************************/
893 :
894 7130 : const char *OGRGeoPackageTableLayer::GetGeometryColumn() const
895 :
896 : {
897 7130 : if (m_poFeatureDefn->GetGeomFieldCount() > 0)
898 7116 : 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 1073 : OGRErr OGRGeoPackageTableLayer::ReadTableDefinition()
913 : {
914 1073 : m_poDS->IncrementReadTableDefCounter();
915 :
916 1073 : bool bReadExtent = false;
917 1073 : sqlite3 *poDb = m_poDS->GetDB();
918 1073 : OGREnvelope oExtent;
919 2146 : CPLString osGeomColumnName;
920 2146 : CPLString osGeomColsType;
921 1073 : bool bHasZ = false;
922 1073 : bool bHasM = false;
923 :
924 : #ifdef ENABLE_GPKG_OGR_CONTENTS
925 1073 : if (m_poDS->m_bHasGPKGOGRContents)
926 : {
927 : CPLString osTrigger1Name(
928 2138 : CPLSPrintf("trigger_insert_feature_count_%s", m_pszTableName));
929 : CPLString osTrigger2Name(
930 2138 : CPLSPrintf("trigger_delete_feature_count_%s", m_pszTableName));
931 : const std::map<CPLString, CPLString> &oMap =
932 1069 : m_poDS->GetNameTypeMapFromSQliteMaster();
933 2091 : if (cpl::contains(oMap, osTrigger1Name.toupper()) &&
934 1022 : cpl::contains(oMap, osTrigger2Name.toupper()))
935 : {
936 1022 : 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 1073 : if (m_poDS->m_bHasGPKGOGRContents)
950 : {
951 1069 : 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 2138 : auto oResultFeatureCount = SQLQuery(poDb, pszSQL);
960 1069 : sqlite3_free(pszSQL);
961 1069 : 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 1069 : if (oResultFeatureCount && oResultFeatureCount->RowCount() == 1)
976 : {
977 1034 : const char *pszFeatureCount = oResultFeatureCount->GetValue(0, 0);
978 1034 : if (pszFeatureCount)
979 : {
980 1017 : m_nTotalFeatureCount =
981 1017 : std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
982 : }
983 : }
984 : }
985 : #endif
986 :
987 : bool bHasPreexistingSingleGeomColumn =
988 1073 : m_poFeatureDefn->GetGeomFieldCount() == 1;
989 1073 : bool bHasMultipleGeomColsInGpkgGeometryColumns = false;
990 :
991 1073 : if (m_bIsInGpkgContents)
992 : {
993 : /* Check that the table name is registered in gpkg_contents */
994 : const std::map<CPLString, GPKGContentsDesc> &oMapContents =
995 1054 : m_poDS->GetContents();
996 : const auto oIterContents =
997 1054 : oMapContents.find(CPLString(m_pszTableName).toupper());
998 1054 : 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 1054 : const GPKGContentsDesc &oContents = oIterContents->second;
1007 :
1008 1054 : const char *pszIdentifier = oContents.osIdentifier.c_str();
1009 1054 : if (pszIdentifier[0] != 0 && strcmp(pszIdentifier, m_pszTableName) != 0)
1010 6 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
1011 1054 : const char *pszDescription = oContents.osDescription.c_str();
1012 1054 : if (pszDescription[0])
1013 12 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
1014 :
1015 1054 : if (m_bIsSpatial)
1016 : {
1017 : /* All the extrema have to be non-NULL for this to make sense */
1018 1735 : if (!oContents.osMinX.empty() && !oContents.osMinY.empty() &&
1019 1735 : !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 994 : 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 1988 : auto oResultGeomCols = SQLQuery(poDb, pszSQL);
1042 994 : sqlite3_free(pszSQL);
1043 994 : 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 994 : 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 994 : int iRow = -1;
1070 994 : bHasMultipleGeomColsInGpkgGeometryColumns =
1071 994 : oResultGeomCols->RowCount() > 1;
1072 995 : for (int i = 0; i < oResultGeomCols->RowCount(); ++i)
1073 : {
1074 : const char *pszGeomColName =
1075 995 : oResultGeomCols->GetValue(1, i);
1076 995 : if (!pszGeomColName)
1077 0 : continue;
1078 1990 : if (!bHasPreexistingSingleGeomColumn ||
1079 995 : strcmp(pszGeomColName,
1080 995 : m_poFeatureDefn->GetGeomFieldDefn(0)
1081 : ->GetNameRef()) == 0)
1082 : {
1083 994 : iRow = i;
1084 994 : break;
1085 : }
1086 : }
1087 :
1088 994 : if (iRow >= 0)
1089 : {
1090 : const char *pszGeomColName =
1091 994 : oResultGeomCols->GetValue(1, iRow);
1092 994 : if (pszGeomColName != nullptr)
1093 994 : osGeomColumnName = pszGeomColName;
1094 : const char *pszGeomColsType =
1095 994 : oResultGeomCols->GetValue(2, iRow);
1096 994 : if (pszGeomColsType != nullptr)
1097 994 : osGeomColsType = pszGeomColsType;
1098 994 : m_iSrs = oResultGeomCols->GetValueAsInteger(3, iRow);
1099 994 : m_nZFlag = oResultGeomCols->GetValueAsInteger(4, iRow);
1100 994 : m_nMFlag = oResultGeomCols->GetValueAsInteger(5, iRow);
1101 994 : if (!(EQUAL(osGeomColsType, "GEOMETRY") && m_nZFlag == 2))
1102 : {
1103 988 : bHasZ = CPL_TO_BOOL(m_nZFlag);
1104 988 : 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 2146 : std::set<std::string> uniqueFieldsUC;
1124 1073 : 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 1065 : constexpr int THRESHOLD_GET_SQLITE_MASTER = 10;
1132 1065 : 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 1063 : 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 1073 : char *pszSQL = sqlite3_mprintf("pragma table_xinfo('%q')", m_pszTableName);
1149 2146 : auto oResultTable = SQLQuery(poDb, pszSQL);
1150 1073 : sqlite3_free(pszSQL);
1151 :
1152 1073 : 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 1073 : int nCountPKIDColumns = 0;
1164 4983 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1165 : {
1166 3910 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1167 3910 : if (nPKIDIndex > 0)
1168 1058 : nCountPKIDColumns++;
1169 : }
1170 1073 : 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 4983 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1179 : {
1180 3910 : const char *pszName = oResultTable->GetValue(1, iRecord);
1181 7820 : std::string osType = oResultTable->GetValue(2, iRecord);
1182 3910 : int bNotNull = oResultTable->GetValueAsInteger(3, iRecord);
1183 3910 : const char *pszDefault = oResultTable->GetValue(4, iRecord);
1184 3910 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1185 3910 : int nHiddenValue = oResultTable->GetValueAsInteger(6, iRecord);
1186 :
1187 3910 : OGRFieldSubType eSubType = OFSTNone;
1188 3910 : int nMaxWidth = 0;
1189 3910 : 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 3910 : bool bIsGenerated = false;
1194 3910 : constexpr const char *GENERATED_ALWAYS_SUFFIX = " GENERATED ALWAYS";
1195 3910 : if (osType.size() > strlen(GENERATED_ALWAYS_SUFFIX) &&
1196 3910 : 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 3910 : constexpr int GENERATED_VIRTUAL = 2;
1204 3910 : constexpr int GENERATED_STORED = 3;
1205 3910 : if (nHiddenValue == GENERATED_VIRTUAL ||
1206 : nHiddenValue == GENERATED_STORED)
1207 : {
1208 2 : bIsGenerated = true;
1209 : }
1210 :
1211 3910 : if (!osType.empty() || m_bIsTable)
1212 : {
1213 3907 : 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 7814 : if (!osType.empty() && !EQUAL(pszName, "OGC_FID") &&
1227 998 : ((nType > OFTMaxType && !osGeomColsType.empty()) ||
1228 2908 : EQUAL(osGeomColumnName, pszName)))
1229 : {
1230 : /* Maybe it is a geometry type? */
1231 : OGRwkbGeometryType oGeomType;
1232 996 : if (nType > OFTMaxType)
1233 996 : oGeomType = GPkgGeometryTypeToWKB(osType.c_str(), bHasZ, bHasM);
1234 : else
1235 0 : oGeomType = wkbUnknown;
1236 996 : if (oGeomType != wkbNone)
1237 : {
1238 1991 : if ((bHasPreexistingSingleGeomColumn &&
1239 995 : (!bHasMultipleGeomColsInGpkgGeometryColumns ||
1240 3 : strcmp(pszName, m_poFeatureDefn->GetGeomFieldDefn(0)
1241 1992 : ->GetNameRef()) == 0)) ||
1242 2 : m_poFeatureDefn->GetGeomFieldCount() == 0)
1243 : {
1244 : OGRwkbGeometryType oGeomTypeGeomCols =
1245 994 : GPkgGeometryTypeToWKB(osGeomColsType.c_str(), bHasZ,
1246 : bHasM);
1247 : /* Enforce consistency between table and metadata */
1248 994 : if (wkbFlatten(oGeomType) == wkbUnknown)
1249 576 : oGeomType = oGeomTypeGeomCols;
1250 994 : 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 994 : if (!bHasPreexistingSingleGeomColumn)
1262 : {
1263 0 : OGRGeomFieldDefn oGeomField(pszName, oGeomType);
1264 0 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1265 : }
1266 994 : bHasPreexistingSingleGeomColumn = false;
1267 994 : if (bNotNull)
1268 3 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetNullable(
1269 : FALSE);
1270 :
1271 : /* Read the SRS */
1272 1988 : auto poSRS = m_poDS->GetSpatialRef(m_iSrs);
1273 994 : 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 2914 : 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 2914 : if (nPKIDIndex > 0 && nCountPKIDColumns == 1 &&
1309 1056 : (nType == OFTInteger || nType == OFTInteger64))
1310 : {
1311 1056 : 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 47 : 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 1073 : 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 1073 : if (bReadExtent)
1399 : {
1400 741 : m_poExtent = std::make_unique<OGREnvelope>(oExtent);
1401 : }
1402 :
1403 : // Look for sub-types such as JSON
1404 1073 : 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 1073 : 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 1073 : BuildColumns();
1545 :
1546 1073 : CheckUnknownExtensions();
1547 :
1548 1073 : InitView();
1549 :
1550 1073 : return OGRERR_NONE;
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* OGRGeoPackageTableLayer() */
1555 : /************************************************************************/
1556 :
1557 4430 : OGRGeoPackageTableLayer::OGRGeoPackageTableLayer(GDALGeoPackageDataset *poDS,
1558 4430 : const char *pszTableName)
1559 4430 : : OGRGeoPackageLayer(poDS), m_pszTableName(CPLStrdup(pszTableName))
1560 : {
1561 4430 : memset(m_abHasGeometryExtension, 0, sizeof(m_abHasGeometryExtension));
1562 :
1563 4430 : m_poFeatureDefn = new OGRFeatureDefn(m_pszTableName);
1564 4430 : SetDescription(m_poFeatureDefn->GetName());
1565 4430 : m_poFeatureDefn->SetGeomType(wkbNone);
1566 4430 : m_poFeatureDefn->Reference();
1567 4430 : }
1568 :
1569 : /************************************************************************/
1570 : /* ~OGRGeoPackageTableLayer() */
1571 : /************************************************************************/
1572 :
1573 8860 : OGRGeoPackageTableLayer::~OGRGeoPackageTableLayer()
1574 : {
1575 4430 : OGRGeoPackageTableLayer::SyncToDisk();
1576 :
1577 : /* Clean up resources in memory */
1578 4430 : if (m_pszTableName)
1579 4430 : CPLFree(m_pszTableName);
1580 :
1581 4430 : if (m_poUpdateStatement)
1582 23 : sqlite3_finalize(m_poUpdateStatement);
1583 :
1584 4430 : if (m_poInsertStatement)
1585 686 : sqlite3_finalize(m_poInsertStatement);
1586 :
1587 4430 : if (m_poGetFeatureStatement)
1588 34 : sqlite3_finalize(m_poGetFeatureStatement);
1589 :
1590 4430 : CancelAsyncNextArrowArray();
1591 8860 : }
1592 :
1593 : /************************************************************************/
1594 : /* CancelAsyncNextArrowArray() */
1595 : /************************************************************************/
1596 :
1597 336571 : void OGRGeoPackageTableLayer::CancelAsyncNextArrowArray()
1598 : {
1599 336571 : 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 336571 : if (m_oThreadNextArrowArray.joinable())
1607 : {
1608 2 : m_oThreadNextArrowArray.join();
1609 : }
1610 :
1611 336571 : m_poFillArrowArray.reset();
1612 :
1613 336585 : 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 336571 : }
1633 :
1634 : /************************************************************************/
1635 : /* InitView() */
1636 : /************************************************************************/
1637 :
1638 1073 : void OGRGeoPackageTableLayer::InitView()
1639 : {
1640 : #ifdef SQLITE_HAS_COLUMN_METADATA
1641 1073 : 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 1073 : }
1742 :
1743 : /************************************************************************/
1744 : /* CheckUpdatableTable() */
1745 : /************************************************************************/
1746 :
1747 5725 : bool OGRGeoPackageTableLayer::CheckUpdatableTable(const char *pszOperation)
1748 : {
1749 5725 : 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 5721 : 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 5715 : 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 504 : std::make_unique<OGRFieldDefn>(oFieldDefn),
1911 504 : m_poFeatureDefn->GetFieldCount() - 1, FieldChangeType::ADD_FIELD,
1912 1512 : 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 5953 : void OGRGeoPackageTableLayer::CreateFeatureCountTriggers(
2122 : const char *pszTableName)
2123 : {
2124 5953 : if (m_bAddOGRFeatureCountTriggers)
2125 : {
2126 1085 : if (pszTableName == nullptr)
2127 1076 : pszTableName = m_pszTableName;
2128 :
2129 1085 : m_bOGRFeatureCountTriggersEnabled = true;
2130 1085 : m_bAddOGRFeatureCountTriggers = false;
2131 1085 : m_bFeatureCountTriggersDeletedInTransaction = false;
2132 :
2133 1085 : CPLDebug("GPKG", "Creating insert/delete feature_count triggers");
2134 1085 : 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 1085 : SQLCommand(m_poDS->GetDB(), pszSQL);
2141 1085 : sqlite3_free(pszSQL);
2142 :
2143 1085 : 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 1085 : SQLCommand(m_poDS->GetDB(), pszSQL);
2150 1085 : sqlite3_free(pszSQL);
2151 : }
2152 5953 : }
2153 :
2154 : /************************************************************************/
2155 : /* DisableFeatureCountTriggers() */
2156 : /************************************************************************/
2157 :
2158 69 : void OGRGeoPackageTableLayer::DisableFeatureCountTriggers(
2159 : bool bNullifyFeatureCount)
2160 : {
2161 69 : if (m_bOGRFeatureCountTriggersEnabled)
2162 : {
2163 69 : m_bOGRFeatureCountTriggersEnabled = false;
2164 69 : m_bAddOGRFeatureCountTriggers = true;
2165 69 : m_bFeatureCountTriggersDeletedInTransaction = m_poDS->IsInTransaction();
2166 :
2167 69 : CPLDebug("GPKG", "Deleting insert/delete feature_count triggers");
2168 :
2169 69 : char *pszSQL = sqlite3_mprintf(
2170 : "DROP TRIGGER \"trigger_insert_feature_count_%w\"", m_pszTableName);
2171 69 : SQLCommand(m_poDS->GetDB(), pszSQL);
2172 69 : sqlite3_free(pszSQL);
2173 :
2174 69 : pszSQL = sqlite3_mprintf(
2175 : "DROP TRIGGER \"trigger_delete_feature_count_%w\"", m_pszTableName);
2176 69 : SQLCommand(m_poDS->GetDB(), pszSQL);
2177 69 : sqlite3_free(pszSQL);
2178 :
2179 69 : if (m_poDS->m_bHasGPKGOGRContents && bNullifyFeatureCount)
2180 : {
2181 60 : pszSQL = sqlite3_mprintf(
2182 : "UPDATE gpkg_ogr_contents SET feature_count = NULL WHERE "
2183 : "lower(table_name )= lower('%q')",
2184 : m_pszTableName);
2185 60 : SQLCommand(m_poDS->GetDB(), pszSQL);
2186 60 : sqlite3_free(pszSQL);
2187 : }
2188 : }
2189 69 : }
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 268845 : void OGRGeoPackageTableLayer::CheckGeometryType(const OGRFeature *poFeature)
2204 : {
2205 268845 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
2206 268845 : const OGRwkbGeometryType eFlattenLayerGeomType = wkbFlatten(eLayerGeomType);
2207 268845 : const OGRGeometry *poGeom = poFeature->GetGeometryRef();
2208 268845 : 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 268845 : if (m_nZFlag == 0 || m_nMFlag == 0)
2237 : {
2238 268840 : if (poGeom != nullptr)
2239 : {
2240 264016 : bool bUpdateGpkgGeometryColumnsTable = false;
2241 264016 : const OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
2242 264016 : 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 264016 : 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 264016 : 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 268845 : }
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 495108 : static float rtreeValueDown(double d)
2354 : {
2355 495108 : float f = static_cast<float>(d);
2356 495108 : if (f > d)
2357 : {
2358 13089 : f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
2359 : }
2360 495108 : return f;
2361 : }
2362 :
2363 495108 : static float rtreeValueUp(double d)
2364 : {
2365 495108 : float f = static_cast<float>(d);
2366 495108 : if (f < d)
2367 : {
2368 13191 : f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
2369 : }
2370 495108 : return f;
2371 : }
2372 :
2373 268776 : OGRErr OGRGeoPackageTableLayer::CreateOrUpsertFeature(OGRFeature *poFeature,
2374 : bool bUpsert)
2375 : {
2376 268776 : if (!m_bFeatureDefnCompleted)
2377 0 : GetLayerDefn();
2378 268776 : 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 268776 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2386 0 : return OGRERR_FAILURE;
2387 :
2388 268776 : CancelAsyncNextArrowArray();
2389 :
2390 537552 : std::string osUpsertUniqueColumnName;
2391 268776 : 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 268776 : 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 268776 : 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 268749 : if (m_bOGRFeatureCountTriggersEnabled)
2451 : {
2452 43 : DisableFeatureCountTriggers();
2453 : }
2454 : }
2455 : #endif
2456 :
2457 268776 : 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 268776 : poFeature->FillUnsetWithDefault(FALSE, nullptr);
2463 268776 : bool bHasDefaultValue = false;
2464 268776 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2465 4739370 : for (int iField = 0; iField < nFieldCount; iField++)
2466 : {
2467 4470590 : if (poFeature->IsFieldSetUnsafe(iField))
2468 4469340 : 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 268776 : 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 536691 : if (m_poInsertStatement &&
2528 267919 : (bHasDefaultValue ||
2529 267919 : m_bInsertStatementWithFID != (poFeature->GetFID() != OGRNullFID) ||
2530 535833 : m_bInsertStatementWithUpsert != bUpsert ||
2531 267916 : m_osInsertStatementUpsertUniqueColumnName != osUpsertUniqueColumnName))
2532 : {
2533 3 : sqlite3_finalize(m_poInsertStatement);
2534 3 : m_poInsertStatement = nullptr;
2535 : }
2536 :
2537 268772 : 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 856 : m_bInsertStatementWithFID = poFeature->GetFID() != OGRNullFID;
2544 856 : m_bInsertStatementWithUpsert = bUpsert;
2545 856 : m_osInsertStatementUpsertUniqueColumnName = osUpsertUniqueColumnName;
2546 : CPLString osCommand = FeatureGenerateInsertSQL(
2547 856 : poFeature, m_bInsertStatementWithFID, !bHasDefaultValue, bUpsert,
2548 856 : osUpsertUniqueColumnName);
2549 :
2550 : /* Prepare the SQL into a statement */
2551 856 : sqlite3 *poDb = m_poDS->GetDB();
2552 856 : int err = SQLPrepareWithError(poDb, osCommand, -1, &m_poInsertStatement,
2553 : nullptr);
2554 856 : if (err != SQLITE_OK)
2555 : {
2556 0 : return OGRERR_FAILURE;
2557 : }
2558 : }
2559 :
2560 : /* Bind values onto the statement now */
2561 537544 : OGRErr errOgr = FeatureBindInsertParameters(poFeature, m_poInsertStatement,
2562 268772 : m_bInsertStatementWithFID,
2563 268772 : !bHasDefaultValue);
2564 268772 : 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 268772 : const int err = sqlite3_step(m_poInsertStatement);
2575 268772 : if (!(err == SQLITE_OK || err == SQLITE_DONE
2576 : #if SQLITE_VERSION_NUMBER >= 3035000L
2577 : || err == SQLITE_ROW
2578 : #endif
2579 : ))
2580 : {
2581 8 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute insert : %s",
2582 8 : sqlite3_errmsg(m_poDS->GetDB())
2583 8 : ? sqlite3_errmsg(m_poDS->GetDB())
2584 : : "");
2585 8 : sqlite3_reset(m_poInsertStatement);
2586 8 : sqlite3_clear_bindings(m_poInsertStatement);
2587 8 : sqlite3_finalize(m_poInsertStatement);
2588 8 : m_poInsertStatement = nullptr;
2589 8 : return OGRERR_FAILURE;
2590 : }
2591 :
2592 : /* Read the latest FID value */
2593 27 : const GIntBig nFID = (bUpsert && !osUpsertUniqueColumnName.empty())
2594 268764 : ?
2595 : #if SQLITE_VERSION_NUMBER >= 3035000L
2596 15 : sqlite3_column_int64(m_poInsertStatement, 0)
2597 : #else
2598 : OGRNullFID
2599 : #endif
2600 268749 : : sqlite3_last_insert_rowid(m_poDS->GetDB());
2601 :
2602 268764 : sqlite3_reset(m_poInsertStatement);
2603 268764 : sqlite3_clear_bindings(m_poInsertStatement);
2604 :
2605 268764 : if (bHasDefaultValue)
2606 : {
2607 1 : sqlite3_finalize(m_poInsertStatement);
2608 1 : m_poInsertStatement = nullptr;
2609 : }
2610 :
2611 268764 : if (nFID != OGRNullFID)
2612 : {
2613 268764 : poFeature->SetFID(nFID);
2614 268764 : 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 268764 : if (IsGeomFieldSet(poFeature))
2624 : {
2625 263974 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
2626 263974 : if (!poGeom->IsEmpty())
2627 : {
2628 262933 : OGREnvelope oEnv;
2629 262933 : poGeom->getEnvelope(&oEnv);
2630 262933 : UpdateExtent(&oEnv);
2631 :
2632 262919 : if (!bUpsert && !m_bDeferredSpatialIndexCreation &&
2633 525852 : HasSpatialIndex() && m_poDS->IsInTransaction())
2634 : {
2635 1639 : m_nCountInsertInTransaction++;
2636 1639 : if (m_nCountInsertInTransactionThreshold < 0)
2637 : {
2638 15 : m_nCountInsertInTransactionThreshold =
2639 15 : atoi(CPLGetConfigOption(
2640 : "OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "100"));
2641 : }
2642 1639 : if (m_nCountInsertInTransaction ==
2643 1639 : m_nCountInsertInTransactionThreshold)
2644 : {
2645 10 : StartDeferredSpatialIndexUpdate();
2646 : }
2647 1629 : 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 246364 : sEntry.nId = nFID;
2675 246364 : sEntry.fMinX = rtreeValueDown(oEnv.MinX);
2676 246364 : sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
2677 246364 : sEntry.fMinY = rtreeValueDown(oEnv.MinY);
2678 246364 : sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
2679 : try
2680 : {
2681 246364 : m_aoRTreeEntries.push_back(sEntry);
2682 246364 : if (m_aoRTreeEntries.size() == m_nRTreeBatchSize)
2683 : {
2684 871 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
2685 871 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
2686 : }
2687 484697 : 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 268764 : if (m_nTotalFeatureCount >= 0)
2710 : {
2711 268667 : if (m_nTotalFeatureCount < std::numeric_limits<int64_t>::max())
2712 : {
2713 268666 : 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 268764 : m_bContentChanged = true;
2733 :
2734 : /* All done! */
2735 268764 : return OGRERR_NONE;
2736 : }
2737 :
2738 268749 : OGRErr OGRGeoPackageTableLayer::ICreateFeature(OGRFeature *poFeature)
2739 : {
2740 268749 : return CreateOrUpsertFeature(poFeature, /* bUpsert=*/false);
2741 : }
2742 :
2743 : /************************************************************************/
2744 : /* SetDeferredSpatialIndexCreation() */
2745 : /************************************************************************/
2746 :
2747 925 : void OGRGeoPackageTableLayer::SetDeferredSpatialIndexCreation(bool bFlag)
2748 : {
2749 925 : m_bDeferredSpatialIndexCreation = bFlag;
2750 925 : if (bFlag)
2751 : {
2752 : // This method is invoked before the layer is added to the dataset,
2753 : // so GetLayerCount() will return 0 for the first layer added.
2754 925 : m_bAllowedRTreeThread =
2755 1747 : m_poDS->GetLayerCount() == 0 && sqlite3_threadsafe() != 0 &&
2756 2569 : CPLGetNumCPUs() >= 2 &&
2757 822 : CPLTestBool(
2758 : CPLGetConfigOption("OGR_GPKG_ALLOW_THREADED_RTREE", "YES"));
2759 :
2760 : // For unit tests
2761 925 : if (CPLTestBool(CPLGetConfigOption(
2762 : "OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "NO")))
2763 : {
2764 60 : m_nRTreeBatchSize = 10;
2765 60 : m_nRTreeBatchesBeforeStart = 1;
2766 : }
2767 : }
2768 925 : }
2769 :
2770 : /************************************************************************/
2771 : /* StartAsyncRTree() */
2772 : /************************************************************************/
2773 :
2774 : // We create a temporary database with only the RTree, and we insert
2775 : // records into it in a dedicated thread, in parallel of the main thread
2776 : // that inserts rows in the user table. When the layer is finalized, we
2777 : // just use bulk copy statements of the form
2778 : // INSERT INTO rtree_xxxx_rowid/node/parent SELECT * FROM
2779 : // temp_rtree.my_rtree_rowid/node/parent to copy the RTree auxiliary tables into
2780 : // the main database, which is a very fast operation.
2781 :
2782 48 : void OGRGeoPackageTableLayer::StartAsyncRTree()
2783 : {
2784 48 : m_osAsyncDBName = m_poDS->GetDescription();
2785 48 : m_osAsyncDBName += ".tmp_rtree_";
2786 48 : bool bCanUseTableName = false;
2787 48 : if (strlen(m_pszTableName) <= 32)
2788 : {
2789 36 : bCanUseTableName = true;
2790 36 : constexpr char DIGIT_ZERO = '0';
2791 144 : for (int i = 0; m_pszTableName[i] != '\0'; ++i)
2792 : {
2793 120 : const char ch = m_pszTableName[i];
2794 132 : if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
2795 12 : (ch >= DIGIT_ZERO && ch <= '9') || ch == '.' || ch == '_'))
2796 : {
2797 12 : bCanUseTableName = false;
2798 12 : break;
2799 : }
2800 : }
2801 : }
2802 48 : if (bCanUseTableName)
2803 24 : m_osAsyncDBName += m_pszTableName;
2804 : else
2805 : {
2806 24 : m_osAsyncDBName += CPLMD5String(m_pszTableName);
2807 : }
2808 48 : m_osAsyncDBName += ".db";
2809 :
2810 48 : m_osAsyncDBAttachName = "temp_rtree_";
2811 48 : m_osAsyncDBAttachName += CPLMD5String(m_pszTableName);
2812 :
2813 48 : VSIUnlink(m_osAsyncDBName.c_str());
2814 48 : CPLDebug("GPKG", "Creating background RTree DB %s",
2815 : m_osAsyncDBName.c_str());
2816 72 : if (sqlite3_open_v2(m_osAsyncDBName.c_str(), &m_hAsyncDBHandle,
2817 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
2818 120 : m_poDS->GetVFS() ? m_poDS->GetVFS()->zName : nullptr) !=
2819 : SQLITE_OK)
2820 : {
2821 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_open_v2() of %s failed",
2822 : m_osAsyncDBName.c_str());
2823 0 : sqlite3_close(m_hAsyncDBHandle);
2824 0 : m_hAsyncDBHandle = nullptr;
2825 : }
2826 48 : if (m_hAsyncDBHandle != nullptr)
2827 : {
2828 : /* Make sure our auxiliary DB has the same page size as the main one.
2829 : * Because the number of RTree cells depends on the SQLite page size.
2830 : * However the sqlite implementation limits to 51 cells maximum per page,
2831 : * which is reached starting with a page size of 2048 bytes.
2832 : * As the default SQLite page size is 4096 currently, having potentially
2833 : * different page sizes >= 4096 between the main and auxiliary DBs would
2834 : * not be a practical issue, but better be consistent.
2835 : */
2836 : const int nPageSize =
2837 48 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA page_size", nullptr);
2838 :
2839 48 : if (SQLCommand(m_hAsyncDBHandle,
2840 : CPLSPrintf("PRAGMA page_size = %d;\n"
2841 : "PRAGMA journal_mode = OFF;\n"
2842 : "PRAGMA synchronous = OFF;",
2843 48 : nPageSize)) == OGRERR_NONE)
2844 : {
2845 48 : char *pszSQL = sqlite3_mprintf("ATTACH DATABASE '%q' AS '%q'",
2846 : m_osAsyncDBName.c_str(),
2847 : m_osAsyncDBAttachName.c_str());
2848 48 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
2849 48 : sqlite3_free(pszSQL);
2850 :
2851 48 : if (eErr == OGRERR_NONE)
2852 : {
2853 48 : m_hRTree = gdal_sqlite_rtree_bl_new(nPageSize);
2854 : try
2855 : {
2856 : m_oThreadRTree =
2857 96 : std::thread([this]() { AsyncRTreeThreadFunction(); });
2858 48 : m_bThreadRTreeStarted = true;
2859 : }
2860 0 : catch (const std::exception &e)
2861 : {
2862 0 : CPLError(CE_Failure, CPLE_AppDefined,
2863 0 : "RTree thread cannot be created: %s", e.what());
2864 : }
2865 : }
2866 : }
2867 :
2868 48 : if (!m_bThreadRTreeStarted)
2869 : {
2870 0 : if (m_hRTree)
2871 : {
2872 0 : gdal_sqlite_rtree_bl_free(m_hRTree);
2873 0 : m_hRTree = nullptr;
2874 : }
2875 0 : m_oQueueRTreeEntries.clear();
2876 0 : m_bErrorDuringRTreeThread = true;
2877 0 : sqlite3_close(m_hAsyncDBHandle);
2878 0 : m_hAsyncDBHandle = nullptr;
2879 0 : VSIUnlink(m_osAsyncDBName.c_str());
2880 : }
2881 : }
2882 : else
2883 : {
2884 0 : m_oQueueRTreeEntries.clear();
2885 0 : m_bErrorDuringRTreeThread = true;
2886 : }
2887 48 : }
2888 :
2889 : /************************************************************************/
2890 : /* RemoveAsyncRTreeTempDB() */
2891 : /************************************************************************/
2892 :
2893 48 : void OGRGeoPackageTableLayer::RemoveAsyncRTreeTempDB()
2894 : {
2895 48 : if (!m_osAsyncDBAttachName.empty())
2896 : {
2897 96 : SQLCommand(
2898 48 : m_poDS->GetDB(),
2899 : CPLSPrintf("DETACH DATABASE \"%s\"",
2900 96 : SQLEscapeName(m_osAsyncDBAttachName.c_str()).c_str()));
2901 48 : m_osAsyncDBAttachName.clear();
2902 48 : VSIUnlink(m_osAsyncDBName.c_str());
2903 48 : m_osAsyncDBName.clear();
2904 : }
2905 48 : }
2906 :
2907 : /************************************************************************/
2908 : /* CancelAsyncRTree() */
2909 : /************************************************************************/
2910 :
2911 36 : void OGRGeoPackageTableLayer::CancelAsyncRTree()
2912 : {
2913 36 : CPLDebug("GPKG", "Cancel background RTree creation");
2914 36 : m_oQueueRTreeEntries.push({});
2915 36 : m_oThreadRTree.join();
2916 36 : m_bThreadRTreeStarted = false;
2917 36 : if (m_hAsyncDBHandle)
2918 : {
2919 36 : sqlite3_close(m_hAsyncDBHandle);
2920 36 : m_hAsyncDBHandle = nullptr;
2921 : }
2922 36 : gdal_sqlite_rtree_bl_free(m_hRTree);
2923 36 : m_hRTree = nullptr;
2924 36 : m_bErrorDuringRTreeThread = true;
2925 36 : RemoveAsyncRTreeTempDB();
2926 36 : }
2927 :
2928 : /************************************************************************/
2929 : /* FinishOrDisableThreadedRTree() */
2930 : /************************************************************************/
2931 :
2932 95 : void OGRGeoPackageTableLayer::FinishOrDisableThreadedRTree()
2933 : {
2934 95 : if (m_bThreadRTreeStarted)
2935 : {
2936 12 : CreateSpatialIndexIfNecessary();
2937 : }
2938 95 : m_bAllowedRTreeThread = false;
2939 95 : }
2940 :
2941 : /************************************************************************/
2942 : /* FlushInMemoryRTree() */
2943 : /************************************************************************/
2944 :
2945 12 : bool OGRGeoPackageTableLayer::FlushInMemoryRTree(sqlite3 *hRTreeDB,
2946 : const char *pszRTreeName)
2947 : {
2948 12 : if (hRTreeDB == m_hAsyncDBHandle)
2949 8 : SQLCommand(hRTreeDB, "BEGIN");
2950 :
2951 12 : char *pszErrMsg = nullptr;
2952 12 : bool bRet = gdal_sqlite_rtree_bl_serialize(m_hRTree, hRTreeDB, pszRTreeName,
2953 : "id", "minx", "miny", "maxx",
2954 : "maxy", &pszErrMsg);
2955 12 : if (hRTreeDB == m_hAsyncDBHandle)
2956 : {
2957 8 : if (bRet)
2958 8 : bRet = SQLCommand(hRTreeDB, "COMMIT") == OGRERR_NONE;
2959 : else
2960 0 : SQLCommand(hRTreeDB, "ROLLBACK");
2961 : }
2962 :
2963 12 : gdal_sqlite_rtree_bl_free(m_hRTree);
2964 12 : m_hRTree = nullptr;
2965 :
2966 12 : if (!bRet)
2967 : {
2968 0 : CPLError(CE_Failure, CPLE_AppDefined,
2969 : "sqlite_rtree_bl_serialize() failed with %s",
2970 0 : pszErrMsg ? pszErrMsg : "(null)");
2971 :
2972 0 : m_bErrorDuringRTreeThread = true;
2973 :
2974 0 : if (m_hAsyncDBHandle)
2975 : {
2976 0 : sqlite3_close(m_hAsyncDBHandle);
2977 0 : m_hAsyncDBHandle = nullptr;
2978 : }
2979 :
2980 0 : m_oQueueRTreeEntries.clear();
2981 : }
2982 12 : sqlite3_free(pszErrMsg);
2983 :
2984 12 : return bRet;
2985 : }
2986 :
2987 : /************************************************************************/
2988 : /* GetMaxRAMUsageAllowedForRTree() */
2989 : /************************************************************************/
2990 :
2991 972 : static size_t GetMaxRAMUsageAllowedForRTree()
2992 : {
2993 972 : const uint64_t nUsableRAM = CPLGetUsablePhysicalRAM();
2994 972 : uint64_t nMaxRAMUsageAllowed =
2995 972 : (nUsableRAM ? nUsableRAM / 10 : 100 * 1024 * 1024);
2996 : const char *pszMaxRAMUsageAllowed =
2997 972 : CPLGetConfigOption("OGR_GPKG_MAX_RAM_USAGE_RTREE", nullptr);
2998 972 : if (pszMaxRAMUsageAllowed)
2999 : {
3000 : nMaxRAMUsageAllowed = static_cast<uint64_t>(
3001 20 : std::strtoull(pszMaxRAMUsageAllowed, nullptr, 10));
3002 : }
3003 972 : if (nMaxRAMUsageAllowed > std::numeric_limits<size_t>::max() - 1U)
3004 : {
3005 0 : nMaxRAMUsageAllowed = std::numeric_limits<size_t>::max() - 1U;
3006 : }
3007 972 : return static_cast<size_t>(nMaxRAMUsageAllowed);
3008 : }
3009 :
3010 : /************************************************************************/
3011 : /* AsyncRTreeThreadFunction() */
3012 : /************************************************************************/
3013 :
3014 48 : void OGRGeoPackageTableLayer::AsyncRTreeThreadFunction()
3015 : {
3016 48 : CPLAssert(m_hRTree);
3017 :
3018 48 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
3019 48 : sqlite3_stmt *hStmt = nullptr;
3020 48 : GIntBig nCount = 0;
3021 : while (true)
3022 : {
3023 884 : const auto aoEntries = m_oQueueRTreeEntries.get_and_pop_front();
3024 884 : if (aoEntries.empty())
3025 44 : break;
3026 :
3027 840 : constexpr int NOTIFICATION_INTERVAL = 500 * 1000;
3028 :
3029 840 : auto oIter = aoEntries.begin();
3030 840 : if (m_hRTree)
3031 : {
3032 4808 : for (; oIter != aoEntries.end(); ++oIter)
3033 : {
3034 4372 : const auto &entry = *oIter;
3035 4372 : if (gdal_sqlite_rtree_bl_ram_usage(m_hRTree) >
3036 8736 : nMaxRAMUsageAllowed ||
3037 4364 : !gdal_sqlite_rtree_bl_insert(m_hRTree, entry.nId,
3038 4364 : entry.fMinX, entry.fMinY,
3039 4364 : entry.fMaxX, entry.fMaxY))
3040 : {
3041 8 : CPLDebug("GPKG", "Too large in-memory RTree. "
3042 : "Flushing it and using memory friendly "
3043 : "algorithm for the rest");
3044 8 : if (!FlushInMemoryRTree(m_hAsyncDBHandle, "my_rtree"))
3045 0 : return;
3046 8 : break;
3047 : }
3048 4364 : ++nCount;
3049 4364 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
3050 : {
3051 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree",
3052 : nCount);
3053 : }
3054 : }
3055 444 : if (oIter == aoEntries.end())
3056 436 : continue;
3057 : }
3058 :
3059 404 : if (hStmt == nullptr)
3060 : {
3061 : const char *pszInsertSQL =
3062 8 : CPLGetConfigOption(
3063 : "OGR_GPKG_SIMULATE_INSERT_INTO_MY_RTREE_PREPARATION_ERROR",
3064 : nullptr)
3065 8 : ? "INSERT INTO my_rtree_SIMULATE_ERROR VALUES (?,?,?,?,?)"
3066 8 : : "INSERT INTO my_rtree VALUES (?,?,?,?,?)";
3067 8 : if (SQLPrepareWithError(m_hAsyncDBHandle, pszInsertSQL, -1, &hStmt,
3068 8 : nullptr) != SQLITE_OK)
3069 : {
3070 4 : m_bErrorDuringRTreeThread = true;
3071 :
3072 4 : sqlite3_close(m_hAsyncDBHandle);
3073 4 : m_hAsyncDBHandle = nullptr;
3074 :
3075 4 : m_oQueueRTreeEntries.clear();
3076 4 : return;
3077 : }
3078 :
3079 4 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3080 : }
3081 :
3082 : #ifdef DEBUG_VERBOSE
3083 : CPLDebug("GPKG",
3084 : "AsyncRTreeThreadFunction(): "
3085 : "Processing batch of %d features, "
3086 : "starting at FID " CPL_FRMT_GIB " and ending "
3087 : "at FID " CPL_FRMT_GIB,
3088 : static_cast<int>(aoEntries.size()), aoEntries.front().nId,
3089 : aoEntries.back().nId);
3090 : #endif
3091 4398 : for (; oIter != aoEntries.end(); ++oIter)
3092 : {
3093 3998 : const auto &entry = *oIter;
3094 3998 : sqlite3_reset(hStmt);
3095 :
3096 3998 : sqlite3_bind_int64(hStmt, 1, entry.nId);
3097 3998 : sqlite3_bind_double(hStmt, 2, entry.fMinX);
3098 3998 : sqlite3_bind_double(hStmt, 3, entry.fMaxX);
3099 3998 : sqlite3_bind_double(hStmt, 4, entry.fMinY);
3100 3998 : sqlite3_bind_double(hStmt, 5, entry.fMaxY);
3101 3998 : int sqlite_err = sqlite3_step(hStmt);
3102 3998 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
3103 : {
3104 0 : CPLError(CE_Failure, CPLE_AppDefined,
3105 : "failed to execute insertion in RTree : %s",
3106 : sqlite3_errmsg(m_hAsyncDBHandle));
3107 0 : m_bErrorDuringRTreeThread = true;
3108 0 : break;
3109 : }
3110 3998 : ++nCount;
3111 3998 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
3112 : {
3113 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree", nCount);
3114 0 : if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3115 : {
3116 0 : m_bErrorDuringRTreeThread = true;
3117 0 : break;
3118 : }
3119 0 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3120 : }
3121 : }
3122 836 : }
3123 44 : if (!m_hRTree)
3124 : {
3125 4 : if (m_bErrorDuringRTreeThread)
3126 : {
3127 0 : SQLCommand(m_hAsyncDBHandle, "ROLLBACK");
3128 : }
3129 4 : else if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3130 : {
3131 0 : m_bErrorDuringRTreeThread = true;
3132 : }
3133 :
3134 4 : sqlite3_finalize(hStmt);
3135 :
3136 4 : if (m_bErrorDuringRTreeThread)
3137 : {
3138 0 : sqlite3_close(m_hAsyncDBHandle);
3139 0 : m_hAsyncDBHandle = nullptr;
3140 :
3141 0 : VSIUnlink(m_osAsyncDBName.c_str());
3142 :
3143 0 : m_oQueueRTreeEntries.clear();
3144 : }
3145 : }
3146 44 : CPLDebug("GPKG",
3147 : "AsyncRTreeThreadFunction(): " CPL_FRMT_GIB
3148 : " rows inserted into RTree",
3149 : nCount);
3150 : }
3151 :
3152 : /************************************************************************/
3153 : /* ISetFeature() */
3154 : /************************************************************************/
3155 :
3156 59 : OGRErr OGRGeoPackageTableLayer::ISetFeature(OGRFeature *poFeature)
3157 : {
3158 59 : if (!m_bFeatureDefnCompleted)
3159 0 : GetLayerDefn();
3160 59 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3161 : {
3162 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3163 : "SetFeature");
3164 0 : return OGRERR_FAILURE;
3165 : }
3166 :
3167 : /* No FID? */
3168 59 : if (poFeature->GetFID() == OGRNullFID)
3169 : {
3170 0 : CPLError(CE_Failure, CPLE_AppDefined,
3171 : "FID required on features given to SetFeature().");
3172 0 : return OGRERR_FAILURE;
3173 : }
3174 :
3175 : /* In case the FID column has also been created as a regular field */
3176 68 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3177 9 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3178 : {
3179 1 : return OGRERR_FAILURE;
3180 : }
3181 :
3182 58 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3183 0 : return OGRERR_FAILURE;
3184 :
3185 58 : CancelAsyncNextArrowArray();
3186 :
3187 58 : if (m_bThreadRTreeStarted)
3188 12 : CancelAsyncRTree();
3189 58 : if (!RunDeferredSpatialIndexUpdate())
3190 0 : return OGRERR_FAILURE;
3191 :
3192 : const sqlite3_int64 nTotalChangesBefore =
3193 : #if SQLITE_VERSION_NUMBER >= 3037000L
3194 58 : sqlite3_total_changes64(m_poDS->GetDB());
3195 : #else
3196 : sqlite3_total_changes(m_poDS->GetDB());
3197 : #endif
3198 :
3199 58 : CheckGeometryType(poFeature);
3200 :
3201 58 : if (!m_osUpdateStatementSQL.empty())
3202 : {
3203 1 : m_osUpdateStatementSQL.clear();
3204 1 : if (m_poUpdateStatement)
3205 1 : sqlite3_finalize(m_poUpdateStatement);
3206 1 : m_poUpdateStatement = nullptr;
3207 : }
3208 58 : if (!m_poUpdateStatement)
3209 : {
3210 : /* Construct a SQL UPDATE statement from the OGRFeature */
3211 : /* Only work with fields that are set */
3212 : /* Do not stick values into SQL, use placeholder and bind values later
3213 : */
3214 46 : const std::string osCommand = FeatureGenerateUpdateSQL(poFeature);
3215 46 : if (osCommand.empty())
3216 1 : return OGRERR_NONE;
3217 :
3218 : /* Prepare the SQL into a statement */
3219 45 : int err = SQLPrepareWithError(m_poDS->GetDB(), osCommand.c_str(),
3220 45 : static_cast<int>(osCommand.size()),
3221 : &m_poUpdateStatement, nullptr);
3222 45 : if (err != SQLITE_OK)
3223 : {
3224 1 : return OGRERR_FAILURE;
3225 : }
3226 : }
3227 :
3228 : /* Bind values onto the statement now */
3229 56 : OGRErr errOgr = FeatureBindUpdateParameters(poFeature, m_poUpdateStatement);
3230 56 : if (errOgr != OGRERR_NONE)
3231 : {
3232 0 : sqlite3_reset(m_poUpdateStatement);
3233 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3234 0 : return errOgr;
3235 : }
3236 :
3237 : /* From here execute the statement and check errors */
3238 56 : int err = sqlite3_step(m_poUpdateStatement);
3239 56 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3240 : {
3241 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3242 0 : sqlite3_errmsg(m_poDS->GetDB()));
3243 0 : sqlite3_reset(m_poUpdateStatement);
3244 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3245 0 : return OGRERR_FAILURE;
3246 : }
3247 :
3248 56 : sqlite3_reset(m_poUpdateStatement);
3249 56 : sqlite3_clear_bindings(m_poUpdateStatement);
3250 :
3251 : const sqlite3_int64 nTotalChangesAfter =
3252 : #if SQLITE_VERSION_NUMBER >= 3037000L
3253 56 : sqlite3_total_changes64(m_poDS->GetDB());
3254 : #else
3255 : sqlite3_total_changes(m_poDS->GetDB());
3256 : #endif
3257 :
3258 : /* Only update the envelope if we changed something */
3259 56 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3260 56 : ? OGRERR_NONE
3261 : : OGRERR_NON_EXISTING_FEATURE;
3262 56 : if (eErr == OGRERR_NONE)
3263 : {
3264 : /* Update the layer extents with this new object */
3265 50 : if (IsGeomFieldSet(poFeature))
3266 : {
3267 36 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3268 36 : if (!poGeom->IsEmpty())
3269 : {
3270 36 : OGREnvelope oEnv;
3271 36 : poGeom->getEnvelope(&oEnv);
3272 36 : UpdateExtent(&oEnv);
3273 : }
3274 : }
3275 :
3276 50 : m_bContentChanged = true;
3277 : }
3278 :
3279 : /* All done! */
3280 56 : return eErr;
3281 : }
3282 :
3283 : /************************************************************************/
3284 : /* IUpsertFeature() */
3285 : /************************************************************************/
3286 :
3287 27 : OGRErr OGRGeoPackageTableLayer::IUpsertFeature(OGRFeature *poFeature)
3288 :
3289 : {
3290 27 : return CreateOrUpsertFeature(poFeature, /* bUpsert = */ true);
3291 : }
3292 :
3293 : //----------------------------------------------------------------------
3294 : // FeatureGenerateUpdateSQL()
3295 : //
3296 : // Build a SQL UPDATE statement that references all the columns in
3297 : // the OGRFeatureDefn that the user asked to be updated, then prepare it for
3298 : // repeated use in a prepared statement. All statements start off with geometry
3299 : // (if it exists, and if it is asked to be updated), then reference each column
3300 : // in the order it appears in the OGRFeatureDefn.
3301 : // FeatureBindParameters operates on the expectation of this
3302 : // column ordering.
3303 :
3304 : //
3305 11 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
3306 : const OGRFeature *poFeature, int nUpdatedFieldsCount,
3307 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3308 : const int * /*panUpdatedGeomFieldsIdx*/) const
3309 : {
3310 11 : bool bNeedComma = false;
3311 11 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
3312 :
3313 : /* Set up our SQL string basics */
3314 22 : std::string osUpdate("UPDATE \"");
3315 11 : osUpdate += SQLEscapeName(m_pszTableName);
3316 11 : osUpdate += "\" SET ";
3317 :
3318 11 : if (nUpdatedGeomFieldsCount == 1 && poFeatureDefn->GetGeomFieldCount() > 0)
3319 : {
3320 2 : osUpdate += '"';
3321 : osUpdate +=
3322 2 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
3323 2 : osUpdate += "\"=?";
3324 2 : bNeedComma = true;
3325 : }
3326 :
3327 : /* Add attribute column names (except FID) to the SQL */
3328 20 : for (int i = 0; i < nUpdatedFieldsCount; i++)
3329 : {
3330 9 : const int iField = panUpdatedFieldsIdx[i];
3331 18 : if (iField == m_iFIDAsRegularColumnIndex ||
3332 9 : poFeatureDefn->GetFieldDefn(iField)->IsGenerated())
3333 0 : continue;
3334 9 : if (!poFeature->IsFieldSet(iField))
3335 1 : continue;
3336 8 : if (!bNeedComma)
3337 8 : bNeedComma = true;
3338 : else
3339 0 : osUpdate += ", ";
3340 :
3341 8 : osUpdate += '"';
3342 : osUpdate +=
3343 8 : SQLEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
3344 8 : osUpdate += "\"=?";
3345 : }
3346 11 : if (!bNeedComma)
3347 1 : return CPLString();
3348 :
3349 10 : osUpdate += " WHERE \"";
3350 10 : osUpdate += SQLEscapeName(m_pszFidColumn);
3351 10 : osUpdate += "\" = ?";
3352 :
3353 10 : return osUpdate;
3354 : }
3355 :
3356 : /************************************************************************/
3357 : /* UpdateFeature() */
3358 : /************************************************************************/
3359 :
3360 11 : OGRErr OGRGeoPackageTableLayer::IUpdateFeature(
3361 : OGRFeature *poFeature, int nUpdatedFieldsCount,
3362 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3363 : const int *panUpdatedGeomFieldsIdx, bool /* bUpdateStyleString*/)
3364 :
3365 : {
3366 11 : if (!m_bFeatureDefnCompleted)
3367 0 : GetLayerDefn();
3368 11 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3369 : {
3370 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3371 : "UpdateFeature");
3372 0 : return OGRERR_FAILURE;
3373 : }
3374 :
3375 : /* No FID? */
3376 11 : if (poFeature->GetFID() == OGRNullFID)
3377 : {
3378 0 : CPLError(CE_Failure, CPLE_AppDefined,
3379 : "FID required on features given to SetFeature().");
3380 0 : return OGRERR_FAILURE;
3381 : }
3382 :
3383 : /* In case the FID column has also been created as a regular field */
3384 11 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3385 0 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3386 : {
3387 0 : return OGRERR_FAILURE;
3388 : }
3389 :
3390 11 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3391 0 : return OGRERR_FAILURE;
3392 :
3393 11 : CancelAsyncNextArrowArray();
3394 :
3395 11 : if (m_bThreadRTreeStarted)
3396 0 : CancelAsyncRTree();
3397 11 : if (!RunDeferredSpatialIndexUpdate())
3398 0 : return OGRERR_FAILURE;
3399 :
3400 11 : CheckGeometryType(poFeature);
3401 :
3402 : /* Construct a SQL UPDATE statement from the OGRFeature */
3403 : /* Only work with fields that are set */
3404 : /* Do not stick values into SQL, use placeholder and bind values later
3405 : */
3406 : std::string osUpdateStatementSQL = FeatureGenerateUpdateSQL(
3407 : poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3408 22 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3409 11 : if (osUpdateStatementSQL.empty())
3410 1 : return OGRERR_NONE;
3411 :
3412 10 : if (m_osUpdateStatementSQL != osUpdateStatementSQL)
3413 : {
3414 8 : if (m_poUpdateStatement)
3415 5 : sqlite3_finalize(m_poUpdateStatement);
3416 8 : m_poUpdateStatement = nullptr;
3417 : /* Prepare the SQL into a statement */
3418 : int err =
3419 8 : SQLPrepareWithError(m_poDS->GetDB(), osUpdateStatementSQL.c_str(),
3420 8 : static_cast<int>(osUpdateStatementSQL.size()),
3421 : &m_poUpdateStatement, nullptr);
3422 8 : if (err != SQLITE_OK)
3423 : {
3424 0 : return OGRERR_FAILURE;
3425 : }
3426 8 : m_osUpdateStatementSQL = std::move(osUpdateStatementSQL);
3427 : }
3428 :
3429 : /* Bind values onto the statement now */
3430 10 : int nColCount = 0;
3431 : const OGRErr errOgr =
3432 10 : FeatureBindParameters(poFeature, m_poUpdateStatement, &nColCount, false,
3433 : false, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3434 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3435 10 : if (errOgr != OGRERR_NONE)
3436 : {
3437 0 : sqlite3_reset(m_poUpdateStatement);
3438 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3439 0 : return errOgr;
3440 : }
3441 :
3442 : // Bind the FID to the "WHERE" clause.
3443 : const int sqlite_err =
3444 10 : sqlite3_bind_int64(m_poUpdateStatement, nColCount, poFeature->GetFID());
3445 10 : if (sqlite_err != SQLITE_OK)
3446 : {
3447 0 : CPLError(CE_Failure, CPLE_AppDefined,
3448 : "failed to bind FID '" CPL_FRMT_GIB "' to statement",
3449 : poFeature->GetFID());
3450 0 : sqlite3_reset(m_poUpdateStatement);
3451 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3452 0 : return OGRERR_FAILURE;
3453 : }
3454 :
3455 : const sqlite3_int64 nTotalChangesBefore =
3456 : #if SQLITE_VERSION_NUMBER >= 3037000L
3457 10 : sqlite3_total_changes64(m_poDS->GetDB());
3458 : #else
3459 : sqlite3_total_changes(m_poDS->GetDB());
3460 : #endif
3461 :
3462 : /* From here execute the statement and check errors */
3463 10 : int err = sqlite3_step(m_poUpdateStatement);
3464 10 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3465 : {
3466 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3467 0 : sqlite3_errmsg(m_poDS->GetDB()));
3468 0 : sqlite3_reset(m_poUpdateStatement);
3469 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3470 0 : return OGRERR_FAILURE;
3471 : }
3472 :
3473 10 : sqlite3_reset(m_poUpdateStatement);
3474 10 : sqlite3_clear_bindings(m_poUpdateStatement);
3475 :
3476 : const sqlite3_int64 nTotalChangesAfter =
3477 : #if SQLITE_VERSION_NUMBER >= 3037000L
3478 10 : sqlite3_total_changes64(m_poDS->GetDB());
3479 : #else
3480 : sqlite3_total_changes(m_poDS->GetDB());
3481 : #endif
3482 :
3483 : /* Only update the envelope if we changed something */
3484 10 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3485 10 : ? OGRERR_NONE
3486 : : OGRERR_NON_EXISTING_FEATURE;
3487 10 : if (eErr == OGRERR_NONE)
3488 : {
3489 : /* Update the layer extents with this new object */
3490 9 : if (nUpdatedGeomFieldsCount == 1 && IsGeomFieldSet(poFeature))
3491 : {
3492 1 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3493 1 : if (!poGeom->IsEmpty())
3494 : {
3495 1 : OGREnvelope oEnv;
3496 1 : poGeom->getEnvelope(&oEnv);
3497 1 : UpdateExtent(&oEnv);
3498 : }
3499 : }
3500 :
3501 9 : m_bContentChanged = true;
3502 : }
3503 :
3504 : /* All done! */
3505 10 : return eErr;
3506 : }
3507 :
3508 : /************************************************************************/
3509 : /* SetAttributeFilter() */
3510 : /************************************************************************/
3511 :
3512 89 : OGRErr OGRGeoPackageTableLayer::SetAttributeFilter(const char *pszQuery)
3513 :
3514 : {
3515 89 : if (!m_bFeatureDefnCompleted)
3516 9 : GetLayerDefn();
3517 89 : CPLFree(m_pszAttrQueryString);
3518 89 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
3519 :
3520 89 : if (pszQuery == nullptr)
3521 40 : osQuery = "";
3522 : else
3523 49 : osQuery = pszQuery;
3524 :
3525 89 : BuildWhere();
3526 :
3527 89 : ResetReading();
3528 :
3529 89 : return OGRERR_NONE;
3530 : }
3531 :
3532 : /************************************************************************/
3533 : /* ResetReading() */
3534 : /************************************************************************/
3535 :
3536 25802 : void OGRGeoPackageTableLayer::ResetReading()
3537 : {
3538 25802 : m_bEOF = false;
3539 25802 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3540 0 : return;
3541 :
3542 25802 : OGRGeoPackageLayer::ResetReading();
3543 :
3544 25802 : if (m_poInsertStatement)
3545 : {
3546 158 : sqlite3_finalize(m_poInsertStatement);
3547 158 : m_poInsertStatement = nullptr;
3548 : }
3549 :
3550 25802 : if (m_poUpdateStatement)
3551 : {
3552 23 : sqlite3_finalize(m_poUpdateStatement);
3553 23 : m_poUpdateStatement = nullptr;
3554 : }
3555 25802 : m_osUpdateStatementSQL.clear();
3556 :
3557 25802 : if (m_poGetFeatureStatement)
3558 : {
3559 22 : sqlite3_finalize(m_poGetFeatureStatement);
3560 22 : m_poGetFeatureStatement = nullptr;
3561 : }
3562 :
3563 25802 : CancelAsyncNextArrowArray();
3564 :
3565 25802 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3566 :
3567 25802 : BuildColumns();
3568 : }
3569 :
3570 : /************************************************************************/
3571 : /* SetNextByIndex() */
3572 : /************************************************************************/
3573 :
3574 17 : OGRErr OGRGeoPackageTableLayer::SetNextByIndex(GIntBig nIndex)
3575 : {
3576 17 : if (nIndex < 0)
3577 : {
3578 3 : m_bEOF = true;
3579 3 : return OGRERR_NON_EXISTING_FEATURE;
3580 : }
3581 14 : if (m_soColumns.empty())
3582 2 : BuildColumns();
3583 14 : return ResetStatementInternal(nIndex);
3584 : }
3585 :
3586 : /************************************************************************/
3587 : /* ResetStatement() */
3588 : /************************************************************************/
3589 :
3590 895 : OGRErr OGRGeoPackageTableLayer::ResetStatement()
3591 :
3592 : {
3593 895 : return ResetStatementInternal(0);
3594 : }
3595 :
3596 : /************************************************************************/
3597 : /* ResetStatementInternal() */
3598 : /************************************************************************/
3599 :
3600 909 : OGRErr OGRGeoPackageTableLayer::ResetStatementInternal(GIntBig nStartIndex)
3601 :
3602 : {
3603 909 : ClearStatement();
3604 :
3605 : /* There is no active query statement set up, */
3606 : /* so job #1 is to prepare the statement. */
3607 : /* Append the attribute filter, if there is one */
3608 1818 : CPLString soSQL;
3609 909 : if (!m_soFilter.empty())
3610 : {
3611 : soSQL.Printf("SELECT %s FROM \"%s\" m WHERE %s", m_soColumns.c_str(),
3612 280 : SQLEscapeName(m_pszTableName).c_str(), m_soFilter.c_str());
3613 :
3614 545 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
3615 265 : HasSpatialIndex())
3616 : {
3617 264 : OGREnvelope sEnvelope;
3618 :
3619 264 : m_poFilterGeom->getEnvelope(&sEnvelope);
3620 :
3621 264 : bool bUseSpatialIndex = true;
3622 522 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
3623 169 : sEnvelope.MinY <= m_poExtent->MinY &&
3624 662 : sEnvelope.MaxX >= m_poExtent->MaxX &&
3625 140 : sEnvelope.MaxY >= m_poExtent->MaxY)
3626 : {
3627 : // Selecting from spatial filter on whole extent can be rather
3628 : // slow. So use function based filtering, just in case the
3629 : // advertized global extent might be wrong. Otherwise we might
3630 : // just discard completely the spatial filter.
3631 137 : bUseSpatialIndex = false;
3632 : }
3633 :
3634 127 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
3635 518 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
3636 127 : !std::isinf(sEnvelope.MaxY))
3637 : {
3638 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3639 : "JOIN \"%s\" r "
3640 : "ON m.\"%s\" = r.id WHERE "
3641 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
3642 : "r.maxy >= %.12f AND r.miny <= %.12f",
3643 : m_soColumns.c_str(),
3644 254 : SQLEscapeName(m_pszTableName).c_str(),
3645 254 : SQLEscapeName(m_osRTreeName).c_str(),
3646 254 : SQLEscapeName(m_osFIDForRTree).c_str(),
3647 127 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
3648 508 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
3649 : }
3650 : }
3651 : }
3652 : else
3653 : soSQL.Printf("SELECT %s FROM \"%s\" m", m_soColumns.c_str(),
3654 629 : SQLEscapeName(m_pszTableName).c_str());
3655 909 : if (nStartIndex > 0)
3656 : {
3657 11 : soSQL += CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB, nStartIndex);
3658 : }
3659 :
3660 909 : CPLDebug("GPKG", "ResetStatement(%s)", soSQL.c_str());
3661 :
3662 909 : int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
3663 : &m_poQueryStatement, nullptr);
3664 909 : if (err != SQLITE_OK)
3665 : {
3666 1 : return OGRERR_FAILURE;
3667 : }
3668 :
3669 908 : m_iNextShapeId = nStartIndex;
3670 908 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3671 :
3672 908 : return OGRERR_NONE;
3673 : }
3674 :
3675 : /************************************************************************/
3676 : /* GetNextFeature() */
3677 : /************************************************************************/
3678 :
3679 12198 : OGRFeature *OGRGeoPackageTableLayer::GetNextFeature()
3680 : {
3681 12198 : if (m_bEOF)
3682 40 : return nullptr;
3683 12158 : if (!m_bFeatureDefnCompleted)
3684 36 : GetLayerDefn();
3685 12158 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3686 0 : return nullptr;
3687 :
3688 12158 : CancelAsyncNextArrowArray();
3689 :
3690 12158 : if (m_poFilterGeom != nullptr)
3691 : {
3692 : // Both are exclusive
3693 10175 : CreateSpatialIndexIfNecessary();
3694 10175 : if (!RunDeferredSpatialIndexUpdate())
3695 0 : return nullptr;
3696 : }
3697 :
3698 12158 : OGRFeature *poFeature = OGRGeoPackageLayer::GetNextFeature();
3699 12158 : if (poFeature && m_iFIDAsRegularColumnIndex >= 0)
3700 : {
3701 1 : poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
3702 : }
3703 12158 : return poFeature;
3704 : }
3705 :
3706 : /************************************************************************/
3707 : /* GetFeature() */
3708 : /************************************************************************/
3709 :
3710 1302 : OGRFeature *OGRGeoPackageTableLayer::GetFeature(GIntBig nFID)
3711 : {
3712 1302 : if (!m_bFeatureDefnCompleted)
3713 11 : GetLayerDefn();
3714 1302 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3715 0 : return nullptr;
3716 1302 : CancelAsyncNextArrowArray();
3717 :
3718 1302 : if (m_pszFidColumn == nullptr)
3719 1 : return OGRLayer::GetFeature(nFID);
3720 :
3721 1301 : if (m_poGetFeatureStatement == nullptr)
3722 : {
3723 56 : CPLString soSQL;
3724 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3725 : "WHERE \"%s\" = ?",
3726 112 : m_soColumns.c_str(), SQLEscapeName(m_pszTableName).c_str(),
3727 168 : SQLEscapeName(m_pszFidColumn).c_str());
3728 :
3729 56 : const int err = SQLPrepareWithError(m_poDS->GetDB(), soSQL.c_str(), -1,
3730 : &m_poGetFeatureStatement, nullptr);
3731 56 : if (err != SQLITE_OK)
3732 : {
3733 0 : return nullptr;
3734 : }
3735 : }
3736 :
3737 1301 : CPL_IGNORE_RET_VAL(sqlite3_bind_int64(m_poGetFeatureStatement, 1, nFID));
3738 :
3739 : /* Should be only one or zero results */
3740 1301 : const int err = sqlite3_step(m_poGetFeatureStatement);
3741 :
3742 : /* Aha, got one */
3743 1301 : if (err == SQLITE_ROW)
3744 : {
3745 1289 : OGRFeature *poFeature = TranslateFeature(m_poGetFeatureStatement);
3746 1289 : if (m_iFIDAsRegularColumnIndex >= 0)
3747 : {
3748 7 : poFeature->SetField(m_iFIDAsRegularColumnIndex,
3749 : poFeature->GetFID());
3750 : }
3751 :
3752 1289 : sqlite3_reset(m_poGetFeatureStatement);
3753 1289 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3754 :
3755 1289 : return poFeature;
3756 : }
3757 :
3758 12 : sqlite3_reset(m_poGetFeatureStatement);
3759 12 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3760 :
3761 : /* Error out on all other return codes */
3762 12 : return nullptr;
3763 : }
3764 :
3765 : /************************************************************************/
3766 : /* DeleteFeature() */
3767 : /************************************************************************/
3768 :
3769 60 : OGRErr OGRGeoPackageTableLayer::DeleteFeature(GIntBig nFID)
3770 : {
3771 60 : if (!m_bFeatureDefnCompleted)
3772 4 : GetLayerDefn();
3773 60 : if (!m_poDS->GetUpdate())
3774 : {
3775 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3776 : "DeleteFeature");
3777 0 : return OGRERR_FAILURE;
3778 : }
3779 60 : if (m_pszFidColumn == nullptr)
3780 : {
3781 0 : return OGRERR_FAILURE;
3782 : }
3783 :
3784 60 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3785 0 : return OGRERR_FAILURE;
3786 :
3787 60 : CancelAsyncNextArrowArray();
3788 :
3789 60 : if (m_bThreadRTreeStarted)
3790 12 : CancelAsyncRTree();
3791 :
3792 60 : if (!RunDeferredSpatialIndexUpdate())
3793 0 : return OGRERR_FAILURE;
3794 :
3795 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3796 60 : if (m_bOGRFeatureCountTriggersEnabled)
3797 : {
3798 17 : DisableFeatureCountTriggers();
3799 : }
3800 : #endif
3801 :
3802 : /* Clear out any existing query */
3803 60 : ResetReading();
3804 :
3805 : /* No filters apply, just use the FID */
3806 60 : CPLString soSQL;
3807 : soSQL.Printf("DELETE FROM \"%s\" WHERE \"%s\" = " CPL_FRMT_GIB,
3808 120 : SQLEscapeName(m_pszTableName).c_str(),
3809 180 : SQLEscapeName(m_pszFidColumn).c_str(), nFID);
3810 :
3811 : const sqlite3_int64 nTotalChangesBefore =
3812 : #if SQLITE_VERSION_NUMBER >= 3037000L
3813 60 : sqlite3_total_changes64(m_poDS->GetDB());
3814 : #else
3815 : sqlite3_total_changes(m_poDS->GetDB());
3816 : #endif
3817 :
3818 60 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), soSQL.c_str());
3819 60 : if (eErr == OGRERR_NONE)
3820 : {
3821 : const sqlite3_int64 nTotalChangesAfter =
3822 : #if SQLITE_VERSION_NUMBER >= 3037000L
3823 59 : sqlite3_total_changes64(m_poDS->GetDB());
3824 : #else
3825 : sqlite3_total_changes(m_poDS->GetDB());
3826 : #endif
3827 :
3828 59 : eErr = nTotalChangesAfter != nTotalChangesBefore
3829 59 : ? OGRERR_NONE
3830 : : OGRERR_NON_EXISTING_FEATURE;
3831 :
3832 59 : if (eErr == OGRERR_NONE)
3833 : {
3834 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3835 51 : if (m_nTotalFeatureCount >= 0)
3836 43 : m_nTotalFeatureCount--;
3837 : #endif
3838 :
3839 51 : m_bContentChanged = true;
3840 : }
3841 : }
3842 60 : return eErr;
3843 : }
3844 :
3845 : /************************************************************************/
3846 : /* DoJobAtTransactionCommit() */
3847 : /************************************************************************/
3848 :
3849 469 : bool OGRGeoPackageTableLayer::DoJobAtTransactionCommit()
3850 : {
3851 469 : if (m_bAllowedRTreeThread)
3852 315 : return true;
3853 :
3854 308 : bool ret = RunDeferredCreationIfNecessary() == OGRERR_NONE &&
3855 154 : RunDeferredSpatialIndexUpdate();
3856 154 : m_nCountInsertInTransaction = 0;
3857 154 : m_aoRTreeTriggersSQL.clear();
3858 154 : m_aoRTreeEntries.clear();
3859 154 : return ret;
3860 : }
3861 :
3862 : /************************************************************************/
3863 : /* DoJobAtTransactionRollback() */
3864 : /************************************************************************/
3865 :
3866 38 : bool OGRGeoPackageTableLayer::DoJobAtTransactionRollback()
3867 : {
3868 38 : if (m_bThreadRTreeStarted)
3869 12 : CancelAsyncRTree();
3870 38 : m_nCountInsertInTransaction = 0;
3871 38 : m_aoRTreeTriggersSQL.clear();
3872 38 : m_aoRTreeEntries.clear();
3873 38 : if (m_bTableCreatedInTransaction)
3874 : {
3875 2 : SyncToDisk();
3876 : }
3877 : else
3878 : {
3879 36 : bool bDeferredSpatialIndexCreationBackup =
3880 : m_bDeferredSpatialIndexCreation;
3881 36 : m_bDeferredSpatialIndexCreation = false;
3882 36 : SyncToDisk();
3883 36 : m_bDeferredSpatialIndexCreation = bDeferredSpatialIndexCreationBackup;
3884 : }
3885 :
3886 38 : ResetReading();
3887 38 : return true;
3888 : }
3889 :
3890 : /************************************************************************/
3891 : /* StartDeferredSpatialIndexUpdate() */
3892 : /************************************************************************/
3893 :
3894 10 : bool OGRGeoPackageTableLayer::StartDeferredSpatialIndexUpdate()
3895 : {
3896 10 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
3897 0 : return true;
3898 :
3899 10 : RevertWorkaroundUpdate1TriggerIssue();
3900 :
3901 10 : m_aoRTreeTriggersSQL.clear();
3902 10 : m_aoRTreeEntries.clear();
3903 :
3904 10 : const char *pszT = m_pszTableName;
3905 10 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3906 10 : m_osRTreeName = "rtree_";
3907 10 : m_osRTreeName += pszT;
3908 10 : m_osRTreeName += "_";
3909 10 : m_osRTreeName += pszC;
3910 :
3911 90 : char *pszSQL = sqlite3_mprintf(
3912 : "SELECT sql FROM sqlite_master WHERE type = 'trigger' "
3913 : "AND name IN ('%q', '%q', '%q', '%q', '%q', '%q', "
3914 : "'%q', '%q', '%q')",
3915 20 : (m_osRTreeName + "_insert").c_str(),
3916 20 : (m_osRTreeName + "_update1").c_str(),
3917 20 : (m_osRTreeName + "_update2").c_str(),
3918 20 : (m_osRTreeName + "_update3").c_str(),
3919 20 : (m_osRTreeName + "_update4").c_str(),
3920 : // update5 replaces update3 in GPKG 1.4
3921 : // cf https://github.com/opengeospatial/geopackage/pull/661
3922 20 : (m_osRTreeName + "_update5").c_str(),
3923 : // update6 and update7 replace update1 in GPKG 1.4
3924 : // cf https://github.com/opengeospatial/geopackage/pull/661
3925 20 : (m_osRTreeName + "_update6").c_str(),
3926 20 : (m_osRTreeName + "_update7").c_str(),
3927 20 : (m_osRTreeName + "_delete").c_str());
3928 20 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
3929 10 : sqlite3_free(pszSQL);
3930 10 : if (oResult)
3931 : {
3932 77 : for (int iRecord = 0; iRecord < oResult->RowCount(); iRecord++)
3933 : {
3934 67 : const char *pszTriggerSQL = oResult->GetValue(0, iRecord);
3935 67 : if (pszTriggerSQL)
3936 : {
3937 67 : m_aoRTreeTriggersSQL.push_back(pszTriggerSQL);
3938 : }
3939 : }
3940 : }
3941 10 : if (m_aoRTreeTriggersSQL.size() != 6 && m_aoRTreeTriggersSQL.size() != 7)
3942 : {
3943 0 : CPLDebug("GPKG", "Could not find expected RTree triggers");
3944 0 : m_aoRTreeTriggersSQL.clear();
3945 0 : return false;
3946 : }
3947 :
3948 10 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers());
3949 :
3950 10 : return true;
3951 : }
3952 :
3953 : /************************************************************************/
3954 : /* FlushPendingSpatialIndexUpdate() */
3955 : /************************************************************************/
3956 :
3957 8 : bool OGRGeoPackageTableLayer::FlushPendingSpatialIndexUpdate()
3958 : {
3959 8 : bool ret = true;
3960 :
3961 : // CPLDebug("GPKG", "Insert %d features in spatial index",
3962 : // static_cast<int>(m_aoRTreeEntries.size()));
3963 :
3964 8 : const char *pszT = m_pszTableName;
3965 8 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3966 :
3967 8 : m_osRTreeName = "rtree_";
3968 8 : m_osRTreeName += pszT;
3969 8 : m_osRTreeName += "_";
3970 8 : m_osRTreeName += pszC;
3971 :
3972 8 : char *pszSQL = sqlite3_mprintf("INSERT INTO \"%w\" VALUES (?,?,?,?,?)",
3973 : m_osRTreeName.c_str());
3974 8 : sqlite3_stmt *hInsertStmt = nullptr;
3975 8 : if (SQLPrepareWithError(m_poDS->GetDB(), pszSQL, -1, &hInsertStmt,
3976 8 : nullptr) != SQLITE_OK)
3977 : {
3978 0 : sqlite3_free(pszSQL);
3979 0 : m_aoRTreeEntries.clear();
3980 0 : return false;
3981 : }
3982 8 : sqlite3_free(pszSQL);
3983 :
3984 1196 : for (size_t i = 0; i < m_aoRTreeEntries.size(); ++i)
3985 : {
3986 1188 : sqlite3_reset(hInsertStmt);
3987 :
3988 1188 : sqlite3_bind_int64(hInsertStmt, 1, m_aoRTreeEntries[i].nId);
3989 1188 : sqlite3_bind_double(hInsertStmt, 2, m_aoRTreeEntries[i].fMinX);
3990 1188 : sqlite3_bind_double(hInsertStmt, 3, m_aoRTreeEntries[i].fMaxX);
3991 1188 : sqlite3_bind_double(hInsertStmt, 4, m_aoRTreeEntries[i].fMinY);
3992 1188 : sqlite3_bind_double(hInsertStmt, 5, m_aoRTreeEntries[i].fMaxY);
3993 1188 : int sqlite_err = sqlite3_step(hInsertStmt);
3994 1188 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
3995 : {
3996 0 : CPLError(CE_Failure, CPLE_AppDefined,
3997 : "failed to execute insertion in RTree : %s",
3998 0 : sqlite3_errmsg(m_poDS->GetDB()));
3999 0 : ret = false;
4000 0 : break;
4001 : }
4002 : }
4003 8 : sqlite3_finalize(hInsertStmt);
4004 8 : m_aoRTreeEntries.clear();
4005 8 : return ret;
4006 : }
4007 :
4008 : /************************************************************************/
4009 : /* RunDeferredSpatialIndexUpdate() */
4010 : /************************************************************************/
4011 :
4012 39554 : bool OGRGeoPackageTableLayer::RunDeferredSpatialIndexUpdate()
4013 : {
4014 39554 : m_nCountInsertInTransaction = 0;
4015 39554 : if (m_aoRTreeTriggersSQL.empty())
4016 39546 : return true;
4017 :
4018 8 : bool ret = FlushPendingSpatialIndexUpdate();
4019 :
4020 8 : RevertWorkaroundUpdate1TriggerIssue();
4021 :
4022 62 : for (const auto &osSQL : m_aoRTreeTriggersSQL)
4023 : {
4024 54 : ret &= SQLCommand(m_poDS->GetDB(), osSQL) == OGRERR_NONE;
4025 : }
4026 8 : m_aoRTreeTriggersSQL.clear();
4027 8 : return ret;
4028 : }
4029 :
4030 : /************************************************************************/
4031 : /* SyncToDisk() */
4032 : /************************************************************************/
4033 :
4034 11088 : OGRErr OGRGeoPackageTableLayer::SyncToDisk()
4035 : {
4036 11088 : if (!m_bFeatureDefnCompleted)
4037 5145 : return OGRERR_NONE;
4038 :
4039 5943 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4040 0 : return OGRERR_FAILURE;
4041 :
4042 : // Both are exclusive
4043 5943 : CreateSpatialIndexIfNecessary();
4044 5943 : if (!RunDeferredSpatialIndexUpdate())
4045 0 : return OGRERR_FAILURE;
4046 5943 : RevertWorkaroundUpdate1TriggerIssue();
4047 :
4048 : /* Save metadata back to the database */
4049 5943 : SaveExtent();
4050 5943 : SaveTimestamp();
4051 :
4052 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4053 5943 : CreateFeatureCountTriggers();
4054 : #endif
4055 :
4056 5943 : return OGRERR_NONE;
4057 : }
4058 :
4059 : /************************************************************************/
4060 : /* StartTransaction() */
4061 : /************************************************************************/
4062 :
4063 129 : OGRErr OGRGeoPackageTableLayer::StartTransaction()
4064 : {
4065 129 : CancelAsyncNextArrowArray();
4066 129 : return m_poDS->StartTransaction();
4067 : }
4068 :
4069 : /************************************************************************/
4070 : /* CommitTransaction() */
4071 : /************************************************************************/
4072 :
4073 92 : OGRErr OGRGeoPackageTableLayer::CommitTransaction()
4074 : {
4075 92 : return m_poDS->CommitTransaction();
4076 : }
4077 :
4078 : /************************************************************************/
4079 : /* RollbackTransaction() */
4080 : /************************************************************************/
4081 :
4082 18 : OGRErr OGRGeoPackageTableLayer::RollbackTransaction()
4083 : {
4084 18 : return m_poDS->RollbackTransaction();
4085 : }
4086 :
4087 : /************************************************************************/
4088 : /* GetTotalFeatureCount() */
4089 : /************************************************************************/
4090 :
4091 601 : GIntBig OGRGeoPackageTableLayer::GetTotalFeatureCount()
4092 : {
4093 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4094 601 : if (m_nTotalFeatureCount < 0 && m_poDS->m_bHasGPKGOGRContents)
4095 : {
4096 : char *pszSQL =
4097 19 : sqlite3_mprintf("SELECT feature_count FROM gpkg_ogr_contents WHERE "
4098 : "lower(table_name) = lower('%q') LIMIT 2",
4099 : m_pszTableName);
4100 38 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4101 19 : sqlite3_free(pszSQL);
4102 19 : if (oResult && oResult->RowCount() == 1)
4103 : {
4104 15 : const char *pszFeatureCount = oResult->GetValue(0, 0);
4105 15 : if (pszFeatureCount)
4106 : {
4107 6 : m_nTotalFeatureCount =
4108 6 : std::max<GIntBig>(0, CPLAtoGIntBig(pszFeatureCount));
4109 : // According to https://sqlite.org/limits.html
4110 : // "A 281 terabytes database can hold no more than approximately 2e+13 rows"
4111 : // We do that check to avoid returning values close to INT64_MAX
4112 6 : if (m_nTotalFeatureCount >
4113 : static_cast<GIntBig>(30) * 1000 * 1000 * 1000 * 1000)
4114 : {
4115 0 : CPLDebug("GPKG", "Invalid value in feature_count field of "
4116 : "gpkg_ogr_contents");
4117 0 : m_nTotalFeatureCount = -1;
4118 : }
4119 : }
4120 : }
4121 : }
4122 601 : return m_nTotalFeatureCount;
4123 : #else
4124 : return 0;
4125 : #endif
4126 : }
4127 :
4128 : /************************************************************************/
4129 : /* GetFeatureCount() */
4130 : /************************************************************************/
4131 :
4132 23128 : GIntBig OGRGeoPackageTableLayer::GetFeatureCount(int /*bForce*/)
4133 : {
4134 23128 : if (!m_bFeatureDefnCompleted)
4135 106 : GetLayerDefn();
4136 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4137 23128 : if (m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr)
4138 : {
4139 248 : const auto nCount = GetTotalFeatureCount();
4140 248 : if (nCount >= 0)
4141 235 : return nCount;
4142 : }
4143 : #endif
4144 :
4145 22893 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4146 0 : return 0;
4147 :
4148 22893 : CancelAsyncNextArrowArray();
4149 :
4150 22893 : if (m_poFilterGeom != nullptr)
4151 : {
4152 : // Both are exclusive
4153 22874 : CreateSpatialIndexIfNecessary();
4154 22874 : if (!RunDeferredSpatialIndexUpdate())
4155 0 : return -1;
4156 : }
4157 :
4158 : /* Ignore bForce, because we always do a full count on the database */
4159 : OGRErr err;
4160 45786 : CPLString soSQL;
4161 22893 : bool bUnregisterSQLFunction = false;
4162 22892 : if (m_bIsTable && m_poFilterGeom != nullptr &&
4163 45785 : m_pszAttrQueryString == nullptr && HasSpatialIndex())
4164 : {
4165 22866 : OGREnvelope sEnvelope;
4166 :
4167 22866 : m_poFilterGeom->getEnvelope(&sEnvelope);
4168 :
4169 45732 : if (!std::isinf(sEnvelope.MinX) && !std::isinf(sEnvelope.MinY) &&
4170 45732 : !std::isinf(sEnvelope.MaxX) && !std::isinf(sEnvelope.MaxY))
4171 : {
4172 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE "
4173 : "maxx >= %.12f AND minx <= %.12f AND "
4174 : "maxy >= %.12f AND miny <= %.12f",
4175 45732 : SQLEscapeName(m_osRTreeName).c_str(),
4176 22866 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4177 45732 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
4178 :
4179 45732 : if (OGRGeometryFactory::haveGEOS() &&
4180 22866 : !(m_bFilterIsEnvelope &&
4181 22866 : wkbFlatten(
4182 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4183 : ->GetType()) == wkbPoint))
4184 : {
4185 22854 : bUnregisterSQLFunction = true;
4186 22854 : sqlite3_create_function(
4187 22854 : m_poDS->hDB, "OGR_GPKG_Intersects_Spatial_Filter", 1,
4188 : SQLITE_UTF8, this, OGR_GPKG_Intersects_Spatial_Filter,
4189 : nullptr, nullptr);
4190 : const char *pszC =
4191 22854 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4192 22854 : ->GetNameRef();
4193 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" m "
4194 : "JOIN \"%s\" r "
4195 : "ON m.\"%s\" = r.id WHERE "
4196 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
4197 : "r.maxy >= %.12f AND r.miny <= %.12f AND "
4198 : "OGR_GPKG_Intersects_Spatial_Filter(m.\"%s\")",
4199 45708 : SQLEscapeName(m_pszTableName).c_str(),
4200 45708 : SQLEscapeName(m_osRTreeName).c_str(),
4201 45708 : SQLEscapeName(m_osFIDForRTree).c_str(),
4202 22854 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4203 22854 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11,
4204 114270 : SQLEscapeName(pszC).c_str());
4205 : }
4206 : }
4207 : }
4208 :
4209 22893 : if (soSQL.empty())
4210 : {
4211 27 : if (!m_soFilter.empty())
4212 : soSQL.Printf("SELECT Count(*) FROM \"%s\" WHERE %s",
4213 28 : SQLEscapeName(m_pszTableName).c_str(),
4214 28 : m_soFilter.c_str());
4215 : else
4216 : soSQL.Printf("SELECT Count(*) FROM \"%s\"",
4217 13 : SQLEscapeName(m_pszTableName).c_str());
4218 : }
4219 :
4220 : /* Just run the query directly and get back integer */
4221 : GIntBig iFeatureCount =
4222 22893 : SQLGetInteger64(m_poDS->GetDB(), soSQL.c_str(), &err);
4223 :
4224 22893 : if (bUnregisterSQLFunction)
4225 : {
4226 22854 : sqlite3_create_function(m_poDS->hDB,
4227 : "OGR_GPKG_Intersects_Spatial_Filter", 1,
4228 : SQLITE_UTF8, this, nullptr, nullptr, nullptr);
4229 : }
4230 :
4231 : /* Generic implementation uses -1 for error condition, so we will too */
4232 22893 : if (err == OGRERR_NONE)
4233 : {
4234 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4235 22893 : if (m_bIsTable && m_poFilterGeom == nullptr &&
4236 19 : m_pszAttrQueryString == nullptr)
4237 : {
4238 13 : m_nTotalFeatureCount = iFeatureCount;
4239 :
4240 13 : if (m_poDS->GetUpdate() && m_poDS->m_bHasGPKGOGRContents)
4241 : {
4242 : const char *pszCount =
4243 6 : CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
4244 6 : char *pszSQL = sqlite3_mprintf(
4245 : "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
4246 : "lower(table_name )= lower('%q')",
4247 : pszCount, m_pszTableName);
4248 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4249 6 : sqlite3_free(pszSQL);
4250 : }
4251 : }
4252 : #endif
4253 22893 : return iFeatureCount;
4254 : }
4255 : else
4256 0 : return -1;
4257 : }
4258 :
4259 : /************************************************************************/
4260 : /* GetExtentFromRTree() */
4261 : /************************************************************************/
4262 :
4263 155 : static bool GetExtentFromRTree(sqlite3 *hDB, const std::string &osRTreeName,
4264 : double &minx, double &miny, double &maxx,
4265 : double &maxy)
4266 : {
4267 : // Cf https://github.com/sqlite/sqlite/blob/master/ext/rtree/rtree.c
4268 : // for the description of the content of the rtree _node table
4269 : // We fetch the root node (nodeno = 1) and iterates over its cells, to
4270 : // take the min/max of their minx/maxx/miny/maxy values.
4271 155 : char *pszSQL = sqlite3_mprintf(
4272 : "SELECT data FROM \"%w_node\" WHERE nodeno = 1", osRTreeName.c_str());
4273 155 : sqlite3_stmt *hStmt = nullptr;
4274 155 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(hDB, pszSQL, -1, &hStmt, nullptr));
4275 155 : sqlite3_free(pszSQL);
4276 155 : bool bOK = false;
4277 155 : if (hStmt)
4278 : {
4279 310 : if (sqlite3_step(hStmt) == SQLITE_ROW &&
4280 155 : sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
4281 : {
4282 155 : const int nBytes = sqlite3_column_bytes(hStmt, 0);
4283 : // coverity[tainted_data_return]
4284 : const GByte *pabyData =
4285 155 : static_cast<const GByte *>(sqlite3_column_blob(hStmt, 0));
4286 155 : constexpr int BLOB_HEADER_SIZE = 4;
4287 155 : if (nBytes > BLOB_HEADER_SIZE)
4288 : {
4289 155 : const int nCellCount = (pabyData[2] << 8) | pabyData[3];
4290 155 : constexpr int SIZEOF_CELL = 24; // int64_t + 4 float
4291 155 : if (nCellCount >= 1 &&
4292 148 : nBytes >= BLOB_HEADER_SIZE + SIZEOF_CELL * nCellCount)
4293 : {
4294 148 : minx = std::numeric_limits<double>::max();
4295 148 : miny = std::numeric_limits<double>::max();
4296 148 : maxx = -std::numeric_limits<double>::max();
4297 148 : maxy = -std::numeric_limits<double>::max();
4298 148 : size_t offset = BLOB_HEADER_SIZE;
4299 486 : for (int i = 0; i < nCellCount; ++i)
4300 : {
4301 338 : offset += sizeof(int64_t);
4302 :
4303 : float fMinX;
4304 338 : memcpy(&fMinX, pabyData + offset, sizeof(float));
4305 338 : offset += sizeof(float);
4306 338 : CPL_MSBPTR32(&fMinX);
4307 338 : minx = std::min(minx, static_cast<double>(fMinX));
4308 :
4309 : float fMaxX;
4310 338 : memcpy(&fMaxX, pabyData + offset, sizeof(float));
4311 338 : offset += sizeof(float);
4312 338 : CPL_MSBPTR32(&fMaxX);
4313 338 : maxx = std::max(maxx, static_cast<double>(fMaxX));
4314 :
4315 : float fMinY;
4316 338 : memcpy(&fMinY, pabyData + offset, sizeof(float));
4317 338 : offset += sizeof(float);
4318 338 : CPL_MSBPTR32(&fMinY);
4319 338 : miny = std::min(miny, static_cast<double>(fMinY));
4320 :
4321 : float fMaxY;
4322 338 : memcpy(&fMaxY, pabyData + offset, sizeof(float));
4323 338 : offset += sizeof(float);
4324 338 : CPL_MSBPTR32(&fMaxY);
4325 338 : maxy = std::max(maxy, static_cast<double>(fMaxY));
4326 : }
4327 :
4328 148 : bOK = true;
4329 : }
4330 : }
4331 : }
4332 155 : sqlite3_finalize(hStmt);
4333 : }
4334 155 : return bOK;
4335 : }
4336 :
4337 : /************************************************************************/
4338 : /* IGetExtent() */
4339 : /************************************************************************/
4340 :
4341 334 : OGRErr OGRGeoPackageTableLayer::IGetExtent(int /* iGeomField */,
4342 : OGREnvelope *psExtent, bool bForce)
4343 : {
4344 334 : if (!m_bFeatureDefnCompleted)
4345 0 : GetLayerDefn();
4346 : /* Extent already calculated! We're done. */
4347 334 : if (m_poExtent != nullptr)
4348 : {
4349 319 : if (psExtent)
4350 : {
4351 319 : *psExtent = *m_poExtent;
4352 : }
4353 319 : return OGRERR_NONE;
4354 : }
4355 :
4356 15 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4357 0 : return OGRERR_FAILURE;
4358 :
4359 15 : CancelAsyncNextArrowArray();
4360 :
4361 24 : if (m_poFeatureDefn->GetGeomFieldCount() && HasSpatialIndex() &&
4362 9 : CPLTestBool(
4363 : CPLGetConfigOption("OGR_GPKG_USE_RTREE_FOR_GET_EXTENT", "TRUE")))
4364 : {
4365 9 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, psExtent->MinX,
4366 9 : psExtent->MinY, psExtent->MaxX, psExtent->MaxY))
4367 : {
4368 2 : m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
4369 2 : m_bExtentChanged = true;
4370 2 : SaveExtent();
4371 2 : return OGRERR_NONE;
4372 : }
4373 : else
4374 : {
4375 7 : UpdateContentsToNullExtent();
4376 7 : return OGRERR_FAILURE;
4377 : }
4378 : }
4379 :
4380 : /* User is OK with expensive calculation */
4381 6 : if (bForce && m_poFeatureDefn->GetGeomFieldCount())
4382 : {
4383 : /* fall back to default implementation (scan all features) and save */
4384 : /* the result for later */
4385 4 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4386 4 : char *pszSQL = sqlite3_mprintf(
4387 : "SELECT MIN(ST_MinX(\"%w\")), MIN(ST_MinY(\"%w\")), "
4388 : "MAX(ST_MaxX(\"%w\")), MAX(ST_MaxY(\"%w\")) FROM \"%w\" WHERE "
4389 : "\"%w\" IS NOT NULL AND NOT ST_IsEmpty(\"%w\")",
4390 : pszC, pszC, pszC, pszC, m_pszTableName, pszC, pszC);
4391 8 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4392 4 : sqlite3_free(pszSQL);
4393 4 : m_poExtent.reset();
4394 8 : if (oResult && oResult->RowCount() == 1 &&
4395 4 : oResult->GetValue(0, 0) != nullptr)
4396 : {
4397 3 : psExtent->MinX = CPLAtof(oResult->GetValue(0, 0));
4398 3 : psExtent->MinY = CPLAtof(oResult->GetValue(1, 0));
4399 3 : psExtent->MaxX = CPLAtof(oResult->GetValue(2, 0));
4400 3 : psExtent->MaxY = CPLAtof(oResult->GetValue(3, 0));
4401 3 : m_poExtent = std::make_unique<OGREnvelope>(*psExtent);
4402 3 : m_bExtentChanged = true;
4403 3 : SaveExtent();
4404 : }
4405 : else
4406 : {
4407 1 : UpdateContentsToNullExtent();
4408 1 : return OGRERR_FAILURE; // we didn't get an extent
4409 : }
4410 3 : return OGRERR_NONE;
4411 : }
4412 :
4413 2 : return OGRERR_FAILURE;
4414 : }
4415 :
4416 : /************************************************************************/
4417 : /* UpdateContentsToNullExtent() */
4418 : /************************************************************************/
4419 :
4420 8 : void OGRGeoPackageTableLayer::UpdateContentsToNullExtent()
4421 : {
4422 8 : if (m_poDS->GetUpdate())
4423 : {
4424 : char *pszSQL =
4425 3 : sqlite3_mprintf("UPDATE gpkg_contents SET "
4426 : "min_x = NULL, min_y = NULL, "
4427 : "max_x = NULL, max_y = NULL "
4428 : "WHERE lower(table_name) = lower('%q') AND "
4429 : "Lower(data_type) = 'features'",
4430 : m_pszTableName);
4431 3 : SQLCommand(m_poDS->GetDB(), pszSQL);
4432 3 : sqlite3_free(pszSQL);
4433 : }
4434 8 : m_bExtentChanged = false;
4435 8 : }
4436 :
4437 : /************************************************************************/
4438 : /* RecomputeExtent() */
4439 : /************************************************************************/
4440 :
4441 4 : void OGRGeoPackageTableLayer::RecomputeExtent()
4442 : {
4443 4 : m_bExtentChanged = true;
4444 4 : m_poExtent.reset();
4445 4 : OGREnvelope sExtent;
4446 4 : CPL_IGNORE_RET_VAL(GetExtent(&sExtent, true));
4447 4 : }
4448 :
4449 : /************************************************************************/
4450 : /* TestCapability() */
4451 : /************************************************************************/
4452 :
4453 2394 : bool OGRGeoPackageTableLayer::TestCapability(const char *pszCap) const
4454 : {
4455 2394 : if (!m_bFeatureDefnCompleted)
4456 17 : GetLayerDefn();
4457 2394 : if (EQUAL(pszCap, OLCSequentialWrite))
4458 : {
4459 32 : return m_poDS->GetUpdate();
4460 : }
4461 2362 : else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
4462 2333 : EQUAL(pszCap, OLCAlterFieldDefn) ||
4463 2326 : EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
4464 2325 : EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCRename))
4465 : {
4466 45 : return m_poDS->GetUpdate() && m_bIsTable;
4467 : }
4468 2317 : else if (EQUAL(pszCap, OLCDeleteFeature) ||
4469 2310 : EQUAL(pszCap, OLCUpsertFeature) ||
4470 2304 : EQUAL(pszCap, OLCUpdateFeature) || EQUAL(pszCap, OLCRandomWrite))
4471 : {
4472 18 : return m_poDS->GetUpdate() && m_pszFidColumn != nullptr;
4473 : }
4474 2299 : else if (EQUAL(pszCap, OLCRandomRead))
4475 : {
4476 6 : return m_pszFidColumn != nullptr;
4477 : }
4478 2293 : else if (EQUAL(pszCap, OLCTransactions))
4479 : {
4480 17 : return TRUE;
4481 : }
4482 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4483 2276 : else if (EQUAL(pszCap, OLCFastFeatureCount))
4484 : {
4485 26 : return m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr &&
4486 26 : m_nTotalFeatureCount >= 0;
4487 : }
4488 : #endif
4489 2263 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
4490 : {
4491 3 : return HasSpatialIndex() || m_bDeferredSpatialIndexCreation;
4492 : }
4493 2260 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
4494 : {
4495 : // Fast may not be that true on large layers, but better than the
4496 : // default implementation for sure...
4497 0 : return TRUE;
4498 : }
4499 2260 : else if (EQUAL(pszCap, OLCFastGetExtent))
4500 : {
4501 5 : return (m_poExtent != nullptr);
4502 : }
4503 2255 : else if (EQUAL(pszCap, OLCCurveGeometries))
4504 1007 : return TRUE;
4505 1248 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
4506 1004 : return TRUE;
4507 244 : else if (EQUAL(pszCap, OLCZGeometries))
4508 166 : return TRUE;
4509 78 : if (EQUAL(pszCap, OLCFastGetExtent3D))
4510 0 : return TRUE;
4511 : else
4512 : {
4513 78 : return OGRGeoPackageLayer::TestCapability(pszCap);
4514 : }
4515 : }
4516 :
4517 : /************************************************************************/
4518 : /* CreateSpatialIndexIfNecessary() */
4519 : /************************************************************************/
4520 :
4521 43612 : void OGRGeoPackageTableLayer::CreateSpatialIndexIfNecessary()
4522 : {
4523 43612 : if (m_bDeferredSpatialIndexCreation)
4524 : {
4525 925 : CreateSpatialIndex();
4526 : }
4527 43612 : }
4528 :
4529 : /************************************************************************/
4530 : /* CreateSpatialIndex() */
4531 : /************************************************************************/
4532 :
4533 935 : bool OGRGeoPackageTableLayer::CreateSpatialIndex(const char *pszTableName)
4534 : {
4535 : OGRErr err;
4536 :
4537 935 : if (!m_bFeatureDefnCompleted)
4538 2 : GetLayerDefn();
4539 :
4540 935 : if (!CheckUpdatableTable("CreateSpatialIndex"))
4541 1 : return false;
4542 :
4543 934 : if (m_bDropRTreeTable)
4544 : {
4545 0 : CPLError(CE_Failure, CPLE_AppDefined,
4546 : "Cannot run CreateSpatialIndex() after non-completed deferred "
4547 : "DropSpatialIndex()");
4548 0 : return false;
4549 : }
4550 :
4551 934 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4552 0 : return false;
4553 :
4554 934 : CancelAsyncNextArrowArray();
4555 :
4556 934 : m_bDeferredSpatialIndexCreation = false;
4557 :
4558 934 : if (m_pszFidColumn == nullptr)
4559 0 : return false;
4560 :
4561 934 : if (HasSpatialIndex())
4562 : {
4563 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index already existing");
4564 1 : return false;
4565 : }
4566 :
4567 933 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
4568 : {
4569 1 : CPLError(CE_Failure, CPLE_AppDefined, "No geometry column");
4570 1 : return false;
4571 : }
4572 932 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
4573 0 : return false;
4574 :
4575 932 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4576 932 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4577 932 : const char *pszI = GetFIDColumn();
4578 :
4579 932 : m_osRTreeName = "rtree_";
4580 932 : m_osRTreeName += pszT;
4581 932 : m_osRTreeName += "_";
4582 932 : m_osRTreeName += pszC;
4583 932 : m_osFIDForRTree = m_pszFidColumn;
4584 :
4585 932 : bool bPopulateFromThreadRTree = false;
4586 932 : if (m_bThreadRTreeStarted)
4587 : {
4588 12 : const bool bThreadHasFinished = m_oQueueRTreeEntries.empty();
4589 12 : if (!m_aoRTreeEntries.empty())
4590 4 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
4591 12 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
4592 12 : m_oQueueRTreeEntries.push(m_aoRTreeEntries);
4593 12 : if (!bThreadHasFinished)
4594 3 : CPLDebug("GPKG", "Waiting for background RTree building to finish");
4595 12 : m_oThreadRTree.join();
4596 12 : if (!bThreadHasFinished)
4597 : {
4598 3 : CPLDebug("GPKG", "Background RTree building finished");
4599 : }
4600 12 : m_bAllowedRTreeThread = false;
4601 12 : m_bThreadRTreeStarted = false;
4602 :
4603 12 : if (m_hAsyncDBHandle)
4604 : {
4605 8 : sqlite3_close(m_hAsyncDBHandle);
4606 8 : m_hAsyncDBHandle = nullptr;
4607 : }
4608 12 : if (m_bErrorDuringRTreeThread)
4609 : {
4610 4 : RemoveAsyncRTreeTempDB();
4611 : }
4612 : else
4613 : {
4614 8 : bPopulateFromThreadRTree = true;
4615 : }
4616 : }
4617 :
4618 932 : m_poDS->SoftStartTransaction();
4619 :
4620 932 : if (m_hRTree)
4621 : {
4622 4 : if (!FlushInMemoryRTree(m_poDS->GetDB(), m_osRTreeName.c_str()))
4623 : {
4624 0 : m_poDS->SoftRollbackTransaction();
4625 0 : return false;
4626 : }
4627 : }
4628 928 : else if (bPopulateFromThreadRTree)
4629 : {
4630 : /* Create virtual table */
4631 4 : char *pszSQL = sqlite3_mprintf("CREATE VIRTUAL TABLE \"%w\" USING "
4632 : "rtree(id, minx, maxx, miny, maxy)",
4633 : m_osRTreeName.c_str());
4634 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4635 4 : sqlite3_free(pszSQL);
4636 4 : if (err != OGRERR_NONE)
4637 : {
4638 0 : m_poDS->SoftRollbackTransaction();
4639 0 : return false;
4640 : }
4641 :
4642 4 : pszSQL = sqlite3_mprintf(
4643 : "DELETE FROM \"%w_node\";\n"
4644 : "INSERT INTO \"%w_node\" SELECT * FROM \"%w\".my_rtree_node;\n"
4645 : "INSERT INTO \"%w_rowid\" SELECT * FROM "
4646 : "\"%w\".my_rtree_rowid;\n"
4647 : "INSERT INTO \"%w_parent\" SELECT * FROM "
4648 : "\"%w\".my_rtree_parent;\n",
4649 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
4650 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4651 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4652 : m_osAsyncDBAttachName.c_str());
4653 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4654 4 : sqlite3_free(pszSQL);
4655 4 : if (err != OGRERR_NONE)
4656 : {
4657 0 : m_poDS->SoftRollbackTransaction();
4658 0 : RemoveAsyncRTreeTempDB();
4659 0 : return false;
4660 : }
4661 : }
4662 : else
4663 : {
4664 : /* Populate the RTree */
4665 924 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
4666 924 : char *pszErrMsg = nullptr;
4667 :
4668 : struct ProgressCbk
4669 : {
4670 602 : static bool progressCbk(const char *pszMessage, void *)
4671 : {
4672 602 : CPLDebug("GPKG", "%s", pszMessage);
4673 602 : return true;
4674 : }
4675 : };
4676 :
4677 1848 : if (!gdal_sqlite_rtree_bl_from_feature_table(
4678 924 : m_poDS->GetDB(), pszT, pszI, pszC, m_osRTreeName.c_str(), "id",
4679 : "minx", "miny", "maxx", "maxy", nMaxRAMUsageAllowed, &pszErrMsg,
4680 : ProgressCbk::progressCbk, nullptr))
4681 : {
4682 0 : CPLError(CE_Failure, CPLE_AppDefined,
4683 : "gdal_sqlite_rtree_bl_from_feature_table() failed "
4684 : "with %s",
4685 0 : pszErrMsg ? pszErrMsg : "(null)");
4686 0 : m_poDS->SoftRollbackTransaction();
4687 0 : sqlite3_free(pszErrMsg);
4688 0 : return false;
4689 : }
4690 : }
4691 :
4692 1864 : CPLString osSQL;
4693 :
4694 : /* Register the table in gpkg_extensions */
4695 932 : char *pszSQL = sqlite3_mprintf(
4696 : "INSERT INTO gpkg_extensions "
4697 : "(table_name,column_name,extension_name,definition,scope) "
4698 : "VALUES ('%q', '%q', 'gpkg_rtree_index', "
4699 : "'http://www.geopackage.org/spec120/#extension_rtree', 'write-only')",
4700 : pszT, pszC);
4701 932 : osSQL += pszSQL;
4702 932 : sqlite3_free(pszSQL);
4703 :
4704 : /* Define Triggers to Maintain Spatial Index Values */
4705 932 : osSQL += ";" + ReturnSQLCreateSpatialIndexTriggers(pszTableName, nullptr);
4706 :
4707 932 : err = SQLCommand(m_poDS->GetDB(), osSQL);
4708 932 : if (err != OGRERR_NONE)
4709 : {
4710 0 : m_poDS->SoftRollbackTransaction();
4711 0 : if (bPopulateFromThreadRTree)
4712 : {
4713 0 : RemoveAsyncRTreeTempDB();
4714 : }
4715 0 : return false;
4716 : }
4717 :
4718 932 : m_poDS->SoftCommitTransaction();
4719 :
4720 932 : if (bPopulateFromThreadRTree)
4721 : {
4722 8 : RemoveAsyncRTreeTempDB();
4723 : }
4724 :
4725 932 : m_bHasSpatialIndex = true;
4726 :
4727 932 : return true;
4728 : }
4729 :
4730 : /************************************************************************/
4731 : /* WorkaroundUpdate1TriggerIssue() */
4732 : /************************************************************************/
4733 :
4734 18 : void OGRGeoPackageTableLayer::WorkaroundUpdate1TriggerIssue()
4735 : {
4736 : // Workaround issue of https://sqlite.org/forum/forumpost/8c8de6ff91
4737 : // Basically the official _update1 spatial index trigger doesn't work
4738 : // with current versions of SQLite when invoked from an UPSERT statement.
4739 : // In GeoPackage 1.4, the update6 and update7 triggers replace update1
4740 :
4741 18 : if (m_bHasUpdate6And7Triggers || m_poFeatureDefn->GetGeomFieldCount() == 0)
4742 12 : return;
4743 :
4744 11 : const char *pszT = m_pszTableName;
4745 11 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4746 11 : const char *pszI = GetFIDColumn();
4747 :
4748 11 : CPLString osRTreeName = "rtree_";
4749 11 : osRTreeName += pszT;
4750 11 : osRTreeName += "_";
4751 11 : osRTreeName += pszC;
4752 :
4753 : // Check if update6 and update7 triggers are there
4754 : {
4755 22 : char *pszSQL = sqlite3_mprintf(
4756 : "SELECT * FROM sqlite_master WHERE type = 'trigger' "
4757 : "AND name IN ('%q', '%q')",
4758 22 : (m_osRTreeName + "_update6").c_str(),
4759 22 : (m_osRTreeName + "_update7").c_str());
4760 11 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4761 11 : sqlite3_free(pszSQL);
4762 11 : if (oResult && oResult->RowCount() == 2)
4763 : {
4764 5 : m_bHasUpdate6And7Triggers = true;
4765 5 : return;
4766 : }
4767 : }
4768 :
4769 : char *pszSQL =
4770 6 : sqlite3_mprintf("SELECT sql FROM sqlite_master WHERE type = 'trigger' "
4771 : "AND name = '%q'",
4772 12 : (m_osRTreeName + "_update1").c_str());
4773 6 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4774 6 : sqlite3_free(pszSQL);
4775 6 : if (oResult && oResult->RowCount() == 1)
4776 : {
4777 6 : const char *pszTriggerSQL = oResult->GetValue(0, 0);
4778 6 : if (pszTriggerSQL)
4779 : {
4780 6 : m_osUpdate1Trigger = pszTriggerSQL;
4781 : }
4782 : }
4783 6 : if (m_osUpdate1Trigger.empty())
4784 0 : return;
4785 :
4786 6 : m_bUpdate1TriggerDisabled = true;
4787 :
4788 : pszSQL =
4789 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update1\"", osRTreeName.c_str());
4790 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4791 6 : sqlite3_free(pszSQL);
4792 :
4793 6 : pszSQL = sqlite3_mprintf(
4794 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4795 : "ON \"%w\" "
4796 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4797 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4798 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4799 : "BEGIN "
4800 : "UPDATE \"%w\" SET "
4801 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4802 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4803 : "WHERE id = NEW.\"%w\";"
4804 : "END",
4805 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4806 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4807 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4808 6 : sqlite3_free(pszSQL);
4809 :
4810 6 : pszSQL = sqlite3_mprintf(
4811 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4812 : "\"%w\" "
4813 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4814 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4815 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4816 : "BEGIN "
4817 : "INSERT INTO \"%w\" VALUES ("
4818 : "NEW.\"%w\","
4819 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4820 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4821 : "); "
4822 : "END",
4823 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4824 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4825 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4826 6 : sqlite3_free(pszSQL);
4827 : }
4828 :
4829 : /************************************************************************/
4830 : /* RevertWorkaroundUpdate1TriggerIssue() */
4831 : /************************************************************************/
4832 :
4833 5969 : void OGRGeoPackageTableLayer::RevertWorkaroundUpdate1TriggerIssue()
4834 : {
4835 5969 : if (!m_bUpdate1TriggerDisabled)
4836 5963 : return;
4837 6 : m_bUpdate1TriggerDisabled = false;
4838 6 : CPLAssert(!m_bHasUpdate6And7Triggers);
4839 :
4840 6 : const char *pszT = m_pszTableName;
4841 6 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4842 :
4843 12 : CPLString osRTreeName = "rtree_";
4844 6 : osRTreeName += pszT;
4845 6 : osRTreeName += "_";
4846 6 : osRTreeName += pszC;
4847 :
4848 : char *pszSQL;
4849 :
4850 6 : SQLCommand(m_poDS->GetDB(), m_osUpdate1Trigger.c_str());
4851 6 : m_osUpdate1Trigger.clear();
4852 :
4853 : pszSQL =
4854 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update6\"", osRTreeName.c_str());
4855 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4856 6 : sqlite3_free(pszSQL);
4857 :
4858 : pszSQL =
4859 6 : sqlite3_mprintf("DROP TRIGGER \"%w_update7\"", osRTreeName.c_str());
4860 6 : SQLCommand(m_poDS->GetDB(), pszSQL);
4861 6 : sqlite3_free(pszSQL);
4862 : }
4863 :
4864 : /************************************************************************/
4865 : /* ReturnSQLCreateSpatialIndexTriggers() */
4866 : /************************************************************************/
4867 :
4868 944 : CPLString OGRGeoPackageTableLayer::ReturnSQLCreateSpatialIndexTriggers(
4869 : const char *pszTableName, const char *pszGeomColName)
4870 : {
4871 : char *pszSQL;
4872 944 : CPLString osSQL;
4873 :
4874 944 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4875 : const char *pszC = (pszGeomColName)
4876 944 : ? pszGeomColName
4877 940 : : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4878 944 : const char *pszI = GetFIDColumn();
4879 :
4880 1888 : CPLString osRTreeName = "rtree_";
4881 944 : osRTreeName += pszT;
4882 944 : osRTreeName += "_";
4883 944 : osRTreeName += pszC;
4884 :
4885 : /* Conditions: Insertion of non-empty geometry
4886 : Actions : Insert record into rtree */
4887 944 : pszSQL = sqlite3_mprintf(
4888 : "CREATE TRIGGER \"%w_insert\" AFTER INSERT ON \"%w\" "
4889 : "WHEN (new.\"%w\" NOT NULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4890 : "BEGIN "
4891 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4892 : "NEW.\"%w\","
4893 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4894 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4895 : "); "
4896 : "END",
4897 : osRTreeName.c_str(), pszT, pszC, pszC, osRTreeName.c_str(), pszI, pszC,
4898 : pszC, pszC, pszC);
4899 944 : osSQL += pszSQL;
4900 944 : sqlite3_free(pszSQL);
4901 :
4902 944 : if (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
4903 941 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
4904 : {
4905 : /* Conditions: Update a non-empty geometry with another non-empty geometry
4906 : Actions : Replace record from R-tree
4907 : */
4908 930 : pszSQL = sqlite3_mprintf(
4909 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4910 : "ON \"%w\" "
4911 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4912 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4913 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4914 : "BEGIN "
4915 : "UPDATE \"%w\" SET "
4916 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4917 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4918 : "WHERE id = NEW.\"%w\";"
4919 : "END",
4920 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4921 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4922 930 : osSQL += ";";
4923 930 : osSQL += pszSQL;
4924 930 : sqlite3_free(pszSQL);
4925 :
4926 : /* Conditions: Update a null/empty geometry with a non-empty geometry
4927 : Actions : Insert record into R-tree
4928 : */
4929 930 : pszSQL = sqlite3_mprintf(
4930 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4931 : "\"%w\" "
4932 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4933 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4934 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4935 : "BEGIN "
4936 : "INSERT INTO \"%w\" VALUES ("
4937 : "NEW.\"%w\","
4938 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4939 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4940 : "); "
4941 : "END",
4942 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4943 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4944 930 : osSQL += ";";
4945 930 : osSQL += pszSQL;
4946 930 : sqlite3_free(pszSQL);
4947 : }
4948 : else
4949 : {
4950 : /* Conditions: Update of geometry column to non-empty geometry
4951 : No row ID change
4952 : Actions : Update record in rtree */
4953 14 : pszSQL = sqlite3_mprintf(
4954 : "CREATE TRIGGER \"%w_update1\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4955 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4956 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4957 : "BEGIN "
4958 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4959 : "NEW.\"%w\","
4960 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4961 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4962 : "); "
4963 : "END",
4964 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4965 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4966 14 : osSQL += ";";
4967 14 : osSQL += pszSQL;
4968 14 : sqlite3_free(pszSQL);
4969 : }
4970 :
4971 : /* Conditions: Update of geometry column to empty geometry
4972 : No row ID change
4973 : Actions : Remove record from rtree */
4974 944 : pszSQL = sqlite3_mprintf(
4975 : "CREATE TRIGGER \"%w_update2\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4976 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4977 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
4978 : "BEGIN "
4979 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4980 : "END",
4981 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4982 : osRTreeName.c_str(), pszI);
4983 944 : osSQL += ";";
4984 944 : osSQL += pszSQL;
4985 944 : sqlite3_free(pszSQL);
4986 :
4987 : /* Conditions: Update of any column
4988 : Row ID change
4989 : Non-empty geometry
4990 : Actions : Remove record from rtree for old <i>
4991 : Insert record into rtree for new <i> */
4992 : pszSQL =
4993 1888 : sqlite3_mprintf("CREATE TRIGGER \"%w_%s\" AFTER UPDATE ON \"%w\" "
4994 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
4995 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4996 : "BEGIN "
4997 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4998 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4999 : "NEW.\"%w\","
5000 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
5001 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
5002 : "); "
5003 : "END",
5004 : osRTreeName.c_str(),
5005 944 : (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
5006 941 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
5007 : ? "update5"
5008 : : "update3",
5009 : pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(), pszI,
5010 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
5011 944 : osSQL += ";";
5012 944 : osSQL += pszSQL;
5013 944 : sqlite3_free(pszSQL);
5014 :
5015 : /* Conditions: Update of any column
5016 : Row ID change
5017 : Empty geometry
5018 : Actions : Remove record from rtree for old and new <i> */
5019 944 : pszSQL = sqlite3_mprintf(
5020 : "CREATE TRIGGER \"%w_update4\" AFTER UPDATE ON \"%w\" "
5021 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
5022 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
5023 : "BEGIN "
5024 : "DELETE FROM \"%w\" WHERE id IN (OLD.\"%w\", NEW.\"%w\"); "
5025 : "END",
5026 : osRTreeName.c_str(), pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(),
5027 : pszI, pszI);
5028 944 : osSQL += ";";
5029 944 : osSQL += pszSQL;
5030 944 : sqlite3_free(pszSQL);
5031 :
5032 : /* Conditions: Row deleted
5033 : Actions : Remove record from rtree for old <i> */
5034 944 : pszSQL = sqlite3_mprintf(
5035 : "CREATE TRIGGER \"%w_delete\" AFTER DELETE ON \"%w\" "
5036 : "WHEN old.\"%w\" NOT NULL "
5037 : "BEGIN "
5038 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
5039 : "END",
5040 : osRTreeName.c_str(), pszT, pszC, osRTreeName.c_str(), pszI);
5041 944 : osSQL += ";";
5042 944 : osSQL += pszSQL;
5043 944 : sqlite3_free(pszSQL);
5044 :
5045 1888 : return osSQL;
5046 : }
5047 :
5048 : /************************************************************************/
5049 : /* CheckUnknownExtensions() */
5050 : /************************************************************************/
5051 :
5052 1073 : void OGRGeoPackageTableLayer::CheckUnknownExtensions()
5053 : {
5054 : const std::map<CPLString, std::vector<GPKGExtensionDesc>> &oMap =
5055 1073 : m_poDS->GetUnknownExtensionsTableSpecific();
5056 1073 : const auto oIter = oMap.find(CPLString(m_pszTableName).toupper());
5057 1073 : if (oIter != oMap.end())
5058 : {
5059 12 : for (size_t i = 0; i < oIter->second.size(); i++)
5060 : {
5061 6 : const char *pszExtName = oIter->second[i].osExtensionName.c_str();
5062 6 : const char *pszDefinition = oIter->second[i].osDefinition.c_str();
5063 6 : const char *pszScope = oIter->second[i].osScope.c_str();
5064 6 : if (m_poDS->GetUpdate() && EQUAL(pszScope, "write-only"))
5065 : {
5066 1 : CPLError(
5067 : CE_Warning, CPLE_AppDefined,
5068 : "Layer %s relies on the '%s' (%s) extension that should "
5069 : "be implemented for safe write-support, but is not "
5070 : "currently. "
5071 : "Update of that layer are strongly discouraged to avoid "
5072 : "corruption.",
5073 : GetName(), pszExtName, pszDefinition);
5074 : }
5075 5 : else if (m_poDS->GetUpdate() && EQUAL(pszScope, "read-write"))
5076 : {
5077 1 : CPLError(
5078 : CE_Warning, CPLE_AppDefined,
5079 : "Layer %s relies on the '%s' (%s) extension that should "
5080 : "be implemented in order to read/write it safely, but is "
5081 : "not currently. "
5082 : "Some data may be missing while reading that layer, and "
5083 : "updates are strongly discouraged.",
5084 : GetName(), pszExtName, pszDefinition);
5085 : }
5086 4 : else if (EQUAL(pszScope, "read-write") &&
5087 : // None of the NGA extensions at
5088 : // http://ngageoint.github.io/GeoPackage/docs/extensions/
5089 : // affect read-only scenarios
5090 2 : !STARTS_WITH(pszExtName, "nga_"))
5091 : {
5092 2 : CPLError(
5093 : CE_Warning, CPLE_AppDefined,
5094 : "Layer %s relies on the '%s' (%s) extension that should "
5095 : "be implemented in order to read it safely, but is not "
5096 : "currently. "
5097 : "Some data may be missing while reading that layer.",
5098 : GetName(), pszExtName, pszDefinition);
5099 : }
5100 : }
5101 : }
5102 1073 : }
5103 :
5104 : /************************************************************************/
5105 : /* CreateGeometryExtensionIfNecessary() */
5106 : /************************************************************************/
5107 :
5108 264037 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5109 : const OGRGeometry *poGeom)
5110 : {
5111 264037 : bool bRet = true;
5112 264037 : if (poGeom != nullptr)
5113 : {
5114 264037 : OGRwkbGeometryType eGType = wkbFlatten(poGeom->getGeometryType());
5115 264037 : if (eGType >= wkbGeometryCollection)
5116 : {
5117 24 : if (eGType > wkbGeometryCollection)
5118 12 : CreateGeometryExtensionIfNecessary(eGType);
5119 : const OGRGeometryCollection *poGC =
5120 24 : dynamic_cast<const OGRGeometryCollection *>(poGeom);
5121 24 : if (poGC != nullptr)
5122 : {
5123 13 : const int nSubGeoms = poGC->getNumGeometries();
5124 34 : for (int i = 0; i < nSubGeoms; i++)
5125 : {
5126 21 : bRet &= CreateGeometryExtensionIfNecessary(
5127 21 : poGC->getGeometryRef(i));
5128 : }
5129 : }
5130 : }
5131 : }
5132 264037 : return bRet;
5133 : }
5134 :
5135 : /************************************************************************/
5136 : /* CreateGeometryExtensionIfNecessary() */
5137 : /************************************************************************/
5138 :
5139 19 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5140 : OGRwkbGeometryType eGType)
5141 : {
5142 19 : eGType = wkbFlatten(eGType);
5143 19 : CPLAssert(eGType > wkbGeometryCollection && eGType <= wkbTriangle);
5144 19 : if (m_abHasGeometryExtension[eGType])
5145 3 : return true;
5146 :
5147 16 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
5148 0 : return false;
5149 :
5150 16 : const char *pszT = m_pszTableName;
5151 16 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5152 16 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5153 :
5154 : // Check first if the extension isn't registered
5155 16 : char *pszSQL = sqlite3_mprintf(
5156 : "SELECT 1 FROM gpkg_extensions WHERE lower(table_name) = lower('%q') "
5157 : "AND "
5158 : "lower(column_name) = lower('%q') AND extension_name = 'gpkg_geom_%s'",
5159 : pszT, pszC, pszGeometryType);
5160 16 : const bool bExists = SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5161 16 : sqlite3_free(pszSQL);
5162 :
5163 16 : if (!bExists)
5164 : {
5165 15 : if (eGType == wkbPolyhedralSurface || eGType == wkbTIN ||
5166 : eGType == wkbTriangle)
5167 : {
5168 2 : CPLError(CE_Warning, CPLE_AppDefined,
5169 : "Registering non-standard gpkg_geom_%s extension",
5170 : pszGeometryType);
5171 : }
5172 :
5173 : /* Register the table in gpkg_extensions */
5174 15 : pszSQL = sqlite3_mprintf(
5175 : "INSERT INTO gpkg_extensions "
5176 : "(table_name,column_name,extension_name,definition,scope) "
5177 : "VALUES ('%q', '%q', 'gpkg_geom_%s', "
5178 : "'http://www.geopackage.org/spec120/#extension_geometry_types', "
5179 : "'read-write')",
5180 : pszT, pszC, pszGeometryType);
5181 15 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5182 15 : sqlite3_free(pszSQL);
5183 15 : if (err != OGRERR_NONE)
5184 0 : return false;
5185 : }
5186 :
5187 16 : m_abHasGeometryExtension[eGType] = true;
5188 16 : return true;
5189 : }
5190 :
5191 : /************************************************************************/
5192 : /* HasSpatialIndex() */
5193 : /************************************************************************/
5194 :
5195 49147 : bool OGRGeoPackageTableLayer::HasSpatialIndex() const
5196 : {
5197 49147 : if (!m_bFeatureDefnCompleted)
5198 3 : GetLayerDefn();
5199 49147 : if (m_bHasSpatialIndex >= 0)
5200 47904 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5201 1243 : m_bHasSpatialIndex = false;
5202 :
5203 3729 : if (m_pszFidColumn == nullptr ||
5204 2475 : m_poFeatureDefn->GetGeomFieldCount() == 0 ||
5205 1232 : !m_poDS->HasExtensionsTable())
5206 710 : return false;
5207 :
5208 533 : const char *pszT = m_pszTableName;
5209 533 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5210 : CPLString osRTreeName(
5211 1066 : CPLString("rtree_").append(pszT).append("_").append(pszC));
5212 : const std::map<CPLString, CPLString> &oMap =
5213 533 : m_poDS->GetNameTypeMapFromSQliteMaster();
5214 533 : if (cpl::contains(oMap, CPLString(osRTreeName).toupper()))
5215 : {
5216 281 : m_bHasSpatialIndex = true;
5217 281 : m_osRTreeName = std::move(osRTreeName);
5218 281 : m_osFIDForRTree = m_pszFidColumn;
5219 : }
5220 :
5221 : // Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
5222 : // Cf https://github.com/OSGeo/gdal/pull/6911
5223 533 : if (m_bHasSpatialIndex)
5224 : {
5225 : const auto nFC =
5226 281 : const_cast<OGRGeoPackageTableLayer *>(this)->GetTotalFeatureCount();
5227 281 : if (nFC >= atoi(CPLGetConfigOption(
5228 : "OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")))
5229 : {
5230 2 : CPLString osSQL = "SELECT 1 FROM \"";
5231 1 : osSQL += SQLEscapeName(pszT);
5232 1 : osSQL += "\" WHERE \"";
5233 2 : osSQL += SQLEscapeName(
5234 1 : const_cast<OGRGeoPackageTableLayer *>(this)->GetFIDColumn());
5235 1 : osSQL += "\" = ";
5236 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5237 1 : osSQL += " AND \"";
5238 1 : osSQL += SQLEscapeName(pszC);
5239 1 : osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
5240 1 : osSQL += SQLEscapeName(pszC);
5241 1 : osSQL += "\")";
5242 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1)
5243 : {
5244 1 : osSQL = "SELECT 1 FROM \"";
5245 1 : osSQL += SQLEscapeName(m_osRTreeName);
5246 1 : osSQL += "\" WHERE id = ";
5247 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5248 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0)
5249 : {
5250 1 : CPLError(CE_Warning, CPLE_AppDefined,
5251 : "Spatial index (perhaps created with GDAL 3.6.0) "
5252 : "of table %s is corrupted. Disabling its use. "
5253 : "This file should be recreated or its spatial "
5254 : "index recreated",
5255 1 : m_pszTableName);
5256 1 : m_bHasSpatialIndex = false;
5257 : }
5258 : }
5259 : }
5260 : }
5261 :
5262 533 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5263 : }
5264 :
5265 : /************************************************************************/
5266 : /* DropSpatialIndex() */
5267 : /************************************************************************/
5268 :
5269 43 : bool OGRGeoPackageTableLayer::DropSpatialIndex(bool bCalledFromSQLFunction)
5270 : {
5271 43 : if (!m_bFeatureDefnCompleted)
5272 0 : GetLayerDefn();
5273 43 : if (!CheckUpdatableTable("DropSpatialIndex"))
5274 1 : return false;
5275 :
5276 42 : if (m_bDropRTreeTable)
5277 : {
5278 0 : CPLError(CE_Failure, CPLE_AppDefined,
5279 : "Cannot run DropSpatialIndex() after non-completed deferred "
5280 : "DropSpatialIndex()");
5281 0 : return false;
5282 : }
5283 :
5284 42 : if (!HasSpatialIndex())
5285 : {
5286 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index not existing");
5287 1 : return false;
5288 : }
5289 :
5290 41 : const char *pszT = m_pszTableName;
5291 41 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5292 : {
5293 41 : char *pszSQL = sqlite3_mprintf(
5294 : "DELETE FROM gpkg_extensions WHERE lower(table_name)=lower('%q') "
5295 : "AND lower(column_name)=lower('%q') AND "
5296 : "extension_name='gpkg_rtree_index'",
5297 : pszT, pszC);
5298 41 : SQLCommand(m_poDS->GetDB(), pszSQL);
5299 41 : sqlite3_free(pszSQL);
5300 : }
5301 :
5302 41 : if (bCalledFromSQLFunction)
5303 : {
5304 : /* We cannot drop a table from a SQLite function call, so we just */
5305 : /* memorize that we will have to delete the table later */
5306 6 : m_bDropRTreeTable = true;
5307 : }
5308 : else
5309 : {
5310 : char *pszSQL =
5311 35 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5312 35 : SQLCommand(m_poDS->GetDB(), pszSQL);
5313 35 : sqlite3_free(pszSQL);
5314 : }
5315 :
5316 41 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5317 :
5318 41 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers().c_str());
5319 :
5320 41 : m_bHasSpatialIndex = false;
5321 41 : return true;
5322 : }
5323 :
5324 : /************************************************************************/
5325 : /* RunDeferredDropRTreeTableIfNecessary() */
5326 : /************************************************************************/
5327 :
5328 1678 : bool OGRGeoPackageTableLayer::RunDeferredDropRTreeTableIfNecessary()
5329 : {
5330 1678 : bool ret = true;
5331 1678 : if (m_bDropRTreeTable)
5332 : {
5333 6 : OGRGeoPackageTableLayer::ResetReading();
5334 :
5335 : char *pszSQL =
5336 6 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5337 6 : ret = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
5338 6 : sqlite3_free(pszSQL);
5339 6 : m_bDropRTreeTable = false;
5340 : }
5341 1678 : return ret;
5342 : }
5343 :
5344 : /************************************************************************/
5345 : /* ReturnSQLDropSpatialIndexTriggers() */
5346 : /************************************************************************/
5347 :
5348 63 : CPLString OGRGeoPackageTableLayer::ReturnSQLDropSpatialIndexTriggers()
5349 : {
5350 63 : char *pszSQL = sqlite3_mprintf(
5351 : "DROP TRIGGER \"%w_insert\";"
5352 : "DROP TRIGGER IF EXISTS \"%w_update1\";" // replaced by update6 and update7 in GPKG 1.4
5353 : "DROP TRIGGER \"%w_update2\";"
5354 : "DROP TRIGGER IF EXISTS \"%w_update3\";" // replace by update5 in GPKG 1.4
5355 : "DROP TRIGGER \"%w_update4\";"
5356 : "DROP TRIGGER IF EXISTS \"%w_update5\";" // replace update3 in GPKG 1.4
5357 : "DROP TRIGGER IF EXISTS \"%w_update6\";" // replace update1 in GPKG 1.4
5358 : "DROP TRIGGER IF EXISTS \"%w_update7\";" // replace update1 in GPKG 1.4
5359 : "DROP TRIGGER \"%w_delete\";",
5360 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5361 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5362 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str());
5363 63 : CPLString osSQL(pszSQL);
5364 63 : sqlite3_free(pszSQL);
5365 :
5366 63 : return osSQL;
5367 : }
5368 :
5369 : /************************************************************************/
5370 : /* Rename() */
5371 : /************************************************************************/
5372 :
5373 12 : OGRErr OGRGeoPackageTableLayer::Rename(const char *pszDstTableName)
5374 : {
5375 12 : if (!m_bFeatureDefnCompleted)
5376 2 : GetLayerDefn();
5377 12 : if (!CheckUpdatableTable("Rename"))
5378 0 : return OGRERR_FAILURE;
5379 :
5380 12 : ResetReading();
5381 12 : SyncToDisk();
5382 :
5383 12 : char *pszSQL = sqlite3_mprintf(
5384 : "SELECT 1 FROM sqlite_master WHERE lower(name) = lower('%q') "
5385 : "AND type IN ('table', 'view')",
5386 : pszDstTableName);
5387 : const bool bAlreadyExists =
5388 12 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5389 12 : sqlite3_free(pszSQL);
5390 12 : if (bAlreadyExists)
5391 : {
5392 3 : CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
5393 : pszDstTableName);
5394 3 : return OGRERR_FAILURE;
5395 : }
5396 :
5397 : // Temporary remove foreign key checks
5398 : const GPKGTemporaryForeignKeyCheckDisabler
5399 18 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
5400 :
5401 9 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
5402 : {
5403 0 : return OGRERR_FAILURE;
5404 : }
5405 :
5406 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5407 9 : DisableFeatureCountTriggers(false);
5408 : #endif
5409 :
5410 18 : CPLString osSQL;
5411 :
5412 9 : pszSQL = sqlite3_mprintf(
5413 : "UPDATE gpkg_geometry_columns SET table_name = '%q' WHERE "
5414 : "lower(table_name )= lower('%q');",
5415 : pszDstTableName, m_pszTableName);
5416 9 : osSQL += pszSQL;
5417 9 : sqlite3_free(pszSQL);
5418 :
5419 : // Rename the identifier if it defaulted to the table name
5420 9 : pszSQL = sqlite3_mprintf(
5421 : "UPDATE gpkg_contents SET identifier = '%q' WHERE "
5422 : "lower(table_name) = lower('%q') AND identifier = '%q';",
5423 : pszDstTableName, m_pszTableName, m_pszTableName);
5424 9 : osSQL += pszSQL;
5425 9 : sqlite3_free(pszSQL);
5426 :
5427 9 : pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET table_name = '%q' WHERE "
5428 : "lower(table_name )= lower('%q');",
5429 : pszDstTableName, m_pszTableName);
5430 9 : osSQL += pszSQL;
5431 9 : sqlite3_free(pszSQL);
5432 :
5433 9 : if (m_poDS->HasExtensionsTable())
5434 : {
5435 8 : pszSQL = sqlite3_mprintf(
5436 : "UPDATE gpkg_extensions SET table_name = '%q' WHERE "
5437 : "lower(table_name )= lower('%q');",
5438 : pszDstTableName, m_pszTableName);
5439 8 : osSQL += pszSQL;
5440 8 : sqlite3_free(pszSQL);
5441 : }
5442 :
5443 9 : if (m_poDS->HasMetadataTables())
5444 : {
5445 4 : pszSQL = sqlite3_mprintf(
5446 : "UPDATE gpkg_metadata_reference SET table_name = '%q' WHERE "
5447 : "lower(table_name )= lower('%q');",
5448 : pszDstTableName, m_pszTableName);
5449 4 : osSQL += pszSQL;
5450 4 : sqlite3_free(pszSQL);
5451 : }
5452 :
5453 9 : if (m_poDS->HasDataColumnsTable())
5454 : {
5455 1 : pszSQL = sqlite3_mprintf(
5456 : "UPDATE gpkg_data_columns SET table_name = '%q' WHERE "
5457 : "lower(table_name )= lower('%q');",
5458 : pszDstTableName, m_pszTableName);
5459 1 : osSQL += pszSQL;
5460 1 : sqlite3_free(pszSQL);
5461 : }
5462 :
5463 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5464 9 : if (m_poDS->m_bHasGPKGOGRContents)
5465 : {
5466 9 : pszSQL = sqlite3_mprintf(
5467 : "UPDATE gpkg_ogr_contents SET table_name = '%q' WHERE "
5468 : "lower(table_name )= lower('%q');",
5469 : pszDstTableName, m_pszTableName);
5470 9 : osSQL += pszSQL;
5471 9 : sqlite3_free(pszSQL);
5472 : }
5473 : #endif
5474 :
5475 9 : if (m_poDS->HasGpkgextRelationsTable())
5476 : {
5477 2 : pszSQL = sqlite3_mprintf(
5478 : "UPDATE gpkgext_relations SET base_table_name = '%q' WHERE "
5479 : "lower(base_table_name )= lower('%q');",
5480 : pszDstTableName, m_pszTableName);
5481 2 : osSQL += pszSQL;
5482 2 : sqlite3_free(pszSQL);
5483 :
5484 2 : pszSQL = sqlite3_mprintf(
5485 : "UPDATE gpkgext_relations SET related_table_name = '%q' WHERE "
5486 : "lower(related_table_name )= lower('%q');",
5487 : pszDstTableName, m_pszTableName);
5488 2 : osSQL += pszSQL;
5489 : ;
5490 2 : sqlite3_free(pszSQL);
5491 :
5492 2 : pszSQL = sqlite3_mprintf(
5493 : "UPDATE gpkgext_relations SET mapping_table_name = '%q' WHERE "
5494 : "lower(mapping_table_name )= lower('%q');",
5495 : pszDstTableName, m_pszTableName);
5496 2 : osSQL += pszSQL;
5497 2 : sqlite3_free(pszSQL);
5498 : }
5499 :
5500 9 : if (m_poDS->HasQGISLayerStyles())
5501 : {
5502 : pszSQL =
5503 1 : sqlite3_mprintf("UPDATE layer_styles SET f_table_name = '%q' WHERE "
5504 : "f_table_name = '%q';",
5505 : pszDstTableName, m_pszTableName);
5506 1 : osSQL += pszSQL;
5507 1 : sqlite3_free(pszSQL);
5508 : }
5509 :
5510 9 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5511 : m_pszTableName, pszDstTableName);
5512 9 : osSQL += pszSQL;
5513 9 : sqlite3_free(pszSQL);
5514 :
5515 9 : const bool bHasSpatialIndex = HasSpatialIndex();
5516 9 : CPLString osRTreeNameNew;
5517 9 : if (bHasSpatialIndex)
5518 : {
5519 8 : osRTreeNameNew = "rtree_";
5520 8 : osRTreeNameNew += pszDstTableName;
5521 8 : osRTreeNameNew += "_";
5522 8 : osRTreeNameNew += m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5523 :
5524 8 : osSQL += ReturnSQLDropSpatialIndexTriggers();
5525 8 : osSQL += ';';
5526 :
5527 8 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5528 : m_osRTreeName.c_str(), osRTreeNameNew.c_str());
5529 8 : osSQL += pszSQL;
5530 8 : sqlite3_free(pszSQL);
5531 :
5532 8 : osSQL += ReturnSQLCreateSpatialIndexTriggers(pszDstTableName, nullptr);
5533 : }
5534 :
5535 9 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL);
5536 :
5537 : // Check foreign key integrity
5538 9 : if (eErr == OGRERR_NONE)
5539 : {
5540 9 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
5541 : }
5542 :
5543 9 : if (eErr == OGRERR_NONE)
5544 : {
5545 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5546 9 : CreateFeatureCountTriggers(pszDstTableName);
5547 : #endif
5548 :
5549 9 : eErr = m_poDS->SoftCommitTransaction();
5550 9 : if (eErr == OGRERR_NONE)
5551 : {
5552 9 : m_poDS->RemoveTableFromSQLiteMasterCache(m_pszTableName);
5553 :
5554 9 : CPLFree(m_pszTableName);
5555 9 : m_pszTableName = CPLStrdup(pszDstTableName);
5556 :
5557 9 : if (bHasSpatialIndex)
5558 : {
5559 8 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5560 8 : m_osRTreeName = std::move(osRTreeNameNew);
5561 : }
5562 : }
5563 : }
5564 : else
5565 : {
5566 0 : m_poDS->SoftRollbackTransaction();
5567 : }
5568 :
5569 9 : if (eErr == OGRERR_NONE)
5570 : {
5571 9 : m_poDS->ClearCachedRelationships();
5572 :
5573 9 : SetDescription(pszDstTableName);
5574 9 : whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
5575 : }
5576 :
5577 9 : return eErr;
5578 : }
5579 :
5580 : /************************************************************************/
5581 : /* ISetSpatialFilter() */
5582 : /************************************************************************/
5583 :
5584 23181 : OGRErr OGRGeoPackageTableLayer::ISetSpatialFilter(int /*iGeomField*/,
5585 : const OGRGeometry *poGeomIn)
5586 :
5587 : {
5588 23181 : if (!m_bFeatureDefnCompleted)
5589 0 : GetLayerDefn();
5590 23181 : if (InstallFilter(poGeomIn))
5591 : {
5592 23147 : BuildWhere();
5593 :
5594 23147 : ResetReading();
5595 : }
5596 23181 : return OGRERR_NONE;
5597 : }
5598 :
5599 : /************************************************************************/
5600 : /* HasFastSpatialFilter() */
5601 : /************************************************************************/
5602 :
5603 2 : bool OGRGeoPackageTableLayer::HasFastSpatialFilter(int m_iGeomColIn)
5604 : {
5605 4 : if (m_iGeomColIn < 0 ||
5606 2 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5607 0 : return false;
5608 2 : return HasSpatialIndex();
5609 : }
5610 :
5611 : /************************************************************************/
5612 : /* GetSpatialWhere() */
5613 : /************************************************************************/
5614 :
5615 23256 : CPLString OGRGeoPackageTableLayer::GetSpatialWhere(int m_iGeomColIn,
5616 : OGRGeometry *poFilterGeom)
5617 : {
5618 23256 : CPLString osSpatialWHERE;
5619 :
5620 46512 : if (m_iGeomColIn < 0 ||
5621 23256 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5622 2 : return osSpatialWHERE;
5623 :
5624 23254 : if (poFilterGeom != nullptr)
5625 : {
5626 23161 : OGREnvelope sEnvelope;
5627 :
5628 23161 : poFilterGeom->getEnvelope(&sEnvelope);
5629 :
5630 : const char *pszC =
5631 23161 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomColIn)->GetNameRef();
5632 :
5633 23169 : if (std::isinf(sEnvelope.MinX) && sEnvelope.MinX < 0 &&
5634 12 : std::isinf(sEnvelope.MinY) && sEnvelope.MinY < 0 &&
5635 12 : std::isinf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 &&
5636 23169 : std::isinf(sEnvelope.MaxY) && sEnvelope.MaxY > 0)
5637 : {
5638 : osSpatialWHERE.Printf(
5639 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5640 4 : SQLEscapeName(pszC).c_str(), SQLEscapeName(pszC).c_str());
5641 149 : return osSpatialWHERE;
5642 : }
5643 :
5644 23157 : bool bUseSpatialIndex = true;
5645 46296 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
5646 222 : sEnvelope.MinY <= m_poExtent->MinY &&
5647 46448 : sEnvelope.MaxX >= m_poExtent->MaxX &&
5648 152 : sEnvelope.MaxY >= m_poExtent->MaxY)
5649 : {
5650 : // Selecting from spatial filter on whole extent can be rather
5651 : // slow. So use function based filtering, just in case the
5652 : // advertized global extent might be wrong. Otherwise we might
5653 : // just discard completely the spatial filter.
5654 149 : bUseSpatialIndex = false;
5655 : }
5656 :
5657 23157 : if (bUseSpatialIndex && HasSpatialIndex())
5658 : {
5659 : osSpatialWHERE.Printf(
5660 : "\"%s\" IN ( SELECT id FROM \"%s\" WHERE "
5661 : "maxx >= %.12f AND minx <= %.12f AND "
5662 : "maxy >= %.12f AND miny <= %.12f)",
5663 46010 : SQLEscapeName(m_osFIDForRTree).c_str(),
5664 46010 : SQLEscapeName(m_osRTreeName).c_str(), sEnvelope.MinX - 1e-11,
5665 23005 : sEnvelope.MaxX + 1e-11, sEnvelope.MinY - 1e-11,
5666 69015 : sEnvelope.MaxY + 1e-11);
5667 : }
5668 : else
5669 : {
5670 152 : if (HasSpatialIndex())
5671 : {
5672 : // If we do have a spatial index, and our filter contains the
5673 : // bounding box of the RTree, then just filter on non-null
5674 : // non-empty geometries.
5675 : double minx, miny, maxx, maxy;
5676 146 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, minx,
5677 146 : miny, maxx, maxy) &&
5678 146 : sEnvelope.MinX <= minx && sEnvelope.MinY <= miny &&
5679 292 : sEnvelope.MaxX >= maxx && sEnvelope.MaxY >= maxy)
5680 : {
5681 : osSpatialWHERE.Printf(
5682 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5683 290 : SQLEscapeName(pszC).c_str(),
5684 435 : SQLEscapeName(pszC).c_str());
5685 145 : return osSpatialWHERE;
5686 : }
5687 : }
5688 :
5689 : /* A bit inefficient but still faster than OGR filtering */
5690 : osSpatialWHERE.Printf(
5691 : "ST_EnvelopesIntersects(\"%s\", %.12f, %.12f, %.12f, %.12f)",
5692 7 : SQLEscapeName(pszC).c_str(), sEnvelope.MinX - 1e-11,
5693 7 : sEnvelope.MinY - 1e-11, sEnvelope.MaxX + 1e-11,
5694 7 : sEnvelope.MaxY + 1e-11);
5695 : }
5696 : }
5697 :
5698 23105 : return osSpatialWHERE;
5699 : }
5700 :
5701 : /************************************************************************/
5702 : /* BuildWhere() */
5703 : /* */
5704 : /* Build the WHERE statement appropriate to the current set of */
5705 : /* criteria (spatial and attribute queries). */
5706 : /************************************************************************/
5707 :
5708 23236 : void OGRGeoPackageTableLayer::BuildWhere()
5709 :
5710 : {
5711 23236 : m_soFilter = "";
5712 :
5713 : CPLString osSpatialWHERE =
5714 46472 : GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
5715 23236 : if (!osSpatialWHERE.empty())
5716 : {
5717 23141 : m_soFilter += osSpatialWHERE;
5718 : }
5719 :
5720 23236 : if (!osQuery.empty())
5721 : {
5722 46 : if (m_soFilter.empty())
5723 : {
5724 40 : m_soFilter += osQuery;
5725 : }
5726 : else
5727 : {
5728 6 : m_soFilter += " AND (";
5729 6 : m_soFilter += osQuery;
5730 6 : m_soFilter += ")";
5731 : }
5732 : }
5733 23236 : CPLDebug("GPKG", "Filter: %s", m_soFilter.c_str());
5734 23236 : }
5735 :
5736 : /************************************************************************/
5737 : /* SetOpeningParameters() */
5738 : /************************************************************************/
5739 :
5740 3401 : void OGRGeoPackageTableLayer::SetOpeningParameters(
5741 : const char *pszTableName, const char *pszObjectType, bool bIsInGpkgContents,
5742 : bool bIsSpatial, const char *pszGeomColName, const char *pszGeomType,
5743 : bool bHasZ, bool bHasM)
5744 : {
5745 3401 : CPLFree(m_pszTableName);
5746 3401 : m_pszTableName = CPLStrdup(pszTableName);
5747 3401 : m_bIsTable = EQUAL(pszObjectType, "table");
5748 3401 : m_bIsInGpkgContents = bIsInGpkgContents;
5749 3401 : m_bIsSpatial = bIsSpatial;
5750 3401 : if (pszGeomType)
5751 : {
5752 : OGRwkbGeometryType eType =
5753 1276 : GPkgGeometryTypeToWKB(pszGeomType, bHasZ, bHasM);
5754 1276 : m_poFeatureDefn->SetGeomType(eType);
5755 1276 : if (eType != wkbNone)
5756 : {
5757 1276 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
5758 : }
5759 : }
5760 3401 : }
5761 :
5762 : /************************************************************************/
5763 : /* SetCreationParameters() */
5764 : /************************************************************************/
5765 :
5766 1029 : void OGRGeoPackageTableLayer::SetCreationParameters(
5767 : OGRwkbGeometryType eGType, const char *pszGeomColumnName, int bGeomNullable,
5768 : const OGRSpatialReference *poSRS, const char *pszSRID,
5769 : const OGRGeomCoordinatePrecision &oCoordPrec, bool bDiscardCoordLSB,
5770 : bool bUndoDiscardCoordLSBOnReading, const char *pszFIDColumnName,
5771 : const char *pszIdentifier, const char *pszDescription)
5772 : {
5773 1029 : m_bIsSpatial = eGType != wkbNone;
5774 1029 : m_bIsInGpkgContents =
5775 1111 : m_bIsSpatial ||
5776 82 : !m_poDS->HasNonSpatialTablesNonRegisteredInGpkgContents();
5777 1029 : m_bFeatureDefnCompleted = true;
5778 1029 : m_bDeferredCreation = true;
5779 1029 : m_bTableCreatedInTransaction = m_poDS->IsInTransaction();
5780 1029 : m_bHasTriedDetectingFID64 = true;
5781 1029 : m_pszFidColumn = CPLStrdup(pszFIDColumnName);
5782 1029 : m_bUndoDiscardCoordLSBOnReading = bUndoDiscardCoordLSBOnReading;
5783 :
5784 1029 : if (eGType != wkbNone)
5785 : {
5786 947 : m_nZFlag = wkbHasZ(eGType) ? 1 : 0;
5787 947 : m_nMFlag = wkbHasM(eGType) ? 1 : 0;
5788 :
5789 1894 : OGRSpatialReferenceRefCountedPtr poGotSRS;
5790 1894 : OGRGeomFieldDefn oGeomFieldDefn(pszGeomColumnName, eGType);
5791 :
5792 947 : oGeomFieldDefn.SetSpatialRef(poSRS);
5793 947 : if (pszSRID)
5794 : {
5795 10 : m_iSrs = atoi(pszSRID);
5796 10 : if (m_iSrs == GDALGeoPackageDataset::FIRST_CUSTOM_SRSID - 1)
5797 : {
5798 1 : m_iSrs = m_poDS->GetSrsId(nullptr);
5799 1 : oGeomFieldDefn.SetSpatialRef(nullptr);
5800 : }
5801 : else
5802 : {
5803 : poGotSRS =
5804 18 : m_poDS->GetSpatialRef(m_iSrs, /* bFallbackToEPSG = */ false,
5805 9 : /* bEmitErrorIfNotFound = */ false);
5806 9 : if (poGotSRS)
5807 : {
5808 6 : oGeomFieldDefn.SetSpatialRef(poGotSRS.get());
5809 : }
5810 : else
5811 : {
5812 3 : bool bOK = false;
5813 : auto poSRSTmp =
5814 6 : OGRSpatialReferenceRefCountedPtr::makeInstance();
5815 3 : if (m_iSrs < 32767)
5816 : {
5817 : CPLErrorHandlerPusher oErrorHandler(
5818 4 : CPLQuietErrorHandler);
5819 4 : CPLErrorStateBackuper oBackuper;
5820 2 : if (poSRSTmp->importFromEPSG(m_iSrs) == OGRERR_NONE)
5821 : {
5822 1 : bOK = true;
5823 1 : poSRSTmp->SetAxisMappingStrategy(
5824 : OAMS_TRADITIONAL_GIS_ORDER);
5825 1 : m_iSrs = m_poDS->GetSrsId(poSRSTmp.get());
5826 1 : oGeomFieldDefn.SetSpatialRef(poSRSTmp.get());
5827 : }
5828 : }
5829 3 : if (!bOK)
5830 : {
5831 2 : CPLError(
5832 : CE_Warning, CPLE_AppDefined,
5833 : "No entry in gpkg_spatial_ref_sys matching SRID=%s",
5834 : pszSRID);
5835 : }
5836 : }
5837 : }
5838 : }
5839 : else
5840 : {
5841 937 : m_iSrs = m_poDS->GetSrsId(poSRS);
5842 : }
5843 947 : oGeomFieldDefn.SetNullable(bGeomNullable);
5844 947 : oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
5845 :
5846 947 : if (bDiscardCoordLSB)
5847 2 : m_sBinaryPrecision.SetFrom(oCoordPrec);
5848 :
5849 : // Save coordinate precision in gpkg_metadata/gpkg_metadata_reference
5850 2831 : if ((oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5851 937 : oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5852 1894 : oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN) &&
5853 10 : (m_poDS->HasMetadataTables() || m_poDS->CreateMetadataTables()))
5854 : {
5855 20 : std::string osCoordPrecision = "<CoordinatePrecision ";
5856 10 : if (oCoordPrec.dfXYResolution !=
5857 : OGRGeomCoordinatePrecision::UNKNOWN)
5858 : osCoordPrecision += CPLSPrintf(" xy_resolution=\"%g\"",
5859 10 : oCoordPrec.dfXYResolution);
5860 10 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5861 : osCoordPrecision += CPLSPrintf(" z_resolution=\"%g\"",
5862 7 : oCoordPrec.dfZResolution);
5863 10 : if (oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5864 : osCoordPrecision += CPLSPrintf(" m_resolution=\"%g\"",
5865 7 : oCoordPrec.dfMResolution);
5866 : osCoordPrecision += CPLSPrintf(" discard_coord_lsb=\"%s\"",
5867 10 : bDiscardCoordLSB ? "true" : "false");
5868 : osCoordPrecision +=
5869 : CPLSPrintf(" undo_discard_coord_lsb_on_reading=\"%s\"",
5870 10 : m_bUndoDiscardCoordLSBOnReading ? "true" : "false");
5871 10 : osCoordPrecision += " />";
5872 :
5873 10 : char *pszSQL = sqlite3_mprintf(
5874 : "INSERT INTO gpkg_metadata "
5875 : "(md_scope, md_standard_uri, mime_type, metadata) VALUES "
5876 : "('dataset','http://gdal.org','text/xml','%q')",
5877 : osCoordPrecision.c_str());
5878 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5879 10 : sqlite3_free(pszSQL);
5880 :
5881 : const sqlite_int64 nFID =
5882 10 : sqlite3_last_insert_rowid(m_poDS->GetDB());
5883 10 : pszSQL = sqlite3_mprintf(
5884 : "INSERT INTO gpkg_metadata_reference (reference_scope, "
5885 : "table_name, column_name, timestamp, md_file_id) VALUES "
5886 : "('column', '%q', '%q', %s, %d)",
5887 : m_pszTableName, pszGeomColumnName,
5888 20 : m_poDS->GetCurrentDateEscapedSQL().c_str(),
5889 : static_cast<int>(nFID));
5890 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5891 10 : sqlite3_free(pszSQL);
5892 : }
5893 :
5894 947 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
5895 : }
5896 1029 : if (pszIdentifier)
5897 : {
5898 3 : m_osIdentifierLCO = pszIdentifier;
5899 3 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
5900 : }
5901 1029 : if (pszDescription)
5902 : {
5903 5 : m_osDescriptionLCO = pszDescription;
5904 5 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
5905 : }
5906 :
5907 1029 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
5908 1029 : }
5909 :
5910 : /************************************************************************/
5911 : /* RegisterGeometryColumn() */
5912 : /************************************************************************/
5913 :
5914 950 : OGRErr OGRGeoPackageTableLayer::RegisterGeometryColumn()
5915 : {
5916 950 : OGRwkbGeometryType eGType = GetGeomType();
5917 950 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5918 : /* Requirement 27: The z value in a gpkg_geometry_columns table row */
5919 : /* SHALL be one of 0 (none), 1 (mandatory), or 2 (optional) */
5920 :
5921 : /* Update gpkg_geometry_columns with the table info */
5922 : char *pszSQL =
5923 950 : sqlite3_mprintf("INSERT INTO gpkg_geometry_columns "
5924 : "(table_name,column_name,geometry_type_name,srs_id,z,m)"
5925 : " VALUES "
5926 : "('%q','%q','%q',%d,%d,%d)",
5927 : GetName(), GetGeometryColumn(), pszGeometryType, m_iSrs,
5928 : m_nZFlag, m_nMFlag);
5929 :
5930 950 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5931 950 : sqlite3_free(pszSQL);
5932 950 : if (err != OGRERR_NONE)
5933 0 : return OGRERR_FAILURE;
5934 :
5935 950 : if (wkbFlatten(eGType) > wkbGeometryCollection)
5936 : {
5937 6 : CreateGeometryExtensionIfNecessary(eGType);
5938 : }
5939 :
5940 950 : return OGRERR_NONE;
5941 : }
5942 :
5943 : /************************************************************************/
5944 : /* GetColumnsOfCreateTable() */
5945 : /************************************************************************/
5946 :
5947 1057 : CPLString OGRGeoPackageTableLayer::GetColumnsOfCreateTable(
5948 : const std::vector<OGRFieldDefn *> &apoFields)
5949 : {
5950 1057 : CPLString osSQL;
5951 :
5952 1057 : char *pszSQL = nullptr;
5953 1057 : bool bNeedComma = false;
5954 1057 : if (m_pszFidColumn != nullptr)
5955 : {
5956 : pszSQL =
5957 1057 : sqlite3_mprintf("\"%w\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
5958 : m_pszFidColumn);
5959 1057 : osSQL += pszSQL;
5960 1057 : sqlite3_free(pszSQL);
5961 1057 : bNeedComma = true;
5962 : }
5963 :
5964 1057 : const OGRwkbGeometryType eGType = GetGeomType();
5965 1057 : if (eGType != wkbNone)
5966 : {
5967 976 : if (bNeedComma)
5968 : {
5969 976 : osSQL += ", ";
5970 : }
5971 976 : bNeedComma = true;
5972 :
5973 : /* Requirement 25: The geometry_type_name value in a
5974 : * gpkg_geometry_columns */
5975 : /* row SHALL be one of the uppercase geometry type names specified in */
5976 : /* Geometry Types (Normative). */
5977 976 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5978 :
5979 : pszSQL =
5980 976 : sqlite3_mprintf("\"%w\" %s", GetGeometryColumn(), pszGeometryType);
5981 976 : osSQL += pszSQL;
5982 976 : sqlite3_free(pszSQL);
5983 976 : if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable())
5984 : {
5985 2 : osSQL += " NOT NULL";
5986 : }
5987 : }
5988 :
5989 5608 : for (size_t i = 0; i < apoFields.size(); i++)
5990 : {
5991 4551 : OGRFieldDefn *poFieldDefn = apoFields[i];
5992 : // Eg. when a geometry type is specified + an sql statement returns no
5993 : // or NULL geometry values, the geom column is incorrectly treated as
5994 : // an attribute column as well with the same name. Not ideal, but skip
5995 : // this column here to avoid duplicate column name error. Issue: #6976.
5996 8947 : if ((eGType != wkbNone) &&
5997 4396 : (EQUAL(poFieldDefn->GetNameRef(), GetGeometryColumn())))
5998 : {
5999 0 : continue;
6000 : }
6001 4551 : if (bNeedComma)
6002 : {
6003 4551 : osSQL += ", ";
6004 : }
6005 4551 : bNeedComma = true;
6006 :
6007 4551 : pszSQL = sqlite3_mprintf("\"%w\" %s", poFieldDefn->GetNameRef(),
6008 : GPkgFieldFromOGR(poFieldDefn->GetType(),
6009 : poFieldDefn->GetSubType(),
6010 : poFieldDefn->GetWidth()));
6011 4551 : osSQL += pszSQL;
6012 4551 : sqlite3_free(pszSQL);
6013 4551 : if (!poFieldDefn->IsNullable())
6014 : {
6015 15 : osSQL += " NOT NULL";
6016 : }
6017 4551 : if (poFieldDefn->IsUnique())
6018 : {
6019 18 : osSQL += " UNIQUE";
6020 : }
6021 4551 : const char *pszDefault = poFieldDefn->GetDefault();
6022 4566 : if (pszDefault != nullptr &&
6023 15 : (!poFieldDefn->IsDefaultDriverSpecific() ||
6024 1 : (pszDefault[0] == '(' &&
6025 1 : pszDefault[strlen(pszDefault) - 1] == ')' &&
6026 1 : (STARTS_WITH_CI(pszDefault + 1, "strftime") ||
6027 0 : STARTS_WITH_CI(pszDefault + 1, " strftime")))))
6028 : {
6029 15 : osSQL += " DEFAULT ";
6030 : OGRField sField;
6031 19 : if (poFieldDefn->GetType() == OFTDateTime &&
6032 4 : OGRParseDate(pszDefault, &sField, 0))
6033 : {
6034 : char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
6035 0 : OGRGetISO8601DateTime(&sField, false, szBuffer);
6036 0 : osSQL += szBuffer;
6037 : }
6038 : /* Make sure CURRENT_TIMESTAMP is translated into appropriate format
6039 : * for GeoPackage */
6040 19 : else if (poFieldDefn->GetType() == OFTDateTime &&
6041 4 : EQUAL(pszDefault, "CURRENT_TIMESTAMP"))
6042 : {
6043 1 : osSQL += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
6044 : }
6045 : else
6046 : {
6047 14 : osSQL += poFieldDefn->GetDefault();
6048 : }
6049 : }
6050 : }
6051 :
6052 1057 : return osSQL;
6053 : }
6054 :
6055 : /************************************************************************/
6056 : /* RunDeferredCreationIfNecessary() */
6057 : /************************************************************************/
6058 :
6059 6002 : OGRErr OGRGeoPackageTableLayer::RunDeferredCreationIfNecessary()
6060 : {
6061 6002 : if (!m_bDeferredCreation)
6062 4973 : return OGRERR_NONE;
6063 1029 : m_bDeferredCreation = false;
6064 :
6065 1029 : const char *pszLayerName = m_poFeatureDefn->GetName();
6066 :
6067 : /* Create the table! */
6068 2058 : CPLString osCommand;
6069 :
6070 1029 : char *pszSQL = sqlite3_mprintf("CREATE TABLE \"%w\" ( ", pszLayerName);
6071 1029 : osCommand += pszSQL;
6072 1029 : sqlite3_free(pszSQL);
6073 :
6074 2058 : std::vector<OGRFieldDefn *> apoFields;
6075 5519 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
6076 : {
6077 4490 : if (i == m_iFIDAsRegularColumnIndex)
6078 4 : continue;
6079 4486 : apoFields.push_back(m_poFeatureDefn->GetFieldDefn(i));
6080 : }
6081 :
6082 1029 : osCommand += GetColumnsOfCreateTable(apoFields);
6083 :
6084 1029 : osCommand += ")";
6085 :
6086 : #ifdef DEBUG
6087 1029 : CPLDebug("GPKG", "exec(%s)", osCommand.c_str());
6088 : #endif
6089 1029 : OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
6090 1029 : if (OGRERR_NONE != err)
6091 0 : return OGRERR_FAILURE;
6092 :
6093 5515 : for (auto &poField : apoFields)
6094 : {
6095 4486 : if (!DoSpecialProcessingForColumnCreation(poField))
6096 : {
6097 0 : return OGRERR_FAILURE;
6098 : }
6099 : }
6100 :
6101 : /* Update gpkg_contents with the table info */
6102 1029 : const OGRwkbGeometryType eGType = GetGeomType();
6103 1029 : const bool bIsSpatial = (eGType != wkbNone);
6104 :
6105 1029 : if (bIsSpatial || m_eASpatialVariant == GPKG_ATTRIBUTES)
6106 : {
6107 1017 : const char *pszIdentifier = GetMetadataItem("IDENTIFIER");
6108 1017 : if (pszIdentifier == nullptr)
6109 1014 : pszIdentifier = pszLayerName;
6110 1017 : const char *pszDescription = GetMetadataItem("DESCRIPTION");
6111 1017 : if (pszDescription == nullptr)
6112 1012 : pszDescription = "";
6113 :
6114 1017 : pszSQL = sqlite3_mprintf(
6115 : "INSERT INTO gpkg_contents "
6116 : "(table_name,data_type,identifier,description,last_change,srs_id) "
6117 : "VALUES "
6118 : "('%q','%q','%q','%q',%s,%d)",
6119 : pszLayerName, (bIsSpatial ? "features" : "attributes"),
6120 : pszIdentifier, pszDescription,
6121 2034 : GDALGeoPackageDataset::GetCurrentDateEscapedSQL().c_str(), m_iSrs);
6122 :
6123 1017 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6124 1017 : sqlite3_free(pszSQL);
6125 1017 : if (err != OGRERR_NONE)
6126 0 : return OGRERR_FAILURE;
6127 : }
6128 :
6129 1029 : if (bIsSpatial)
6130 : {
6131 : // Insert into gpkg_geometry_columns after gpkg_contents because of
6132 : // foreign key constraints
6133 948 : err = RegisterGeometryColumn();
6134 948 : if (err != OGRERR_NONE)
6135 0 : return OGRERR_FAILURE;
6136 : }
6137 :
6138 : #ifdef ENABLE_GPKG_OGR_CONTENTS
6139 1029 : if (m_poDS->m_bHasGPKGOGRContents)
6140 : {
6141 1023 : pszSQL = sqlite3_mprintf("DELETE FROM gpkg_ogr_contents WHERE "
6142 : "lower(table_name) = lower('%q')",
6143 : pszLayerName);
6144 1023 : SQLCommand(m_poDS->GetDB(), pszSQL);
6145 1023 : sqlite3_free(pszSQL);
6146 :
6147 1023 : pszSQL = sqlite3_mprintf(
6148 : "INSERT INTO gpkg_ogr_contents (table_name, feature_count) "
6149 : "VALUES ('%q', 0)",
6150 : pszLayerName);
6151 1023 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6152 1023 : sqlite3_free(pszSQL);
6153 1023 : if (err == OGRERR_NONE)
6154 : {
6155 1023 : m_nTotalFeatureCount = 0;
6156 1023 : m_bAddOGRFeatureCountTriggers = true;
6157 : }
6158 : }
6159 : #endif
6160 :
6161 1029 : ResetReading();
6162 :
6163 1029 : return OGRERR_NONE;
6164 : }
6165 :
6166 : /************************************************************************/
6167 : /* GetMetadata() */
6168 : /************************************************************************/
6169 :
6170 15442 : CSLConstList OGRGeoPackageTableLayer::GetMetadata(const char *pszDomain)
6171 :
6172 : {
6173 15442 : if (!m_bFeatureDefnCompleted)
6174 362 : GetLayerDefn();
6175 15442 : if (!m_bHasTriedDetectingFID64 && m_pszFidColumn != nullptr)
6176 : {
6177 450 : m_bHasTriedDetectingFID64 = true;
6178 :
6179 : /* --------------------------------------------------------------------
6180 : */
6181 : /* Find if the FID holds 64bit values */
6182 : /* --------------------------------------------------------------------
6183 : */
6184 :
6185 : // Normally the fid should be AUTOINCREMENT, so check sqlite_sequence
6186 450 : OGRErr err = OGRERR_NONE;
6187 : char *pszSQL =
6188 450 : sqlite3_mprintf("SELECT seq FROM sqlite_sequence WHERE name = '%q'",
6189 : m_pszTableName);
6190 450 : CPLPushErrorHandler(CPLQuietErrorHandler);
6191 450 : GIntBig nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
6192 450 : CPLPopErrorHandler();
6193 450 : sqlite3_free(pszSQL);
6194 450 : if (err != OGRERR_NONE)
6195 : {
6196 47 : CPLErrorReset();
6197 :
6198 : // In case of error, fallback to taking the MAX of the FID
6199 47 : pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
6200 : m_pszFidColumn, m_pszTableName);
6201 :
6202 47 : nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, nullptr);
6203 47 : sqlite3_free(pszSQL);
6204 : }
6205 450 : if (nMaxId > INT_MAX)
6206 1 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
6207 : }
6208 :
6209 15442 : if (m_bHasReadMetadataFromStorage)
6210 13971 : return OGRLayer::GetMetadata(pszDomain);
6211 :
6212 1471 : m_bHasReadMetadataFromStorage = true;
6213 :
6214 1471 : if (!m_poDS->HasMetadataTables())
6215 1138 : return OGRLayer::GetMetadata(pszDomain);
6216 :
6217 333 : char *pszSQL = sqlite3_mprintf(
6218 : "SELECT md.metadata, md.md_standard_uri, md.mime_type, "
6219 : "mdr.reference_scope "
6220 : "FROM gpkg_metadata md "
6221 : "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
6222 : "WHERE lower(mdr.table_name) = lower('%q') ORDER BY md.id "
6223 : "LIMIT 1000", // to avoid denial of service
6224 : m_pszTableName);
6225 :
6226 666 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
6227 333 : sqlite3_free(pszSQL);
6228 333 : if (!oResult)
6229 : {
6230 0 : return OGRLayer::GetMetadata(pszDomain);
6231 : }
6232 :
6233 333 : char **papszMetadata = CSLDuplicate(OGRLayer::GetMetadata());
6234 :
6235 : /* GDAL metadata */
6236 634 : for (int i = 0; i < oResult->RowCount(); i++)
6237 : {
6238 301 : const char *pszMetadata = oResult->GetValue(0, i);
6239 301 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6240 301 : const char *pszMimeType = oResult->GetValue(2, i);
6241 301 : const char *pszReferenceScope = oResult->GetValue(3, i);
6242 301 : if (pszMetadata && pszMDStandardURI && pszMimeType &&
6243 301 : pszReferenceScope && EQUAL(pszMDStandardURI, "http://gdal.org") &&
6244 301 : EQUAL(pszMimeType, "text/xml") && EQUAL(pszReferenceScope, "table"))
6245 : {
6246 282 : CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
6247 282 : if (psXMLNode)
6248 : {
6249 564 : GDALMultiDomainMetadata oLocalMDMD;
6250 282 : oLocalMDMD.XMLInit(psXMLNode, FALSE);
6251 :
6252 : papszMetadata =
6253 282 : CSLMerge(papszMetadata, oLocalMDMD.GetMetadata());
6254 282 : CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
6255 282 : CSLConstList papszIter = papszDomainList;
6256 597 : while (papszIter && *papszIter)
6257 : {
6258 315 : if (!EQUAL(*papszIter, ""))
6259 34 : oMDMD.SetMetadata(oLocalMDMD.GetMetadata(*papszIter),
6260 : *papszIter);
6261 315 : papszIter++;
6262 : }
6263 :
6264 282 : CPLDestroyXMLNode(psXMLNode);
6265 : }
6266 : }
6267 : }
6268 :
6269 333 : OGRLayer::SetMetadata(papszMetadata);
6270 333 : CSLDestroy(papszMetadata);
6271 333 : papszMetadata = nullptr;
6272 :
6273 : /* Add non-GDAL metadata now */
6274 333 : int nNonGDALMDILocal = 1;
6275 634 : for (int i = 0; i < oResult->RowCount(); i++)
6276 : {
6277 301 : const char *pszMetadata = oResult->GetValue(0, i);
6278 301 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6279 301 : const char *pszMimeType = oResult->GetValue(2, i);
6280 : // const char* pszReferenceScope = oResult->GetValue(3, i);
6281 301 : if (pszMetadata == nullptr || pszMDStandardURI == nullptr ||
6282 : pszMimeType == nullptr
6283 : /* || pszReferenceScope == nullptr */)
6284 : {
6285 : // should not happen as there are NOT NULL constraints
6286 : // But a database could lack such NOT NULL constraints or have
6287 : // large values that would cause a memory allocation failure.
6288 0 : continue;
6289 : }
6290 : // int bIsGPKGScope = EQUAL(pszReferenceScope, "geopackage");
6291 301 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6292 301 : EQUAL(pszMimeType, "text/xml"))
6293 301 : continue;
6294 :
6295 0 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6296 0 : EQUAL(pszMimeType, "text/plain"))
6297 : {
6298 0 : if (STARTS_WITH_CI(pszMetadata, "coordinate_epoch="))
6299 : {
6300 0 : continue;
6301 : }
6302 : }
6303 :
6304 : /*if( strcmp( pszMDStandardURI, "http://www.isotc211.org/2005/gmd" ) ==
6305 : 0 && strcmp( pszMimeType, "text/xml" ) == 0 )
6306 : {
6307 : char* apszMD[2];
6308 : apszMD[0] = (char*)pszMetadata;
6309 : apszMD[1] = NULL;
6310 : oMDMD.SetMetadata(apszMD, "xml:MD_Metadata");
6311 : }
6312 : else*/
6313 : {
6314 0 : oMDMD.SetMetadataItem(
6315 : CPLSPrintf("GPKG_METADATA_ITEM_%d", nNonGDALMDILocal),
6316 : pszMetadata);
6317 0 : nNonGDALMDILocal++;
6318 : }
6319 : }
6320 :
6321 333 : return OGRLayer::GetMetadata(pszDomain);
6322 : }
6323 :
6324 : /************************************************************************/
6325 : /* GetMetadataItem() */
6326 : /************************************************************************/
6327 :
6328 13166 : const char *OGRGeoPackageTableLayer::GetMetadataItem(const char *pszName,
6329 : const char *pszDomain)
6330 : {
6331 13166 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
6332 : }
6333 :
6334 : /************************************************************************/
6335 : /* GetMetadataDomainList() */
6336 : /************************************************************************/
6337 :
6338 359 : char **OGRGeoPackageTableLayer::GetMetadataDomainList()
6339 : {
6340 359 : GetMetadata();
6341 359 : return OGRLayer::GetMetadataDomainList();
6342 : }
6343 :
6344 : /************************************************************************/
6345 : /* SetMetadata() */
6346 : /************************************************************************/
6347 :
6348 167 : CPLErr OGRGeoPackageTableLayer::SetMetadata(CSLConstList papszMetadata,
6349 : const char *pszDomain)
6350 : {
6351 167 : GetMetadata(); /* force loading from storage if needed */
6352 167 : CPLErr eErr = OGRLayer::SetMetadata(papszMetadata, pszDomain);
6353 167 : m_poDS->SetMetadataDirty();
6354 167 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
6355 : {
6356 98 : if (!m_osIdentifierLCO.empty())
6357 1 : OGRLayer::SetMetadataItem("IDENTIFIER", m_osIdentifierLCO);
6358 98 : if (!m_osDescriptionLCO.empty())
6359 1 : OGRLayer::SetMetadataItem("DESCRIPTION", m_osDescriptionLCO);
6360 : }
6361 167 : return eErr;
6362 : }
6363 :
6364 : /************************************************************************/
6365 : /* SetMetadataItem() */
6366 : /************************************************************************/
6367 :
6368 682 : CPLErr OGRGeoPackageTableLayer::SetMetadataItem(const char *pszName,
6369 : const char *pszValue,
6370 : const char *pszDomain)
6371 : {
6372 682 : GetMetadata(); /* force loading from storage if needed */
6373 683 : if (!m_osIdentifierLCO.empty() && EQUAL(pszName, "IDENTIFIER") &&
6374 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6375 1 : return CE_None;
6376 682 : if (!m_osDescriptionLCO.empty() && EQUAL(pszName, "DESCRIPTION") &&
6377 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6378 1 : return CE_None;
6379 680 : m_poDS->SetMetadataDirty();
6380 680 : return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
6381 : }
6382 :
6383 : /************************************************************************/
6384 : /* RecreateTable() */
6385 : /************************************************************************/
6386 :
6387 : OGRErr
6388 12 : OGRGeoPackageTableLayer::RecreateTable(const CPLString &osColumnsForCreate,
6389 : const CPLString &osFieldListForSelect)
6390 : {
6391 : /* -------------------------------------------------------------------- */
6392 : /* Save existing related triggers and index */
6393 : /* -------------------------------------------------------------------- */
6394 12 : sqlite3 *hDB = m_poDS->GetDB();
6395 :
6396 12 : char *pszSQL = sqlite3_mprintf(
6397 : "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') "
6398 : "AND lower(tbl_name)=lower('%q') LIMIT 10000",
6399 : m_pszTableName);
6400 12 : OGRErr eErr = OGRERR_NONE;
6401 12 : auto oTriggers = SQLQuery(hDB, pszSQL);
6402 12 : sqlite3_free(pszSQL);
6403 :
6404 : /* -------------------------------------------------------------------- */
6405 : /* Make a temporary table with new content. */
6406 : /* -------------------------------------------------------------------- */
6407 12 : if (oTriggers)
6408 : {
6409 12 : pszSQL = sqlite3_mprintf("CREATE TABLE \"%w_ogr_tmp\" (%s)",
6410 : m_pszTableName, osColumnsForCreate.c_str());
6411 12 : eErr = SQLCommand(hDB, pszSQL);
6412 12 : sqlite3_free(pszSQL);
6413 : }
6414 : else
6415 : {
6416 0 : eErr = OGRERR_FAILURE;
6417 : }
6418 :
6419 12 : if (eErr == OGRERR_NONE)
6420 : {
6421 12 : pszSQL = sqlite3_mprintf(
6422 : "INSERT INTO \"%w_ogr_tmp\" SELECT %s FROM \"%w\"", m_pszTableName,
6423 : osFieldListForSelect.c_str(), m_pszTableName);
6424 12 : eErr = SQLCommand(hDB, pszSQL);
6425 12 : sqlite3_free(pszSQL);
6426 : }
6427 :
6428 : /* -------------------------------------------------------------------- */
6429 : /* Drop the original table */
6430 : /* -------------------------------------------------------------------- */
6431 12 : if (eErr == OGRERR_NONE)
6432 : {
6433 10 : pszSQL = sqlite3_mprintf("DROP TABLE \"%w\"", m_pszTableName);
6434 10 : eErr = SQLCommand(hDB, pszSQL);
6435 10 : sqlite3_free(pszSQL);
6436 : }
6437 :
6438 : /* -------------------------------------------------------------------- */
6439 : /* Rename temporary table as new table */
6440 : /* -------------------------------------------------------------------- */
6441 12 : if (eErr == OGRERR_NONE)
6442 : {
6443 10 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w_ogr_tmp\" RENAME TO \"%w\"",
6444 : m_pszTableName, m_pszTableName);
6445 10 : eErr = SQLCommand(hDB, pszSQL);
6446 10 : sqlite3_free(pszSQL);
6447 : }
6448 :
6449 : /* -------------------------------------------------------------------- */
6450 : /* Recreate existing related tables, triggers and index */
6451 : /* -------------------------------------------------------------------- */
6452 89 : for (int i = 0;
6453 89 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
6454 : {
6455 77 : const char *pszSQLTriggerIdx = oTriggers->GetValue(0, i);
6456 77 : if (pszSQLTriggerIdx != nullptr && *pszSQLTriggerIdx != '\0')
6457 : {
6458 76 : eErr = SQLCommand(hDB, pszSQLTriggerIdx);
6459 : }
6460 : }
6461 :
6462 24 : return eErr;
6463 : }
6464 :
6465 : /************************************************************************/
6466 : /* BuildSelectFieldList() */
6467 : /************************************************************************/
6468 :
6469 12 : CPLString OGRGeoPackageTableLayer::BuildSelectFieldList(
6470 : const std::vector<OGRFieldDefn *> &apoFields)
6471 : {
6472 12 : CPLString osFieldListForSelect;
6473 :
6474 12 : char *pszSQL = nullptr;
6475 12 : bool bNeedComma = false;
6476 :
6477 12 : if (m_pszFidColumn != nullptr)
6478 : {
6479 12 : pszSQL = sqlite3_mprintf("\"%w\"", m_pszFidColumn);
6480 12 : osFieldListForSelect += pszSQL;
6481 12 : sqlite3_free(pszSQL);
6482 12 : bNeedComma = true;
6483 : }
6484 :
6485 12 : if (GetGeomType() != wkbNone)
6486 : {
6487 12 : if (bNeedComma)
6488 : {
6489 12 : osFieldListForSelect += ", ";
6490 : }
6491 12 : bNeedComma = true;
6492 :
6493 12 : pszSQL = sqlite3_mprintf("\"%w\"", GetGeometryColumn());
6494 12 : osFieldListForSelect += pszSQL;
6495 12 : sqlite3_free(pszSQL);
6496 : }
6497 :
6498 33 : for (size_t iField = 0; iField < apoFields.size(); iField++)
6499 : {
6500 21 : if (bNeedComma)
6501 : {
6502 21 : osFieldListForSelect += ", ";
6503 : }
6504 21 : bNeedComma = true;
6505 :
6506 21 : OGRFieldDefn *poFieldDefn = apoFields[iField];
6507 21 : pszSQL = sqlite3_mprintf("\"%w\"", poFieldDefn->GetNameRef());
6508 21 : osFieldListForSelect += pszSQL;
6509 21 : sqlite3_free(pszSQL);
6510 : }
6511 :
6512 12 : return osFieldListForSelect;
6513 : }
6514 :
6515 : /************************************************************************/
6516 : /* DeleteField() */
6517 : /************************************************************************/
6518 :
6519 163 : OGRErr OGRGeoPackageTableLayer::DeleteField(int iFieldToDelete)
6520 : {
6521 163 : if (!m_bFeatureDefnCompleted)
6522 3 : GetLayerDefn();
6523 163 : if (!CheckUpdatableTable("DeleteField"))
6524 2 : return OGRERR_FAILURE;
6525 :
6526 321 : if (iFieldToDelete < 0 ||
6527 160 : iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
6528 : {
6529 2 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6530 2 : return OGRERR_FAILURE;
6531 : }
6532 :
6533 159 : ResetReading();
6534 159 : RunDeferredCreationIfNecessary();
6535 159 : if (!RunDeferredSpatialIndexUpdate())
6536 0 : return OGRERR_FAILURE;
6537 :
6538 : const char *pszFieldName =
6539 159 : m_poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
6540 :
6541 : /* -------------------------------------------------------------------- */
6542 : /* Drop any iterator since we change the DB structure */
6543 : /* -------------------------------------------------------------------- */
6544 159 : m_poDS->ResetReadingAllLayers();
6545 :
6546 : // Temporary remove foreign key checks
6547 : const GPKGTemporaryForeignKeyCheckDisabler
6548 318 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
6549 :
6550 174 : if (m_poDS->GetCurrentSavepoint().empty() &&
6551 15 : m_poDS->SoftStartTransaction() != OGRERR_NONE)
6552 : {
6553 0 : return OGRERR_FAILURE;
6554 : }
6555 :
6556 : // ALTER TABLE ... DROP COLUMN ... was first implemented in 3.35.0 but
6557 : // there was bug fixes related to it until 3.35.5
6558 : #if SQLITE_VERSION_NUMBER >= 3035005L
6559 159 : OGRErr eErr = SQLCommand(
6560 318 : m_poDS->GetDB(), CPLString()
6561 : .Printf("ALTER TABLE \"%s\" DROP COLUMN \"%s\"",
6562 318 : SQLEscapeName(m_pszTableName).c_str(),
6563 477 : SQLEscapeName(pszFieldName).c_str())
6564 : .c_str());
6565 : #else
6566 : /* -------------------------------------------------------------------- */
6567 : /* Recreate table in a transaction */
6568 : /* Build list of old fields, and the list of new fields. */
6569 : /* -------------------------------------------------------------------- */
6570 : std::vector<OGRFieldDefn *> apoFields;
6571 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6572 : {
6573 : if (iField == iFieldToDelete)
6574 : continue;
6575 :
6576 : OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6577 : apoFields.push_back(poFieldDefn);
6578 : }
6579 :
6580 : CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
6581 : CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6582 :
6583 : OGRErr eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
6584 : #endif
6585 :
6586 : /* -------------------------------------------------------------------- */
6587 : /* Update gpkg_extensions if needed. */
6588 : /* -------------------------------------------------------------------- */
6589 159 : if (eErr == OGRERR_NONE && m_poDS->HasExtensionsTable())
6590 : {
6591 154 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_extensions WHERE "
6592 : "lower(table_name) = lower('%q') AND "
6593 : "lower(column_name) = lower('%q')",
6594 : m_pszTableName, pszFieldName);
6595 154 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6596 154 : sqlite3_free(pszSQL);
6597 : }
6598 :
6599 : /* -------------------------------------------------------------------- */
6600 : /* Update gpkg_data_columns if needed. */
6601 : /* -------------------------------------------------------------------- */
6602 159 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6603 : {
6604 4 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_data_columns WHERE "
6605 : "lower(table_name) = lower('%q') AND "
6606 : "lower(column_name) = lower('%q')",
6607 : m_pszTableName, pszFieldName);
6608 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6609 4 : sqlite3_free(pszSQL);
6610 : }
6611 :
6612 : /* -------------------------------------------------------------------- */
6613 : /* Update gpkg_metadata_reference if needed. */
6614 : /* -------------------------------------------------------------------- */
6615 159 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6616 : {
6617 : {
6618 : // Delete from gpkg_metadata metadata records that are only
6619 : // referenced by the column we are about to drop
6620 3 : char *pszSQL = sqlite3_mprintf(
6621 : "DELETE FROM gpkg_metadata WHERE id IN ("
6622 : "SELECT DISTINCT md_file_id FROM "
6623 : "gpkg_metadata_reference WHERE "
6624 : "lower(table_name) = lower('%q') "
6625 : "AND lower(column_name) = lower('%q') "
6626 : "AND md_parent_id is NULL) "
6627 : "AND id NOT IN ("
6628 : "SELECT DISTINCT md_file_id FROM gpkg_metadata_reference WHERE "
6629 : "md_file_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 ("
6636 : "lower(table_name) <> lower('%q') "
6637 : "OR column_name IS NULL "
6638 : "OR lower(column_name) <> lower('%q')))",
6639 : m_pszTableName, pszFieldName, m_pszTableName, pszFieldName,
6640 : m_pszTableName, pszFieldName);
6641 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6642 3 : sqlite3_free(pszSQL);
6643 : }
6644 :
6645 3 : if (eErr == OGRERR_NONE)
6646 : {
6647 : char *pszSQL =
6648 3 : sqlite3_mprintf("DELETE FROM gpkg_metadata_reference WHERE "
6649 : "lower(table_name) = lower('%q') AND "
6650 : "lower(column_name) = lower('%q')",
6651 : m_pszTableName, pszFieldName);
6652 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6653 3 : sqlite3_free(pszSQL);
6654 : }
6655 : }
6656 :
6657 : /* -------------------------------------------------------------------- */
6658 : /* Check foreign key integrity if enforcement of foreign keys */
6659 : /* constraint is enabled. */
6660 : /* -------------------------------------------------------------------- */
6661 318 : if (eErr == OGRERR_NONE &&
6662 159 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
6663 : {
6664 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
6665 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
6666 : }
6667 :
6668 : /* -------------------------------------------------------------------- */
6669 : /* Finish */
6670 : /* -------------------------------------------------------------------- */
6671 159 : if (eErr == OGRERR_NONE)
6672 : {
6673 :
6674 159 : if (m_poDS->GetCurrentSavepoint().empty())
6675 15 : eErr = m_poDS->SoftCommitTransaction();
6676 :
6677 159 : if (eErr == OGRERR_NONE)
6678 : {
6679 :
6680 159 : if (m_poDS->IsInTransaction())
6681 : {
6682 153 : auto poFieldDefn{whileUnsealing(m_poFeatureDefn)
6683 306 : ->StealFieldDefn(iFieldToDelete)};
6684 153 : if (poFieldDefn)
6685 : {
6686 : m_apoFieldDefnChanges.emplace_back(
6687 153 : std::move(poFieldDefn), iFieldToDelete,
6688 153 : FieldChangeType::DELETE_FIELD,
6689 306 : m_poDS->GetCurrentSavepoint());
6690 : }
6691 : else
6692 : {
6693 0 : eErr = OGRERR_FAILURE;
6694 : }
6695 : }
6696 : else
6697 : {
6698 12 : eErr = whileUnsealing(m_poFeatureDefn)
6699 6 : ->DeleteFieldDefn(iFieldToDelete);
6700 : }
6701 159 : ResetReading();
6702 : }
6703 : }
6704 0 : else if (m_poDS->GetCurrentSavepoint().empty())
6705 : {
6706 0 : m_poDS->SoftRollbackTransaction();
6707 : }
6708 :
6709 159 : return eErr;
6710 : }
6711 :
6712 : /************************************************************************/
6713 : /* RenameFieldInAuxiliaryTables() */
6714 : /************************************************************************/
6715 :
6716 : OGRErr
6717 17 : OGRGeoPackageTableLayer::RenameFieldInAuxiliaryTables(const char *pszOldName,
6718 : const char *pszNewName)
6719 : {
6720 17 : OGRErr eErr = OGRERR_NONE;
6721 17 : sqlite3 *hDB = m_poDS->GetDB();
6722 :
6723 : /* -------------------------------------------------------------------- */
6724 : /* Update gpkg_extensions if needed. */
6725 : /* -------------------------------------------------------------------- */
6726 17 : if (/* eErr == OGRERR_NONE && */ m_poDS->HasExtensionsTable())
6727 : {
6728 15 : char *pszSQL = sqlite3_mprintf(
6729 : "UPDATE gpkg_extensions SET column_name = '%q' WHERE "
6730 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6731 : "lower('%q')",
6732 : pszNewName, m_pszTableName, pszOldName);
6733 15 : eErr = SQLCommand(hDB, pszSQL);
6734 15 : sqlite3_free(pszSQL);
6735 : }
6736 :
6737 : /* -------------------------------------------------------------------- */
6738 : /* Update gpkg_data_columns if needed. */
6739 : /* -------------------------------------------------------------------- */
6740 17 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6741 : {
6742 6 : char *pszSQL = sqlite3_mprintf(
6743 : "UPDATE gpkg_data_columns SET column_name = '%q' WHERE "
6744 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6745 : "lower('%q')",
6746 : pszNewName, m_pszTableName, pszOldName);
6747 6 : eErr = SQLCommand(hDB, pszSQL);
6748 6 : sqlite3_free(pszSQL);
6749 : }
6750 :
6751 : /* -------------------------------------------------------------------- */
6752 : /* Update gpkg_metadata_reference if needed. */
6753 : /* -------------------------------------------------------------------- */
6754 17 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6755 : {
6756 7 : char *pszSQL = sqlite3_mprintf(
6757 : "UPDATE gpkg_metadata_reference SET column_name = '%q' WHERE "
6758 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6759 : "lower('%q')",
6760 : pszNewName, m_pszTableName, pszOldName);
6761 7 : eErr = SQLCommand(hDB, pszSQL);
6762 7 : sqlite3_free(pszSQL);
6763 : }
6764 :
6765 17 : return eErr;
6766 : }
6767 :
6768 : /************************************************************************/
6769 : /* AlterFieldDefn() */
6770 : /************************************************************************/
6771 :
6772 34 : OGRErr OGRGeoPackageTableLayer::AlterFieldDefn(int iFieldToAlter,
6773 : OGRFieldDefn *poNewFieldDefn,
6774 : int nFlagsIn)
6775 : {
6776 34 : if (!m_bFeatureDefnCompleted)
6777 1 : GetLayerDefn();
6778 34 : if (!CheckUpdatableTable("AlterFieldDefn"))
6779 2 : return OGRERR_FAILURE;
6780 :
6781 32 : if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
6782 : {
6783 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6784 1 : return OGRERR_FAILURE;
6785 : }
6786 :
6787 : /* -------------------------------------------------------------------- */
6788 : /* Deferred actions, reset state. */
6789 : /* -------------------------------------------------------------------- */
6790 31 : ResetReading();
6791 31 : RunDeferredCreationIfNecessary();
6792 31 : if (m_bThreadRTreeStarted)
6793 0 : CancelAsyncRTree();
6794 31 : if (!RunDeferredSpatialIndexUpdate())
6795 0 : return OGRERR_FAILURE;
6796 :
6797 : /* -------------------------------------------------------------------- */
6798 : /* Check that the new column name is not a duplicate. */
6799 : /* -------------------------------------------------------------------- */
6800 :
6801 : OGRFieldDefn *poFieldDefnToAlter =
6802 31 : m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
6803 62 : const CPLString osOldColName(poFieldDefnToAlter->GetNameRef());
6804 31 : const CPLString osNewColName((nFlagsIn & ALTER_NAME_FLAG)
6805 : ? CPLString(poNewFieldDefn->GetNameRef())
6806 62 : : osOldColName);
6807 :
6808 : const bool bRenameCol =
6809 60 : (nFlagsIn & ALTER_NAME_FLAG) &&
6810 29 : strcmp(poNewFieldDefn->GetNameRef(), osOldColName) != 0;
6811 31 : if (bRenameCol)
6812 : {
6813 54 : if ((m_pszFidColumn &&
6814 18 : strcmp(poNewFieldDefn->GetNameRef(), m_pszFidColumn) == 0) ||
6815 17 : (GetGeomType() != wkbNone &&
6816 17 : strcmp(poNewFieldDefn->GetNameRef(),
6817 53 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0) ||
6818 16 : m_poFeatureDefn->GetFieldIndex(poNewFieldDefn->GetNameRef()) >= 0)
6819 : {
6820 4 : CPLError(CE_Failure, CPLE_AppDefined,
6821 : "Field name %s is already used for another field",
6822 : poNewFieldDefn->GetNameRef());
6823 4 : return OGRERR_FAILURE;
6824 : }
6825 : }
6826 :
6827 : /* -------------------------------------------------------------------- */
6828 : /* Build the modified field definition from the flags. */
6829 : /* -------------------------------------------------------------------- */
6830 54 : OGRFieldDefn oTmpFieldDefn(poFieldDefnToAlter);
6831 27 : bool bUseRewriteSchemaMethod = (m_poDS->m_nSoftTransactionLevel == 0);
6832 27 : int nActualFlags = 0;
6833 27 : if (bRenameCol)
6834 : {
6835 14 : nActualFlags |= ALTER_NAME_FLAG;
6836 14 : oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef());
6837 : }
6838 50 : if ((nFlagsIn & ALTER_TYPE_FLAG) != 0 &&
6839 23 : (poFieldDefnToAlter->GetType() != poNewFieldDefn->GetType() ||
6840 17 : poFieldDefnToAlter->GetSubType() != poNewFieldDefn->GetSubType()))
6841 : {
6842 8 : nActualFlags |= ALTER_TYPE_FLAG;
6843 8 : oTmpFieldDefn.SetSubType(OFSTNone);
6844 8 : oTmpFieldDefn.SetType(poNewFieldDefn->GetType());
6845 8 : oTmpFieldDefn.SetSubType(poNewFieldDefn->GetSubType());
6846 : }
6847 71 : if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) != 0 &&
6848 44 : (poFieldDefnToAlter->GetWidth() != poNewFieldDefn->GetWidth() ||
6849 21 : poFieldDefnToAlter->GetPrecision() != poNewFieldDefn->GetPrecision()))
6850 : {
6851 2 : nActualFlags |= ALTER_WIDTH_PRECISION_FLAG;
6852 2 : oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth());
6853 2 : oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision());
6854 : }
6855 50 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) != 0 &&
6856 23 : poFieldDefnToAlter->IsNullable() != poNewFieldDefn->IsNullable())
6857 : {
6858 7 : nActualFlags |= ALTER_NULLABLE_FLAG;
6859 7 : if (!poNewFieldDefn->IsNullable())
6860 5 : bUseRewriteSchemaMethod = false;
6861 7 : oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable());
6862 : }
6863 50 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) != 0 &&
6864 23 : !((poFieldDefnToAlter->GetDefault() == nullptr &&
6865 20 : poNewFieldDefn->GetDefault() == nullptr) ||
6866 4 : (poFieldDefnToAlter->GetDefault() != nullptr &&
6867 3 : poNewFieldDefn->GetDefault() != nullptr &&
6868 2 : strcmp(poFieldDefnToAlter->GetDefault(),
6869 : poNewFieldDefn->GetDefault()) == 0)))
6870 : {
6871 2 : nActualFlags |= ALTER_DEFAULT_FLAG;
6872 2 : oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault());
6873 : }
6874 50 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) != 0 &&
6875 23 : poFieldDefnToAlter->IsUnique() != poNewFieldDefn->IsUnique())
6876 : {
6877 2 : nActualFlags |= ALTER_UNIQUE_FLAG;
6878 2 : if (poNewFieldDefn->IsUnique())
6879 1 : bUseRewriteSchemaMethod = false;
6880 2 : oTmpFieldDefn.SetUnique(poNewFieldDefn->IsUnique());
6881 : }
6882 50 : if ((nFlagsIn & ALTER_DOMAIN_FLAG) != 0 &&
6883 23 : poFieldDefnToAlter->GetDomainName() != poNewFieldDefn->GetDomainName())
6884 : {
6885 3 : nActualFlags |= ALTER_DOMAIN_FLAG;
6886 3 : oTmpFieldDefn.SetDomainName(poNewFieldDefn->GetDomainName());
6887 : }
6888 51 : if ((nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG) != 0 &&
6889 24 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
6890 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
6891 : {
6892 5 : nActualFlags |= ALTER_ALTERNATIVE_NAME_FLAG;
6893 5 : oTmpFieldDefn.SetAlternativeName(
6894 : poNewFieldDefn->GetAlternativeNameRef());
6895 : }
6896 51 : if ((nFlagsIn & ALTER_COMMENT_FLAG) != 0 &&
6897 24 : poFieldDefnToAlter->GetComment() != poNewFieldDefn->GetComment())
6898 : {
6899 3 : nActualFlags |= ALTER_COMMENT_FLAG;
6900 3 : oTmpFieldDefn.SetComment(poNewFieldDefn->GetComment());
6901 : }
6902 :
6903 : /* -------------------------------------------------------------------- */
6904 : /* Build list of old fields, and the list of new fields. */
6905 : /* -------------------------------------------------------------------- */
6906 54 : std::vector<OGRFieldDefn *> apoFields;
6907 54 : std::vector<OGRFieldDefn *> apoFieldsOld;
6908 90 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6909 : {
6910 : OGRFieldDefn *poFieldDefn;
6911 63 : if (iField == iFieldToAlter)
6912 : {
6913 27 : poFieldDefn = &oTmpFieldDefn;
6914 : }
6915 : else
6916 : {
6917 36 : poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6918 : }
6919 63 : apoFields.push_back(poFieldDefn);
6920 63 : apoFieldsOld.push_back(m_poFeatureDefn->GetFieldDefn(iField));
6921 : }
6922 :
6923 54 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6924 :
6925 : /* -------------------------------------------------------------------- */
6926 : /* Drop any iterator since we change the DB structure */
6927 : /* -------------------------------------------------------------------- */
6928 27 : m_poDS->ResetReadingAllLayers();
6929 :
6930 : #if SQLITE_VERSION_NUMBER >= 3053000L
6931 : const bool bUseRenameColumn =
6932 : (nActualFlags == ALTER_NAME_FLAG ||
6933 : nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG));
6934 : #else
6935 27 : const bool bUseRenameColumn = (nActualFlags == ALTER_NAME_FLAG);
6936 : #endif
6937 27 : if (bUseRenameColumn)
6938 4 : bUseRewriteSchemaMethod = false;
6939 :
6940 27 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
6941 0 : return OGRERR_FAILURE;
6942 :
6943 27 : sqlite3 *hDB = m_poDS->GetDB();
6944 27 : OGRErr eErr = OGRERR_NONE;
6945 :
6946 : /* -------------------------------------------------------------------- */
6947 : /* Drop triggers and index that look like to be related to the */
6948 : /* column if renaming. We re-install some indexes afterwards. */
6949 : /* -------------------------------------------------------------------- */
6950 0 : std::unique_ptr<SQLResult> oTriggers;
6951 : // cppcheck-suppress knownConditionTrueFalse
6952 27 : if (bRenameCol && !bUseRenameColumn)
6953 : {
6954 10 : char *pszSQL = sqlite3_mprintf(
6955 : "SELECT name, type, sql FROM sqlite_master WHERE "
6956 : "type IN ('trigger','index') "
6957 : "AND lower(tbl_name)=lower('%q') AND sql LIKE '%%%q%%' LIMIT 10000",
6958 20 : m_pszTableName, SQLEscapeName(osOldColName).c_str());
6959 10 : oTriggers = SQLQuery(hDB, pszSQL);
6960 10 : sqlite3_free(pszSQL);
6961 :
6962 10 : if (!oTriggers)
6963 : {
6964 0 : eErr = OGRERR_FAILURE;
6965 : }
6966 :
6967 12 : for (int i = 0; oTriggers && i < oTriggers->RowCount(); i++)
6968 : {
6969 : pszSQL =
6970 2 : sqlite3_mprintf("DROP %s \"%w\"", oTriggers->GetValue(1, i),
6971 : oTriggers->GetValue(0, i));
6972 2 : eErr = SQLCommand(hDB, pszSQL);
6973 2 : sqlite3_free(pszSQL);
6974 : }
6975 : }
6976 :
6977 27 : bool bUseRecreateTableMethod =
6978 27 : !bUseRenameColumn && !bUseRewriteSchemaMethod;
6979 :
6980 : #if SQLITE_VERSION_NUMBER >= 3053000L
6981 : if (eErr == OGRERR_NONE &&
6982 : (nActualFlags == ALTER_NULLABLE_FLAG ||
6983 : nActualFlags == (ALTER_NAME_FLAG | ALTER_NULLABLE_FLAG)))
6984 : {
6985 : eErr =
6986 : SQLCommand(m_poDS->GetDB(),
6987 : CPLString().Printf(
6988 : "ALTER TABLE \"%s\" ALTER COLUMN \"%s\" %s NOT NULL",
6989 : SQLEscapeName(m_pszTableName).c_str(),
6990 : SQLEscapeName(osOldColName).c_str(),
6991 : poNewFieldDefn->IsNullable() ? "DROP" : "SET"));
6992 : bUseRecreateTableMethod = false;
6993 : bUseRewriteSchemaMethod = false;
6994 : }
6995 : #endif
6996 :
6997 27 : if (bUseRenameColumn)
6998 : {
6999 4 : if (eErr == OGRERR_NONE)
7000 : {
7001 4 : CPLDebug("GPKG", "Running ALTER TABLE RENAME COLUMN");
7002 4 : eErr = SQLCommand(
7003 4 : m_poDS->GetDB(),
7004 8 : CPLString()
7005 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
7006 8 : SQLEscapeName(m_pszTableName).c_str(),
7007 8 : SQLEscapeName(osOldColName).c_str(),
7008 16 : SQLEscapeName(osNewColName).c_str())
7009 : .c_str());
7010 : }
7011 : }
7012 23 : else if (bUseRecreateTableMethod)
7013 : {
7014 : /* --------------------------------------------------------------------
7015 : */
7016 : /* If we are within a transaction, we cannot use the method */
7017 : /* that consists in altering the database in a raw way. */
7018 : /* --------------------------------------------------------------------
7019 : */
7020 : const CPLString osFieldListForSelect(
7021 22 : BuildSelectFieldList(apoFieldsOld));
7022 :
7023 11 : if (eErr == OGRERR_NONE)
7024 : {
7025 11 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
7026 : }
7027 : }
7028 12 : else if (bUseRewriteSchemaMethod)
7029 : {
7030 : /* --------------------------------------------------------------------
7031 : */
7032 : /* Rewrite schema in a transaction by altering the database */
7033 : /* schema in a rather raw way, as described at bottom of */
7034 : /* https://www.sqlite.org/lang_altertable.html */
7035 : /* --------------------------------------------------------------------
7036 : */
7037 :
7038 : /* --------------------------------------------------------------------
7039 : */
7040 : /* Collect schema version number. */
7041 : /* --------------------------------------------------------------------
7042 : */
7043 12 : int nSchemaVersion = SQLGetInteger(hDB, "PRAGMA schema_version", &eErr);
7044 :
7045 : /* --------------------------------------------------------------------
7046 : */
7047 : /* Turn on writable schema. */
7048 : /* --------------------------------------------------------------------
7049 : */
7050 12 : if (eErr == OGRERR_NONE)
7051 : {
7052 12 : eErr = m_poDS->PragmaCheck("writable_schema=ON", "", 0);
7053 : }
7054 :
7055 : /* --------------------------------------------------------------------
7056 : */
7057 : /* Rewrite CREATE TABLE statement. */
7058 : /* --------------------------------------------------------------------
7059 : */
7060 12 : if (eErr == OGRERR_NONE)
7061 : {
7062 : char *psSQLCreateTable =
7063 12 : sqlite3_mprintf("CREATE TABLE \"%w\" (%s)", m_pszTableName,
7064 : osColumnsForCreate.c_str());
7065 12 : char *pszSQL = sqlite3_mprintf("UPDATE sqlite_master SET sql='%q' "
7066 : "WHERE type='table' AND name='%q'",
7067 : psSQLCreateTable, m_pszTableName);
7068 12 : eErr = SQLCommand(hDB, pszSQL);
7069 12 : sqlite3_free(psSQLCreateTable);
7070 12 : sqlite3_free(pszSQL);
7071 : }
7072 :
7073 : /* --------------------------------------------------------------------
7074 : */
7075 : /* Increment schema number. */
7076 : /* --------------------------------------------------------------------
7077 : */
7078 12 : if (eErr == OGRERR_NONE)
7079 : {
7080 12 : char *pszSQL = sqlite3_mprintf("PRAGMA schema_version = %d",
7081 : nSchemaVersion + 1);
7082 12 : eErr = SQLCommand(hDB, pszSQL);
7083 12 : sqlite3_free(pszSQL);
7084 : }
7085 :
7086 : /* --------------------------------------------------------------------
7087 : */
7088 : /* Turn off writable schema. */
7089 : /* --------------------------------------------------------------------
7090 : */
7091 12 : if (eErr == OGRERR_NONE)
7092 : {
7093 12 : eErr = m_poDS->PragmaCheck("writable_schema=OFF", "", 0);
7094 : }
7095 : }
7096 :
7097 : /* -------------------------------------------------------------------- */
7098 : /* Update auxiliary tables */
7099 : /* -------------------------------------------------------------------- */
7100 27 : if (bRenameCol && eErr == OGRERR_NONE)
7101 : {
7102 13 : eErr = RenameFieldInAuxiliaryTables(osOldColName.c_str(),
7103 : poNewFieldDefn->GetNameRef());
7104 : }
7105 :
7106 : /* -------------------------------------------------------------------- */
7107 : /* Update gpkgext_relations if needed. */
7108 : /* -------------------------------------------------------------------- */
7109 27 : if (bRenameCol && eErr == OGRERR_NONE && m_poDS->HasGpkgextRelationsTable())
7110 : {
7111 2 : char *pszSQL = sqlite3_mprintf(
7112 : "UPDATE gpkgext_relations SET base_primary_column = '%q' WHERE "
7113 : "lower(base_table_name) = lower('%q') AND "
7114 : "lower(base_primary_column) = lower('%q')",
7115 : poNewFieldDefn->GetNameRef(), m_pszTableName, osOldColName.c_str());
7116 2 : eErr = SQLCommand(hDB, pszSQL);
7117 2 : sqlite3_free(pszSQL);
7118 :
7119 2 : if (eErr == OGRERR_NONE)
7120 : {
7121 : pszSQL =
7122 2 : sqlite3_mprintf("UPDATE gpkgext_relations SET "
7123 : "related_primary_column = '%q' WHERE "
7124 : "lower(related_table_name) = lower('%q') AND "
7125 : "lower(related_primary_column) = lower('%q')",
7126 : poNewFieldDefn->GetNameRef(), m_pszTableName,
7127 : osOldColName.c_str());
7128 2 : eErr = SQLCommand(hDB, pszSQL);
7129 2 : sqlite3_free(pszSQL);
7130 : }
7131 2 : m_poDS->ClearCachedRelationships();
7132 : }
7133 :
7134 : /* -------------------------------------------------------------------- */
7135 : /* Run integrity check only if explicitly required. */
7136 : /* -------------------------------------------------------------------- */
7137 52 : if (eErr == OGRERR_NONE &&
7138 25 : CPLTestBool(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")))
7139 : {
7140 0 : CPLDebug("GPKG", "Running PRAGMA integrity_check");
7141 0 : eErr = m_poDS->PragmaCheck("integrity_check", "ok", 1);
7142 : }
7143 :
7144 : /* -------------------------------------------------------------------- */
7145 : /* Otherwise check foreign key integrity if enforcement of foreign */
7146 : /* kets constraint is enabled. */
7147 : /* -------------------------------------------------------------------- */
7148 52 : else if (eErr == OGRERR_NONE &&
7149 25 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
7150 : {
7151 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
7152 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
7153 : }
7154 :
7155 : /* -------------------------------------------------------------------- */
7156 : /* Finish */
7157 : /* -------------------------------------------------------------------- */
7158 27 : if (eErr == OGRERR_NONE)
7159 : {
7160 :
7161 25 : eErr = m_poDS->SoftCommitTransaction();
7162 :
7163 : // We need to force database reopening due to schema change
7164 37 : if (eErr == OGRERR_NONE && bUseRewriteSchemaMethod &&
7165 12 : !m_poDS->ReOpenDB())
7166 : {
7167 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen database");
7168 0 : eErr = OGRERR_FAILURE;
7169 : }
7170 25 : hDB = m_poDS->GetDB();
7171 :
7172 : /* --------------------------------------------------------------------
7173 : */
7174 : /* Recreate indices. */
7175 : /* --------------------------------------------------------------------
7176 : */
7177 27 : for (int i = 0;
7178 27 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
7179 : {
7180 2 : if (EQUAL(oTriggers->GetValue(1, i), "index"))
7181 : {
7182 4 : CPLString osSQL(oTriggers->GetValue(2, i));
7183 : // CREATE INDEX idx_name ON table_name(column_name)
7184 2 : char **papszTokens = SQLTokenize(osSQL);
7185 2 : if (CSLCount(papszTokens) == 8 &&
7186 2 : EQUAL(papszTokens[0], "CREATE") &&
7187 2 : EQUAL(papszTokens[1], "INDEX") &&
7188 6 : EQUAL(papszTokens[3], "ON") && EQUAL(papszTokens[5], "(") &&
7189 2 : EQUAL(papszTokens[7], ")"))
7190 : {
7191 2 : osSQL = "CREATE INDEX ";
7192 2 : osSQL += papszTokens[2];
7193 2 : osSQL += " ON ";
7194 2 : osSQL += papszTokens[4];
7195 2 : osSQL += "(\"";
7196 2 : osSQL += SQLEscapeName(osNewColName);
7197 2 : osSQL += "\")";
7198 2 : eErr = SQLCommand(hDB, osSQL);
7199 : }
7200 2 : CSLDestroy(papszTokens);
7201 : }
7202 : }
7203 :
7204 25 : if (eErr == OGRERR_NONE)
7205 : {
7206 :
7207 25 : if (m_poDS->IsInTransaction())
7208 : {
7209 : m_apoFieldDefnChanges.emplace_back(
7210 6 : std::make_unique<OGRFieldDefn>(poFieldDefnToAlter),
7211 6 : iFieldToAlter, FieldChangeType::ALTER_FIELD,
7212 18 : m_poDS->GetCurrentSavepoint());
7213 : }
7214 :
7215 50 : auto oTemporaryUnsealer(poFieldDefnToAlter->GetTemporaryUnsealer());
7216 25 : bool bNeedsEntryInGpkgDataColumns = false;
7217 :
7218 : // field type
7219 25 : if (nActualFlags & ALTER_TYPE_FLAG)
7220 : {
7221 8 : poFieldDefnToAlter->SetSubType(OFSTNone);
7222 8 : poFieldDefnToAlter->SetType(poNewFieldDefn->GetType());
7223 8 : poFieldDefnToAlter->SetSubType(poNewFieldDefn->GetSubType());
7224 : }
7225 37 : if (poFieldDefnToAlter->GetType() == OFTString &&
7226 12 : poFieldDefnToAlter->GetSubType() == OFSTJSON)
7227 : {
7228 1 : bNeedsEntryInGpkgDataColumns = true;
7229 : }
7230 :
7231 : // name
7232 25 : if (nActualFlags & ALTER_NAME_FLAG)
7233 : {
7234 13 : poFieldDefnToAlter->SetName(poNewFieldDefn->GetNameRef());
7235 : }
7236 :
7237 : // width/precision
7238 25 : if (nActualFlags & ALTER_WIDTH_PRECISION_FLAG)
7239 : {
7240 2 : poFieldDefnToAlter->SetWidth(poNewFieldDefn->GetWidth());
7241 2 : poFieldDefnToAlter->SetPrecision(
7242 : poNewFieldDefn->GetPrecision());
7243 : }
7244 :
7245 : // constraints
7246 25 : if (nActualFlags & ALTER_NULLABLE_FLAG)
7247 5 : poFieldDefnToAlter->SetNullable(poNewFieldDefn->IsNullable());
7248 25 : if (nActualFlags & ALTER_DEFAULT_FLAG)
7249 2 : poFieldDefnToAlter->SetDefault(poNewFieldDefn->GetDefault());
7250 25 : if (nActualFlags & ALTER_UNIQUE_FLAG)
7251 2 : poFieldDefnToAlter->SetUnique(poNewFieldDefn->IsUnique());
7252 :
7253 : // domain
7254 28 : if ((nActualFlags & ALTER_DOMAIN_FLAG) &&
7255 3 : poFieldDefnToAlter->GetDomainName() !=
7256 3 : poNewFieldDefn->GetDomainName())
7257 : {
7258 3 : poFieldDefnToAlter->SetDomainName(
7259 : poNewFieldDefn->GetDomainName());
7260 : }
7261 25 : if (!poFieldDefnToAlter->GetDomainName().empty())
7262 : {
7263 3 : bNeedsEntryInGpkgDataColumns = true;
7264 : }
7265 :
7266 : // alternative name
7267 30 : if ((nActualFlags & ALTER_ALTERNATIVE_NAME_FLAG) &&
7268 5 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
7269 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
7270 : {
7271 5 : poFieldDefnToAlter->SetAlternativeName(
7272 : poNewFieldDefn->GetAlternativeNameRef());
7273 : }
7274 50 : if (!std::string(poFieldDefnToAlter->GetAlternativeNameRef())
7275 25 : .empty())
7276 : {
7277 7 : bNeedsEntryInGpkgDataColumns = true;
7278 : }
7279 :
7280 : // comment
7281 28 : if ((nActualFlags & ALTER_COMMENT_FLAG) &&
7282 3 : poFieldDefnToAlter->GetComment() !=
7283 3 : poNewFieldDefn->GetComment())
7284 : {
7285 3 : poFieldDefnToAlter->SetComment(poNewFieldDefn->GetComment());
7286 : }
7287 25 : if (!poFieldDefnToAlter->GetComment().empty())
7288 : {
7289 3 : bNeedsEntryInGpkgDataColumns = true;
7290 : }
7291 :
7292 25 : if (m_poDS->HasDataColumnsTable())
7293 : {
7294 14 : char *pszSQL = sqlite3_mprintf(
7295 : "DELETE FROM gpkg_data_columns WHERE "
7296 : "lower(table_name) = lower('%q') AND "
7297 : "lower(column_name) = lower('%q')",
7298 : m_pszTableName, poFieldDefnToAlter->GetNameRef());
7299 14 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7300 14 : sqlite3_free(pszSQL);
7301 : }
7302 :
7303 25 : if (bNeedsEntryInGpkgDataColumns)
7304 : {
7305 12 : if (!DoSpecialProcessingForColumnCreation(poFieldDefnToAlter))
7306 0 : eErr = OGRERR_FAILURE;
7307 : }
7308 :
7309 25 : ResetReading();
7310 : }
7311 : }
7312 : else
7313 : {
7314 2 : m_poDS->SoftRollbackTransaction();
7315 : }
7316 :
7317 27 : return eErr;
7318 : }
7319 :
7320 : /************************************************************************/
7321 : /* AlterGeomFieldDefn() */
7322 : /************************************************************************/
7323 :
7324 8 : OGRErr OGRGeoPackageTableLayer::AlterGeomFieldDefn(
7325 : int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
7326 : int nFlagsIn)
7327 : {
7328 8 : if (!m_bFeatureDefnCompleted)
7329 1 : GetLayerDefn();
7330 8 : if (!CheckUpdatableTable("AlterGeomFieldDefn"))
7331 0 : return OGRERR_FAILURE;
7332 :
7333 16 : if (iGeomFieldToAlter < 0 ||
7334 8 : iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
7335 : {
7336 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
7337 0 : return OGRERR_FAILURE;
7338 : }
7339 :
7340 : /* -------------------------------------------------------------------- */
7341 : /* Deferred actions, reset state. */
7342 : /* -------------------------------------------------------------------- */
7343 8 : ResetReading();
7344 8 : RunDeferredCreationIfNecessary();
7345 8 : if (m_bThreadRTreeStarted)
7346 0 : CancelAsyncRTree();
7347 8 : if (!RunDeferredSpatialIndexUpdate())
7348 0 : return OGRERR_FAILURE;
7349 8 : RevertWorkaroundUpdate1TriggerIssue();
7350 :
7351 : /* -------------------------------------------------------------------- */
7352 : /* Drop any iterator since we change the DB structure */
7353 : /* -------------------------------------------------------------------- */
7354 8 : m_poDS->ResetReadingAllLayers();
7355 :
7356 8 : auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
7357 16 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
7358 :
7359 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
7360 : {
7361 : // could be potentially done. Requires rewriting the CREATE TABLE
7362 : // statement
7363 0 : if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
7364 : {
7365 0 : CPLError(CE_Failure, CPLE_NotSupported,
7366 : "Altering the geometry field type is not currently "
7367 : "supported for "
7368 : "GeoPackage");
7369 0 : return OGRERR_FAILURE;
7370 : }
7371 : }
7372 :
7373 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
7374 : {
7375 : // could be potentially done. Requires rewriting the CREATE TABLE
7376 : // statement
7377 0 : if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
7378 : {
7379 0 : CPLError(CE_Failure, CPLE_NotSupported,
7380 : "Altering the nullable state of the geometry field "
7381 : "is not currently supported for GeoPackage");
7382 0 : return OGRERR_FAILURE;
7383 : }
7384 : }
7385 :
7386 12 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
7387 4 : strcmp(poGeomFieldDefn->GetNameRef(),
7388 : poNewGeomFieldDefn->GetNameRef()) != 0)
7389 : {
7390 4 : const bool bHasSpatialIndex = HasSpatialIndex();
7391 :
7392 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7393 0 : return OGRERR_FAILURE;
7394 :
7395 : // Rename geometry field
7396 4 : auto eErr = SQLCommand(
7397 4 : m_poDS->GetDB(),
7398 4 : CPLString()
7399 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
7400 8 : SQLEscapeName(m_pszTableName).c_str(),
7401 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7402 16 : SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str())
7403 : .c_str());
7404 4 : if (eErr != OGRERR_NONE)
7405 : {
7406 0 : m_poDS->SoftRollbackTransaction();
7407 0 : return OGRERR_FAILURE;
7408 : }
7409 :
7410 : // Update gpkg_geometry_columns
7411 4 : eErr = SQLCommand(
7412 4 : m_poDS->GetDB(),
7413 4 : CPLString()
7414 : .Printf(
7415 : "UPDATE gpkg_geometry_columns SET column_name = '%s' "
7416 : "WHERE lower(table_name) = lower('%s') "
7417 : "AND lower(column_name) = lower('%s')",
7418 8 : SQLEscapeLiteral(poNewGeomFieldDefn->GetNameRef()).c_str(),
7419 8 : SQLEscapeLiteral(m_pszTableName).c_str(),
7420 16 : SQLEscapeLiteral(poGeomFieldDefn->GetNameRef()).c_str())
7421 : .c_str());
7422 4 : if (eErr != OGRERR_NONE)
7423 : {
7424 0 : m_poDS->SoftRollbackTransaction();
7425 0 : return OGRERR_FAILURE;
7426 : }
7427 :
7428 : // Update auxiliary tables
7429 4 : eErr = RenameFieldInAuxiliaryTables(poGeomFieldDefn->GetNameRef(),
7430 : poNewGeomFieldDefn->GetNameRef());
7431 4 : if (eErr != OGRERR_NONE)
7432 : {
7433 0 : m_poDS->SoftRollbackTransaction();
7434 0 : return OGRERR_FAILURE;
7435 : }
7436 :
7437 4 : std::string osNewRTreeName;
7438 4 : if (bHasSpatialIndex)
7439 : {
7440 4 : osNewRTreeName = "rtree_";
7441 4 : osNewRTreeName += m_pszTableName;
7442 4 : osNewRTreeName += "_";
7443 4 : osNewRTreeName += poNewGeomFieldDefn->GetNameRef();
7444 :
7445 : // Rename spatial index tables (not strictly needed, but for
7446 : // consistency)
7447 : eErr =
7448 4 : SQLCommand(m_poDS->GetDB(),
7449 4 : CPLString().Printf(
7450 : "ALTER TABLE \"%s\" RENAME TO \"%s\"",
7451 8 : SQLEscapeName(m_osRTreeName.c_str()).c_str(),
7452 12 : SQLEscapeName(osNewRTreeName.c_str()).c_str()));
7453 4 : if (eErr != OGRERR_NONE)
7454 : {
7455 0 : m_poDS->SoftRollbackTransaction();
7456 0 : return OGRERR_FAILURE;
7457 : }
7458 :
7459 : // Finally rename triggers (not strictly needed, but for
7460 : // consistency)
7461 4 : std::string osTriggerSQL;
7462 4 : osTriggerSQL = ReturnSQLDropSpatialIndexTriggers();
7463 4 : osTriggerSQL += ";";
7464 8 : osTriggerSQL += ReturnSQLCreateSpatialIndexTriggers(
7465 4 : nullptr, poNewGeomFieldDefn->GetNameRef());
7466 4 : eErr = SQLCommand(m_poDS->GetDB(), osTriggerSQL.c_str());
7467 4 : if (eErr != OGRERR_NONE)
7468 : {
7469 0 : m_poDS->SoftRollbackTransaction();
7470 0 : return OGRERR_FAILURE;
7471 : }
7472 : }
7473 :
7474 4 : eErr = m_poDS->SoftCommitTransaction();
7475 4 : if (eErr != OGRERR_NONE)
7476 : {
7477 0 : return OGRERR_FAILURE;
7478 : }
7479 :
7480 4 : poGeomFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
7481 :
7482 4 : if (bHasSpatialIndex)
7483 : {
7484 4 : m_osRTreeName = osNewRTreeName;
7485 : }
7486 : }
7487 :
7488 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0 ||
7489 5 : (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7490 : {
7491 4 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
7492 4 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
7493 :
7494 4 : OGRSpatialReferenceRefCountedPtr poNewSRS;
7495 4 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
7496 : {
7497 3 : if (poNewSRSRef != nullptr)
7498 : {
7499 : poNewSRS =
7500 2 : OGRSpatialReferenceRefCountedPtr::makeClone(poNewSRSRef);
7501 2 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) ==
7502 : 0)
7503 : {
7504 2 : if (poOldSRS)
7505 1 : poNewSRS->SetCoordinateEpoch(
7506 : poOldSRS->GetCoordinateEpoch());
7507 : }
7508 : }
7509 : }
7510 1 : else if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7511 : {
7512 1 : if (poOldSRS != nullptr)
7513 : {
7514 : poNewSRS =
7515 1 : OGRSpatialReferenceRefCountedPtr::makeClone(poOldSRS);
7516 1 : if (poNewSRSRef)
7517 1 : poNewSRS->SetCoordinateEpoch(
7518 : poNewSRSRef->GetCoordinateEpoch());
7519 : }
7520 : }
7521 :
7522 4 : const char *const apszOptions[] = {
7523 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
7524 4 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
7525 10 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
7526 2 : (poOldSRS != nullptr && poNewSRS != nullptr &&
7527 2 : !poOldSRS->IsSame(poNewSRS.get(), apszOptions)))
7528 : {
7529 : // Temporary remove foreign key checks
7530 : const GPKGTemporaryForeignKeyCheckDisabler
7531 4 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
7532 :
7533 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7534 0 : return OGRERR_FAILURE;
7535 :
7536 4 : const int nNewSRID = m_poDS->GetSrsId(poNewSRS.get());
7537 :
7538 : // Replace the old SRID by the new ones in geometry blobs
7539 4 : int32_t nNewSRID_LSB = nNewSRID;
7540 4 : CPL_LSBPTR32(&nNewSRID_LSB);
7541 4 : GByte abySRID_LSB[5] = {0, 0, 0, 0};
7542 4 : memcpy(abySRID_LSB, &nNewSRID_LSB, 4);
7543 4 : char *pszSRID_LSB_HEX = CPLBinaryToHex(4, abySRID_LSB);
7544 :
7545 4 : int32_t nNewSRID_MSB = nNewSRID;
7546 4 : CPL_MSBPTR32(&nNewSRID_MSB);
7547 4 : GByte abySRID_MSB[5] = {0, 0, 0, 0};
7548 4 : memcpy(abySRID_MSB, &nNewSRID_MSB, 4);
7549 4 : char *pszSRID_MSB_HEX = CPLBinaryToHex(4, abySRID_MSB);
7550 :
7551 : // Black magic below...
7552 : // the substr(hex(...) IN ('0','2',...'E') checks if bit 0 of the
7553 : // 4th byte is 0 and use that to decide how to replace the old SRID
7554 : // by the new one.
7555 4 : CPLString osSQL;
7556 : osSQL.Printf(
7557 : "UPDATE \"%s\" SET \"%s\" = "
7558 : "CAST(substr(\"%s\", 1, 4) || "
7559 : "(CASE WHEN substr(hex(substr(\"%s\", 4, 1)),2) IN "
7560 : "('0','2','4','6','8','A','C','E') "
7561 : "THEN x'%s' ELSE x'%s' END) || substr(\"%s\", 9) AS BLOB) "
7562 : "WHERE \"%s\" IS NOT NULL",
7563 8 : SQLEscapeName(m_pszTableName).c_str(),
7564 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7565 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7566 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7567 : pszSRID_MSB_HEX, pszSRID_LSB_HEX,
7568 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7569 28 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str());
7570 4 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL.c_str());
7571 4 : CPLFree(pszSRID_MSB_HEX);
7572 4 : CPLFree(pszSRID_LSB_HEX);
7573 4 : if (eErr != OGRERR_NONE)
7574 : {
7575 0 : m_poDS->SoftRollbackTransaction();
7576 0 : return OGRERR_FAILURE;
7577 : }
7578 :
7579 4 : char *pszSQL = sqlite3_mprintf(
7580 : "UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'",
7581 : nNewSRID, m_pszTableName);
7582 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7583 4 : sqlite3_free(pszSQL);
7584 4 : if (eErr != OGRERR_NONE)
7585 : {
7586 0 : m_poDS->SoftRollbackTransaction();
7587 0 : return OGRERR_FAILURE;
7588 : }
7589 :
7590 4 : pszSQL = sqlite3_mprintf(
7591 : "UPDATE gpkg_geometry_columns SET srs_id = %d WHERE "
7592 : "table_name = '%q' AND column_name = '%q'",
7593 : nNewSRID, m_pszTableName, poGeomFieldDefn->GetNameRef());
7594 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7595 4 : sqlite3_free(pszSQL);
7596 4 : if (eErr != OGRERR_NONE)
7597 : {
7598 0 : m_poDS->SoftRollbackTransaction();
7599 0 : return OGRERR_FAILURE;
7600 : }
7601 :
7602 4 : if (m_poDS->SoftCommitTransaction() != OGRERR_NONE)
7603 : {
7604 0 : return OGRERR_FAILURE;
7605 : }
7606 :
7607 4 : m_iSrs = nNewSRID;
7608 4 : poGeomFieldDefn->SetSpatialRef(poNewSRS.get());
7609 : }
7610 : }
7611 :
7612 8 : return OGRERR_NONE;
7613 : }
7614 :
7615 : /************************************************************************/
7616 : /* ReorderFields() */
7617 : /************************************************************************/
7618 :
7619 4 : OGRErr OGRGeoPackageTableLayer::ReorderFields(int *panMap)
7620 : {
7621 4 : if (!m_bFeatureDefnCompleted)
7622 0 : GetLayerDefn();
7623 4 : if (!CheckUpdatableTable("ReorderFields"))
7624 2 : return OGRERR_FAILURE;
7625 :
7626 2 : if (m_poFeatureDefn->GetFieldCount() == 0)
7627 0 : return OGRERR_NONE;
7628 :
7629 2 : OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
7630 2 : if (eErr != OGRERR_NONE)
7631 1 : return eErr;
7632 :
7633 : /* -------------------------------------------------------------------- */
7634 : /* Deferred actions, reset state. */
7635 : /* -------------------------------------------------------------------- */
7636 1 : ResetReading();
7637 1 : RunDeferredCreationIfNecessary();
7638 1 : if (m_bThreadRTreeStarted)
7639 0 : CancelAsyncRTree();
7640 1 : if (!RunDeferredSpatialIndexUpdate())
7641 0 : return OGRERR_FAILURE;
7642 :
7643 : /* -------------------------------------------------------------------- */
7644 : /* Drop any iterator since we change the DB structure */
7645 : /* -------------------------------------------------------------------- */
7646 1 : m_poDS->ResetReadingAllLayers();
7647 :
7648 : /* -------------------------------------------------------------------- */
7649 : /* Build list of old fields, and the list of new fields. */
7650 : /* -------------------------------------------------------------------- */
7651 2 : std::vector<OGRFieldDefn *> apoFields;
7652 3 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
7653 : {
7654 : OGRFieldDefn *poFieldDefn =
7655 2 : m_poFeatureDefn->GetFieldDefn(panMap[iField]);
7656 2 : apoFields.push_back(poFieldDefn);
7657 : }
7658 :
7659 2 : const CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
7660 2 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
7661 :
7662 : /* -------------------------------------------------------------------- */
7663 : /* Recreate table in a transaction */
7664 : /* -------------------------------------------------------------------- */
7665 1 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7666 0 : return OGRERR_FAILURE;
7667 :
7668 1 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
7669 :
7670 : /* -------------------------------------------------------------------- */
7671 : /* Finish */
7672 : /* -------------------------------------------------------------------- */
7673 1 : if (eErr == OGRERR_NONE)
7674 : {
7675 1 : eErr = m_poDS->SoftCommitTransaction();
7676 :
7677 1 : if (eErr == OGRERR_NONE)
7678 : {
7679 1 : eErr = whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
7680 : }
7681 :
7682 1 : ResetReading();
7683 : }
7684 : else
7685 : {
7686 0 : m_poDS->SoftRollbackTransaction();
7687 : }
7688 :
7689 1 : return eErr;
7690 : }
7691 :
7692 : /************************************************************************/
7693 : /* OGR_GPKG_GeometryTypeAggregate() */
7694 : /************************************************************************/
7695 :
7696 : namespace
7697 : {
7698 : struct GeometryTypeAggregateContext
7699 : {
7700 : sqlite3 *m_hDB = nullptr;
7701 : int m_nFlags = 0;
7702 : bool m_bIsGeometryTypeAggregateInterrupted = false;
7703 : std::map<OGRwkbGeometryType, int64_t> m_oMapCount{};
7704 : std::set<OGRwkbGeometryType> m_oSetNotNull{};
7705 :
7706 14 : explicit GeometryTypeAggregateContext(sqlite3 *hDB, int nFlags)
7707 14 : : m_hDB(hDB), m_nFlags(nFlags)
7708 : {
7709 14 : }
7710 :
7711 : GeometryTypeAggregateContext(const GeometryTypeAggregateContext &) = delete;
7712 : GeometryTypeAggregateContext &
7713 : operator=(const GeometryTypeAggregateContext &) = delete;
7714 :
7715 2 : void SetGeometryTypeAggregateInterrupted(bool b)
7716 : {
7717 2 : m_bIsGeometryTypeAggregateInterrupted = b;
7718 2 : if (b)
7719 2 : sqlite3_interrupt(m_hDB);
7720 2 : }
7721 : };
7722 :
7723 : } // namespace
7724 :
7725 1376 : static void OGR_GPKG_GeometryTypeAggregate_Step(sqlite3_context *pContext,
7726 : int /*argc*/,
7727 : sqlite3_value **argv)
7728 : {
7729 : const GByte *pabyBLOB =
7730 1376 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
7731 :
7732 : auto poContext = static_cast<GeometryTypeAggregateContext *>(
7733 1376 : sqlite3_user_data(pContext));
7734 :
7735 1376 : OGRwkbGeometryType eGeometryType = wkbNone;
7736 1376 : OGRErr err = OGRERR_FAILURE;
7737 1376 : if (pabyBLOB != nullptr)
7738 : {
7739 : GPkgHeader sHeader;
7740 1353 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
7741 2706 : if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, &sHeader) == OGRERR_NONE &&
7742 1353 : static_cast<size_t>(nBLOBLen) >= sHeader.nHeaderLen + 5)
7743 : {
7744 1353 : err = OGRReadWKBGeometryType(pabyBLOB + sHeader.nHeaderLen,
7745 : wkbVariantIso, &eGeometryType);
7746 1353 : if (eGeometryType == wkbGeometryCollection25D &&
7747 4 : (poContext->m_nFlags & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
7748 : {
7749 : auto poGeom = std::unique_ptr<OGRGeometry>(
7750 2 : GPkgGeometryToOGR(pabyBLOB, nBLOBLen, nullptr));
7751 1 : if (poGeom)
7752 : {
7753 1 : const auto poGC = poGeom->toGeometryCollection();
7754 1 : if (poGC->getNumGeometries() > 0)
7755 : {
7756 : auto eSubGeomType =
7757 1 : poGC->getGeometryRef(0)->getGeometryType();
7758 1 : if (eSubGeomType == wkbTINZ)
7759 1 : eGeometryType = wkbTINZ;
7760 : }
7761 : }
7762 : }
7763 : }
7764 : }
7765 : else
7766 : {
7767 : // NULL geometry
7768 23 : err = OGRERR_NONE;
7769 : }
7770 1376 : if (err == OGRERR_NONE)
7771 : {
7772 1376 : ++poContext->m_oMapCount[eGeometryType];
7773 1376 : if (eGeometryType != wkbNone &&
7774 1353 : (poContext->m_nFlags & OGR_GGT_STOP_IF_MIXED) != 0)
7775 : {
7776 4 : poContext->m_oSetNotNull.insert(eGeometryType);
7777 4 : if (poContext->m_oSetNotNull.size() == 2)
7778 : {
7779 2 : poContext->SetGeometryTypeAggregateInterrupted(true);
7780 : }
7781 : }
7782 : }
7783 1376 : }
7784 :
7785 11 : static void OGR_GPKG_GeometryTypeAggregate_Finalize(sqlite3_context *)
7786 : {
7787 11 : }
7788 :
7789 : /************************************************************************/
7790 : /* GetGeometryTypes() */
7791 : /************************************************************************/
7792 :
7793 15 : OGRGeometryTypeCounter *OGRGeoPackageTableLayer::GetGeometryTypes(
7794 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
7795 : GDALProgressFunc pfnProgress, void *pProgressData)
7796 : {
7797 15 : const OGRFeatureDefn *poDefn = GetLayerDefn();
7798 :
7799 : /* -------------------------------------------------------------------- */
7800 : /* Deferred actions, reset state. */
7801 : /* -------------------------------------------------------------------- */
7802 15 : RunDeferredCreationIfNecessary();
7803 15 : if (!RunDeferredSpatialIndexUpdate())
7804 : {
7805 0 : nEntryCountOut = 0;
7806 0 : return nullptr;
7807 : }
7808 :
7809 15 : const int nGeomFieldCount = poDefn->GetGeomFieldCount();
7810 15 : if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
7811 : {
7812 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
7813 1 : nEntryCountOut = 0;
7814 1 : return nullptr;
7815 : }
7816 :
7817 : #ifdef SQLITE_HAS_PROGRESS_HANDLER
7818 : struct CancelCallback
7819 : {
7820 : sqlite3 *m_hDB = nullptr;
7821 : GDALProgressFunc m_pfnProgress = nullptr;
7822 : void *m_pProgressData = nullptr;
7823 :
7824 14 : CancelCallback(sqlite3 *hDB, GDALProgressFunc pfnProgressIn,
7825 : void *pProgressDataIn)
7826 14 : : m_hDB(hDB),
7827 14 : m_pfnProgress(pfnProgressIn != GDALDummyProgress ? pfnProgressIn
7828 : : nullptr),
7829 14 : m_pProgressData(pProgressDataIn)
7830 : {
7831 14 : if (m_pfnProgress)
7832 : {
7833 : // If changing that value, update
7834 : // ogr_gpkg.py::test_ogr_gpkg_get_geometry_types
7835 2 : constexpr int COUNT_VM_INSTRUCTIONS = 1000;
7836 2 : sqlite3_progress_handler(m_hDB, COUNT_VM_INSTRUCTIONS,
7837 : ProgressHandler, this);
7838 : }
7839 14 : }
7840 :
7841 14 : ~CancelCallback()
7842 14 : {
7843 14 : if (m_pfnProgress)
7844 : {
7845 2 : sqlite3_progress_handler(m_hDB, 0, nullptr, nullptr);
7846 : }
7847 14 : }
7848 :
7849 : CancelCallback(const CancelCallback &) = delete;
7850 : CancelCallback &operator=(const CancelCallback &) = delete;
7851 :
7852 1 : static int ProgressHandler(void *pData)
7853 : {
7854 1 : CancelCallback *psCancelCallback =
7855 : static_cast<CancelCallback *>(pData);
7856 1 : return psCancelCallback->m_pfnProgress != nullptr &&
7857 1 : psCancelCallback->m_pfnProgress(
7858 : 0.0, "", psCancelCallback->m_pProgressData)
7859 2 : ? 0
7860 1 : : 1;
7861 : }
7862 : };
7863 :
7864 28 : CancelCallback oCancelCallback(m_poDS->hDB, pfnProgress, pProgressData);
7865 : #else
7866 : CPL_IGNORE_RET_VAL(pfnProgress);
7867 : CPL_IGNORE_RET_VAL(pProgressData);
7868 : #endif
7869 :
7870 : // For internal use only
7871 :
7872 28 : GeometryTypeAggregateContext sContext(m_poDS->hDB, nFlagsGGT);
7873 :
7874 28 : CPLString osFuncName;
7875 14 : osFuncName.Printf("OGR_GPKG_GeometryTypeAggregate_INTERNAL_%p", &sContext);
7876 :
7877 14 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
7878 : &sContext, nullptr,
7879 : OGR_GPKG_GeometryTypeAggregate_Step,
7880 : OGR_GPKG_GeometryTypeAggregate_Finalize);
7881 :
7882 : // Using this aggregate function is slightly faster than using
7883 : // sqlite3_step() to loop over each geometry blob (650 ms vs 750ms on a 1.6
7884 : // GB db with 3.3 million features)
7885 28 : char *pszSQL = sqlite3_mprintf(
7886 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
7887 14 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
7888 30 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
7889 14 : char *pszErrMsg = nullptr;
7890 : const int rc =
7891 14 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
7892 :
7893 : // Delete function
7894 14 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
7895 : nullptr, nullptr, nullptr, nullptr);
7896 :
7897 14 : if (rc != SQLITE_OK && !sContext.m_bIsGeometryTypeAggregateInterrupted)
7898 : {
7899 1 : if (rc != SQLITE_INTERRUPT)
7900 : {
7901 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
7902 : pszSQL, pszErrMsg);
7903 : }
7904 1 : sqlite3_free(pszErrMsg);
7905 1 : sqlite3_free(pszSQL);
7906 1 : nEntryCountOut = 0;
7907 1 : return nullptr;
7908 : }
7909 13 : sqlite3_free(pszErrMsg);
7910 13 : sqlite3_free(pszSQL);
7911 :
7912 : // Format result
7913 13 : nEntryCountOut = static_cast<int>(sContext.m_oMapCount.size());
7914 : OGRGeometryTypeCounter *pasRet = static_cast<OGRGeometryTypeCounter *>(
7915 13 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
7916 13 : int i = 0;
7917 47 : for (const auto &sEntry : sContext.m_oMapCount)
7918 : {
7919 34 : pasRet[i].eGeomType = sEntry.first;
7920 34 : pasRet[i].nCount = sEntry.second;
7921 34 : ++i;
7922 : }
7923 13 : return pasRet;
7924 : }
7925 :
7926 : /************************************************************************/
7927 : /* OGR_GPKG_FillArrowArray_Step() */
7928 : /************************************************************************/
7929 :
7930 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7931 : sqlite3_value **argv);
7932 :
7933 64974 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7934 : sqlite3_value **argv)
7935 : {
7936 : auto psFillArrowArray = static_cast<OGRGPKGTableLayerFillArrowArray *>(
7937 64974 : sqlite3_user_data(pContext));
7938 :
7939 : // To be called under psFillArrowArray->oMutex
7940 : const auto SubmitBatchToCaller =
7941 762 : [psFillArrowArray](std::unique_lock<std::mutex> &oLock)
7942 : {
7943 127 : psFillArrowArray->psHelper->Shrink(psFillArrowArray->nCountRows);
7944 127 : psFillArrowArray->bThreadReady = true;
7945 127 : psFillArrowArray->oCV.notify_one();
7946 254 : while (psFillArrowArray->nCountRows > 0)
7947 : {
7948 127 : psFillArrowArray->oCV.wait(oLock);
7949 : }
7950 65101 : };
7951 :
7952 : const auto DealWithTooBigArray =
7953 549 : [&SubmitBatchToCaller, psFillArrowArray](int iFeat)
7954 : {
7955 134 : CPLDebug("GPKG",
7956 : "OGR_GPKG_FillArrowArray_Step(): premature "
7957 : "notification of %d features to consumer due "
7958 : "to too big array",
7959 : iFeat);
7960 134 : if (psFillArrowArray->bAsynchronousMode)
7961 : {
7962 242 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7963 121 : psFillArrowArray->bMemoryLimitReached = true;
7964 121 : SubmitBatchToCaller(oLock);
7965 : }
7966 : else
7967 : {
7968 : {
7969 13 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7970 13 : psFillArrowArray->bMemoryLimitReached = true;
7971 : }
7972 13 : sqlite3_interrupt(psFillArrowArray->hDB);
7973 : }
7974 134 : };
7975 :
7976 : {
7977 64974 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7978 129948 : if (psFillArrowArray->nCountRows >=
7979 64974 : psFillArrowArray->psHelper->m_nMaxBatchSize)
7980 : {
7981 6 : if (psFillArrowArray->bAsynchronousMode)
7982 : {
7983 6 : SubmitBatchToCaller(oLock);
7984 : // Note that psFillArrowArray->psHelper.get() will generally now be
7985 : // different from before the wait()
7986 : }
7987 : else
7988 : {
7989 : // should not happen !
7990 : psFillArrowArray->osErrorMsg = "OGR_GPKG_FillArrowArray_Step() "
7991 0 : "got more rows than expected!";
7992 0 : sqlite3_interrupt(psFillArrowArray->hDB);
7993 0 : psFillArrowArray->bErrorOccurred = true;
7994 0 : return;
7995 : }
7996 : }
7997 64974 : if (psFillArrowArray->nCountRows < 0)
7998 2 : return;
7999 : }
8000 :
8001 64972 : if (psFillArrowArray->nMemLimit == 0)
8002 148 : psFillArrowArray->nMemLimit = OGRArrowArrayHelper::GetMemLimit();
8003 64972 : const auto nMemLimit = psFillArrowArray->nMemLimit;
8004 : const int SQLITE_MAX_FUNCTION_ARG =
8005 64972 : sqlite3_limit(psFillArrowArray->hDB, SQLITE_LIMIT_FUNCTION_ARG, -1);
8006 64972 : const bool bDateTimeAsString = psFillArrowArray->bDateTimeAsString;
8007 65093 : begin:
8008 : int iFeat;
8009 : OGRArrowArrayHelper *psHelper;
8010 : {
8011 130186 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
8012 65093 : iFeat = psFillArrowArray->nCountRows;
8013 65093 : psHelper = psFillArrowArray->psHelper.get();
8014 : }
8015 65093 : int iCol = 0;
8016 65093 : const int iFieldStart = sqlite3_value_int(argv[iCol]);
8017 65093 : ++iCol;
8018 65093 : int iField = std::max(0, iFieldStart);
8019 :
8020 : GIntBig nFID;
8021 65093 : if (iFieldStart < 0)
8022 : {
8023 65087 : nFID = sqlite3_value_int64(argv[iCol]);
8024 65087 : iCol++;
8025 65087 : if (psHelper->m_panFIDValues)
8026 : {
8027 65068 : psHelper->m_panFIDValues[iFeat] = nFID;
8028 : }
8029 65087 : psFillArrowArray->nCurFID = nFID;
8030 : }
8031 : else
8032 : {
8033 6 : nFID = psFillArrowArray->nCurFID;
8034 : }
8035 :
8036 130175 : if (iFieldStart < 0 && !psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
8037 65082 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8038 : {
8039 65079 : const int iArrowField = psHelper->m_mapOGRGeomFieldToArrowField[0];
8040 65079 : auto psArray = psHelper->m_out_array->children[iArrowField];
8041 65079 : size_t nWKBSize = 0;
8042 65079 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
8043 65079 : if (nSqlite3ColType == SQLITE_BLOB)
8044 : {
8045 : GPkgHeader oHeader;
8046 64695 : memset(&oHeader, 0, sizeof(oHeader));
8047 :
8048 64695 : const GByte *pabyWkb = nullptr;
8049 64695 : const int nBlobSize = sqlite3_value_bytes(argv[iCol]);
8050 : // coverity[tainted_data_return]
8051 : const GByte *pabyBlob =
8052 64695 : static_cast<const GByte *>(sqlite3_value_blob(argv[iCol]));
8053 64695 : std::vector<GByte> abyWkb;
8054 64695 : if (nBlobSize >= 8 && pabyBlob && pabyBlob[0] == 'G' &&
8055 64694 : pabyBlob[1] == 'P')
8056 : {
8057 64694 : if (psFillArrowArray->poLayer->m_bUndoDiscardCoordLSBOnReading)
8058 : {
8059 : OGRGeometry *poGeomPtr =
8060 1 : GPkgGeometryToOGR(pabyBlob, nBlobSize, nullptr);
8061 1 : if (poGeomPtr)
8062 : {
8063 1 : poGeomPtr->roundCoordinates(
8064 1 : psFillArrowArray->poFeatureDefn->GetGeomFieldDefn(0)
8065 : ->GetCoordinatePrecision());
8066 1 : nWKBSize = poGeomPtr->WkbSize();
8067 1 : abyWkb.resize(nWKBSize);
8068 1 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
8069 1 : wkbVariantIso) !=
8070 : OGRERR_NONE)
8071 : {
8072 0 : nWKBSize = 0;
8073 : }
8074 : else
8075 : {
8076 1 : pabyWkb = abyWkb.data();
8077 : }
8078 1 : delete poGeomPtr;
8079 : }
8080 : }
8081 : else
8082 : {
8083 : /* Read header */
8084 : OGRErr err =
8085 64693 : GPkgHeaderFromWKB(pabyBlob, nBlobSize, &oHeader);
8086 64693 : if (err == OGRERR_NONE)
8087 : {
8088 : /* WKB pointer */
8089 64693 : pabyWkb = pabyBlob + oHeader.nHeaderLen;
8090 64693 : nWKBSize = nBlobSize - oHeader.nHeaderLen;
8091 : }
8092 64694 : }
8093 : }
8094 1 : else if (nBlobSize > 0 && pabyBlob)
8095 : {
8096 : // Try also spatialite geometry blobs, although that is
8097 : // not really expected...
8098 1 : OGRGeometry *poGeomPtr = nullptr;
8099 1 : if (OGRSQLiteImportSpatiaLiteGeometry(
8100 1 : pabyBlob, nBlobSize, &poGeomPtr) != OGRERR_NONE)
8101 : {
8102 1 : CPLError(CE_Failure, CPLE_AppDefined,
8103 : "Unable to read geometry");
8104 : }
8105 : else
8106 : {
8107 0 : nWKBSize = poGeomPtr->WkbSize();
8108 0 : abyWkb.resize(nWKBSize);
8109 0 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
8110 0 : wkbVariantIso) != OGRERR_NONE)
8111 : {
8112 0 : nWKBSize = 0;
8113 : }
8114 : else
8115 : {
8116 0 : pabyWkb = abyWkb.data();
8117 : }
8118 : }
8119 1 : delete poGeomPtr;
8120 : }
8121 :
8122 64695 : if (nWKBSize != 0)
8123 : {
8124 : // Deal with spatial filter
8125 64694 : if (psFillArrowArray->poLayerForFilterGeom)
8126 : {
8127 33 : OGREnvelope sEnvelope;
8128 33 : bool bEnvelopeAlreadySet = false;
8129 33 : if (oHeader.bEmpty)
8130 : {
8131 0 : bEnvelopeAlreadySet = true;
8132 : }
8133 33 : else if (oHeader.bExtentHasXY)
8134 : {
8135 31 : bEnvelopeAlreadySet = true;
8136 31 : sEnvelope.MinX = oHeader.MinX;
8137 31 : sEnvelope.MinY = oHeader.MinY;
8138 31 : sEnvelope.MaxX = oHeader.MaxX;
8139 31 : sEnvelope.MaxY = oHeader.MaxY;
8140 : }
8141 :
8142 66 : if (!psFillArrowArray->poLayerForFilterGeom
8143 33 : ->FilterWKBGeometry(pabyWkb, nWKBSize,
8144 : bEnvelopeAlreadySet,
8145 : sEnvelope))
8146 : {
8147 2 : return;
8148 : }
8149 : }
8150 :
8151 64692 : if (iFeat > 0)
8152 : {
8153 64526 : auto panOffsets = static_cast<int32_t *>(
8154 64526 : const_cast<void *>(psArray->buffers[1]));
8155 64526 : const uint32_t nCurLength =
8156 64526 : static_cast<uint32_t>(panOffsets[iFeat]);
8157 64526 : if (nWKBSize <= nMemLimit &&
8158 64526 : nWKBSize > nMemLimit - nCurLength)
8159 : {
8160 52 : DealWithTooBigArray(iFeat);
8161 52 : if (psFillArrowArray->bAsynchronousMode)
8162 : {
8163 47 : goto begin;
8164 : }
8165 : else
8166 : {
8167 5 : return;
8168 : }
8169 : }
8170 : }
8171 :
8172 64640 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8173 : iArrowField, iFeat, nWKBSize);
8174 64640 : if (outPtr == nullptr)
8175 : {
8176 0 : goto error;
8177 : }
8178 64640 : memcpy(outPtr, pabyWkb, nWKBSize);
8179 : }
8180 : else
8181 : {
8182 1 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8183 : }
8184 : }
8185 :
8186 65025 : if (nWKBSize == 0)
8187 : {
8188 385 : if (!psHelper->SetNull(iArrowField, iFeat))
8189 : {
8190 0 : goto error;
8191 : }
8192 : }
8193 65025 : iCol++;
8194 : }
8195 :
8196 1268260 : for (; iField < psHelper->m_nFieldCount; iField++)
8197 : {
8198 1203310 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8199 1203310 : if (iArrowField < 0)
8200 73 : continue;
8201 1203240 : if (iCol == SQLITE_MAX_FUNCTION_ARG)
8202 6 : break;
8203 :
8204 : const OGRFieldDefn *poFieldDefn =
8205 1203240 : psFillArrowArray->poFeatureDefn->GetFieldDefnUnsafe(iField);
8206 :
8207 1203240 : auto psArray = psHelper->m_out_array->children[iArrowField];
8208 :
8209 1203240 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
8210 1203240 : if (nSqlite3ColType == SQLITE_NULL)
8211 : {
8212 784 : if (!psHelper->SetNull(iArrowField, iFeat))
8213 : {
8214 0 : goto error;
8215 : }
8216 784 : iCol++;
8217 784 : continue;
8218 : }
8219 :
8220 1202450 : switch (poFieldDefn->GetType())
8221 : {
8222 1245 : case OFTInteger:
8223 : {
8224 1245 : const int nVal = sqlite3_value_int(argv[iCol]);
8225 1245 : if (poFieldDefn->GetSubType() == OFSTBoolean)
8226 : {
8227 122 : if (nVal != 0)
8228 : {
8229 91 : psHelper->SetBoolOn(psArray, iFeat);
8230 : }
8231 : }
8232 1123 : else if (poFieldDefn->GetSubType() == OFSTInt16)
8233 : {
8234 59 : psHelper->SetInt16(psArray, iFeat,
8235 : static_cast<int16_t>(nVal));
8236 : }
8237 : else
8238 : {
8239 1064 : psHelper->SetInt32(psArray, iFeat, nVal);
8240 : }
8241 1245 : break;
8242 : }
8243 :
8244 220 : case OFTInteger64:
8245 : {
8246 220 : psHelper->SetInt64(psArray, iFeat,
8247 220 : sqlite3_value_int64(argv[iCol]));
8248 220 : break;
8249 : }
8250 :
8251 301 : case OFTReal:
8252 : {
8253 301 : const double dfVal = sqlite3_value_double(argv[iCol]);
8254 301 : if (poFieldDefn->GetSubType() == OFSTFloat32)
8255 : {
8256 51 : psHelper->SetFloat(psArray, iFeat,
8257 : static_cast<float>(dfVal));
8258 : }
8259 : else
8260 : {
8261 250 : psHelper->SetDouble(psArray, iFeat, dfVal);
8262 : }
8263 301 : break;
8264 : }
8265 :
8266 176 : case OFTBinary:
8267 : {
8268 : const uint32_t nBytes =
8269 176 : static_cast<uint32_t>(sqlite3_value_bytes(argv[iCol]));
8270 : // coverity[tainted_data_return]
8271 176 : const void *pabyData = sqlite3_value_blob(argv[iCol]);
8272 176 : if (pabyData != nullptr || nBytes == 0)
8273 : {
8274 176 : if (iFeat > 0)
8275 : {
8276 119 : auto panOffsets = static_cast<int32_t *>(
8277 119 : const_cast<void *>(psArray->buffers[1]));
8278 119 : const uint32_t nCurLength =
8279 119 : static_cast<uint32_t>(panOffsets[iFeat]);
8280 119 : if (nBytes <= nMemLimit &&
8281 119 : nBytes > nMemLimit - nCurLength)
8282 : {
8283 41 : DealWithTooBigArray(iFeat);
8284 41 : if (psFillArrowArray->bAsynchronousMode)
8285 : {
8286 37 : goto begin;
8287 : }
8288 : else
8289 : {
8290 4 : return;
8291 : }
8292 : }
8293 : }
8294 :
8295 135 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8296 : iArrowField, iFeat, nBytes);
8297 135 : if (outPtr == nullptr)
8298 : {
8299 0 : goto error;
8300 : }
8301 135 : if (nBytes)
8302 135 : memcpy(outPtr, pabyData, nBytes);
8303 : }
8304 : else
8305 : {
8306 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8307 : }
8308 135 : break;
8309 : }
8310 :
8311 51 : case OFTDate:
8312 : {
8313 : OGRField ogrField;
8314 : const auto pszTxt = reinterpret_cast<const char *>(
8315 51 : sqlite3_value_text(argv[iCol]));
8316 102 : if (pszTxt != nullptr &&
8317 51 : psFillArrowArray->poLayer->ParseDateField(
8318 : pszTxt, &ogrField, poFieldDefn, nFID))
8319 : {
8320 51 : psHelper->SetDate(psArray, iFeat,
8321 51 : psFillArrowArray->brokenDown, ogrField);
8322 : }
8323 51 : break;
8324 : }
8325 :
8326 65 : case OFTDateTime:
8327 : {
8328 65 : if (!bDateTimeAsString)
8329 : {
8330 : OGRField ogrField;
8331 : const auto pszTxt = reinterpret_cast<const char *>(
8332 55 : sqlite3_value_text(argv[iCol]));
8333 110 : if (pszTxt != nullptr &&
8334 55 : psFillArrowArray->poLayer->ParseDateTimeField(
8335 : pszTxt, &ogrField, poFieldDefn, nFID))
8336 : {
8337 55 : psHelper->SetDateTime(
8338 55 : psArray, iFeat, psFillArrowArray->brokenDown,
8339 55 : psHelper->m_anTZFlags[iField], ogrField);
8340 : }
8341 55 : break;
8342 : }
8343 : else
8344 : {
8345 : [[fallthrough]];
8346 : }
8347 : }
8348 :
8349 : case OFTString:
8350 : {
8351 : const auto pszTxt = reinterpret_cast<const char *>(
8352 1200400 : sqlite3_value_text(argv[iCol]));
8353 1200400 : if (pszTxt != nullptr)
8354 : {
8355 1200400 : const size_t nBytes = strlen(pszTxt);
8356 1200400 : if (iFeat > 0)
8357 : {
8358 1200280 : auto panOffsets = static_cast<int32_t *>(
8359 1200280 : const_cast<void *>(psArray->buffers[1]));
8360 1200280 : const uint32_t nCurLength =
8361 1200280 : static_cast<uint32_t>(panOffsets[iFeat]);
8362 1200280 : if (nBytes <= nMemLimit &&
8363 1200280 : nBytes > nMemLimit - nCurLength)
8364 : {
8365 41 : DealWithTooBigArray(iFeat);
8366 41 : if (psFillArrowArray->bAsynchronousMode)
8367 : {
8368 37 : goto begin;
8369 : }
8370 : else
8371 : {
8372 4 : return;
8373 : }
8374 : }
8375 : }
8376 :
8377 1200360 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8378 : iArrowField, iFeat, nBytes);
8379 1200360 : if (outPtr == nullptr)
8380 : {
8381 0 : goto error;
8382 : }
8383 1200360 : if (nBytes)
8384 1200360 : memcpy(outPtr, pszTxt, nBytes);
8385 : }
8386 : else
8387 : {
8388 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8389 : }
8390 1200360 : break;
8391 : }
8392 :
8393 0 : default:
8394 0 : break;
8395 : }
8396 :
8397 1202370 : iCol++;
8398 : }
8399 :
8400 64957 : if (iField == psHelper->m_nFieldCount)
8401 : {
8402 64951 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
8403 64951 : psFillArrowArray->nCountRows++;
8404 : }
8405 64957 : return;
8406 :
8407 0 : error:
8408 0 : sqlite3_interrupt(psFillArrowArray->hDB);
8409 : }
8410 :
8411 : /************************************************************************/
8412 : /* OGR_GPKG_FillArrowArray_Finalize() */
8413 : /************************************************************************/
8414 :
8415 157 : static void OGR_GPKG_FillArrowArray_Finalize(sqlite3_context * /*pContext*/)
8416 : {
8417 157 : }
8418 :
8419 : /************************************************************************/
8420 : /* GetNextArrowArrayAsynchronous() */
8421 : /************************************************************************/
8422 :
8423 202 : int OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronous(
8424 : struct ArrowArrayStream *stream, struct ArrowArray *out_array)
8425 : {
8426 202 : memset(out_array, 0, sizeof(*out_array));
8427 :
8428 202 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8429 :
8430 202 : if (m_poFillArrowArray)
8431 : {
8432 150 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8433 150 : if (m_poFillArrowArray->bIsFinished)
8434 : {
8435 25 : return 0;
8436 : }
8437 : }
8438 :
8439 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
8440 354 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
8441 177 : if (out_array->release == nullptr)
8442 : {
8443 0 : return ENOMEM;
8444 : }
8445 :
8446 177 : if (m_poFillArrowArray == nullptr)
8447 : {
8448 : // Check that the total number of arguments passed to
8449 : // OGR_GPKG_FillArrowArray_INTERNAL() doesn't exceed SQLITE_MAX_FUNCTION_ARG
8450 : // If it does, we cannot reliably use GetNextArrowArrayAsynchronous() in
8451 : // the situation where the ArrowArray would exceed the nMemLimit.
8452 : // So be on the safe side, and rely on the base OGRGeoPackageLayer
8453 : // implementation
8454 : const int SQLITE_MAX_FUNCTION_ARG =
8455 52 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
8456 52 : int nCountArgs = 1 // field index
8457 : + 1; // FID column
8458 101 : if (!psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
8459 49 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8460 : {
8461 49 : ++nCountArgs;
8462 : }
8463 472 : for (int iField = 0; iField < psHelper->m_nFieldCount; iField++)
8464 : {
8465 422 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8466 422 : if (iArrowField >= 0)
8467 : {
8468 422 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
8469 : {
8470 2 : psHelper.reset();
8471 2 : if (out_array->release)
8472 2 : out_array->release(out_array);
8473 2 : return OGRGeoPackageLayer::GetNextArrowArray(stream,
8474 2 : out_array);
8475 : }
8476 420 : ++nCountArgs;
8477 : }
8478 : }
8479 :
8480 : m_poFillArrowArray =
8481 50 : std::make_unique<OGRGPKGTableLayerFillArrowArray>();
8482 50 : m_poFillArrowArray->psHelper = std::move(psHelper);
8483 100 : m_poFillArrowArray->bDateTimeAsString =
8484 50 : m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
8485 : false);
8486 50 : m_poFillArrowArray->poFeatureDefn = m_poFeatureDefn;
8487 50 : m_poFillArrowArray->poLayer = this;
8488 50 : m_poFillArrowArray->hDB = m_poDS->GetDB();
8489 50 : memset(&m_poFillArrowArray->brokenDown, 0,
8490 : sizeof(m_poFillArrowArray->brokenDown));
8491 50 : m_poFillArrowArray->bAsynchronousMode = true;
8492 50 : if (m_poFilterGeom)
8493 10 : m_poFillArrowArray->poLayerForFilterGeom = this;
8494 :
8495 : try
8496 : {
8497 100 : m_oThreadNextArrowArray = std::thread(
8498 100 : [this]() { GetNextArrowArrayAsynchronousWorker(); });
8499 : }
8500 0 : catch (const std::exception &e)
8501 : {
8502 0 : m_poFillArrowArray.reset();
8503 0 : CPLError(CE_Failure, CPLE_AppDefined,
8504 0 : "Cannot start worker thread: %s", e.what());
8505 0 : out_array->release(out_array);
8506 0 : return ENOMEM;
8507 : }
8508 : }
8509 : else
8510 : {
8511 125 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8512 125 : if (m_poFillArrowArray->bErrorOccurred)
8513 : {
8514 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8515 0 : m_poFillArrowArray->osErrorMsg.c_str());
8516 0 : out_array->release(out_array);
8517 0 : return EIO;
8518 : }
8519 :
8520 : // Resume worker thread
8521 125 : m_poFillArrowArray->psHelper = std::move(psHelper);
8522 125 : m_poFillArrowArray->nCountRows = 0;
8523 125 : m_poFillArrowArray->bThreadReady = false;
8524 125 : m_poFillArrowArray->bMemoryLimitReached = false;
8525 125 : m_poFillArrowArray->oCV.notify_one();
8526 : }
8527 :
8528 : // Wait for GetNextArrowArrayAsynchronousWorker() /
8529 : // OGR_GPKG_FillArrowArray_Step() to have generated a result set (or an
8530 : // error)
8531 : bool bIsFinished;
8532 350 : std::string osErrorMsg;
8533 : {
8534 350 : std::unique_lock<std::mutex> oLock(m_poFillArrowArray->oMutex);
8535 344 : while (!m_poFillArrowArray->bThreadReady)
8536 : {
8537 169 : m_poFillArrowArray->oCV.wait(oLock);
8538 : }
8539 175 : CPLAssert(m_poFillArrowArray->bErrorOccurred ||
8540 : m_poFillArrowArray->bMemoryLimitReached ||
8541 : m_poFillArrowArray->bIsFinished ||
8542 : m_poFillArrowArray->nCountRows ==
8543 : m_poFillArrowArray->psHelper->m_nMaxBatchSize);
8544 175 : bIsFinished = m_poFillArrowArray->bIsFinished;
8545 175 : osErrorMsg = m_poFillArrowArray->osErrorMsg;
8546 : }
8547 :
8548 175 : if (m_poFillArrowArray->bErrorOccurred)
8549 : {
8550 2 : m_oThreadNextArrowArray.join();
8551 2 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
8552 2 : m_poFillArrowArray->psHelper->ClearArray();
8553 2 : return EIO;
8554 : }
8555 173 : else if (bIsFinished)
8556 : {
8557 46 : m_oThreadNextArrowArray.join();
8558 : }
8559 :
8560 173 : return 0;
8561 : }
8562 :
8563 : /************************************************************************/
8564 : /* GetNextArrowArrayAsynchronousWorker() */
8565 : /************************************************************************/
8566 :
8567 50 : void OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronousWorker()
8568 : {
8569 50 : sqlite3_create_function(
8570 50 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
8571 50 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, m_poFillArrowArray.get(), nullptr,
8572 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
8573 :
8574 100 : std::string osSQL;
8575 50 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
8576 :
8577 1752 : const auto AddFields = [this, &osSQL]()
8578 : {
8579 54 : if (m_pszFidColumn)
8580 : {
8581 51 : osSQL += "m.\"";
8582 51 : osSQL += SQLEscapeName(m_pszFidColumn);
8583 51 : osSQL += '"';
8584 : }
8585 : else
8586 : {
8587 3 : osSQL += "NULL";
8588 : }
8589 :
8590 54 : if (!m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField
8591 105 : .empty() &&
8592 51 : m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8593 : {
8594 51 : osSQL += ",m.\"";
8595 51 : osSQL += SQLEscapeName(GetGeometryColumn());
8596 51 : osSQL += '"';
8597 : }
8598 242 : for (int iField = 0;
8599 242 : iField < m_poFillArrowArray->psHelper->m_nFieldCount; iField++)
8600 : {
8601 : const int iArrowField =
8602 188 : m_poFillArrowArray->psHelper->m_mapOGRFieldToArrowField[iField];
8603 188 : if (iArrowField >= 0)
8604 : {
8605 : const OGRFieldDefn *poFieldDefn =
8606 188 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
8607 188 : osSQL += ",m.\"";
8608 188 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
8609 188 : osSQL += '"';
8610 : }
8611 : }
8612 54 : };
8613 :
8614 50 : AddFields();
8615 :
8616 50 : osSQL += ") FROM ";
8617 50 : if (m_iNextShapeId > 0)
8618 : {
8619 4 : osSQL += "(SELECT ";
8620 4 : AddFields();
8621 4 : osSQL += " FROM ";
8622 : }
8623 50 : osSQL += '\"';
8624 50 : osSQL += SQLEscapeName(m_pszTableName);
8625 50 : osSQL += "\" m";
8626 50 : if (!m_soFilter.empty())
8627 : {
8628 35 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
8629 10 : HasSpatialIndex())
8630 : {
8631 10 : OGREnvelope sEnvelope;
8632 :
8633 10 : m_poFilterGeom->getEnvelope(&sEnvelope);
8634 :
8635 10 : bool bUseSpatialIndex = true;
8636 20 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
8637 1 : sEnvelope.MinY <= m_poExtent->MinY &&
8638 21 : sEnvelope.MaxX >= m_poExtent->MaxX &&
8639 1 : sEnvelope.MaxY >= m_poExtent->MaxY)
8640 : {
8641 : // Selecting from spatial filter on whole extent can be rather
8642 : // slow. So use function based filtering, just in case the
8643 : // advertized global extent might be wrong. Otherwise we might
8644 : // just discard completely the spatial filter.
8645 1 : bUseSpatialIndex = false;
8646 : }
8647 :
8648 9 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
8649 28 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
8650 9 : !std::isinf(sEnvelope.MaxY))
8651 : {
8652 : osSQL +=
8653 : CPLSPrintf(" JOIN \"%s\" r "
8654 : "ON m.\"%s\" = r.id WHERE "
8655 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
8656 : "r.maxy >= %.12f AND r.miny <= %.12f",
8657 18 : SQLEscapeName(m_osRTreeName).c_str(),
8658 18 : SQLEscapeName(m_osFIDForRTree).c_str(),
8659 9 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
8660 27 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
8661 : }
8662 : }
8663 : else
8664 : {
8665 15 : osSQL += " WHERE ";
8666 15 : osSQL += m_soFilter;
8667 : }
8668 : }
8669 :
8670 50 : if (m_iNextShapeId > 0)
8671 : osSQL +=
8672 4 : CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB ") m", m_iNextShapeId);
8673 :
8674 : // CPLDebug("GPKG", "%s", osSQL.c_str());
8675 :
8676 50 : char *pszErrMsg = nullptr;
8677 100 : std::string osErrorMsg;
8678 50 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
8679 50 : &pszErrMsg) != SQLITE_OK)
8680 : {
8681 2 : osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
8682 : }
8683 50 : sqlite3_free(pszErrMsg);
8684 :
8685 : // Delete function
8686 50 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
8687 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
8688 : nullptr, nullptr, nullptr);
8689 :
8690 100 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8691 50 : m_poFillArrowArray->bIsFinished = true;
8692 50 : m_poFillArrowArray->bErrorOccurred = !osErrorMsg.empty();
8693 50 : m_poFillArrowArray->osErrorMsg = std::move(osErrorMsg);
8694 :
8695 50 : if (m_poFillArrowArray->nCountRows >= 0)
8696 : {
8697 48 : m_poFillArrowArray->psHelper->Shrink(m_poFillArrowArray->nCountRows);
8698 48 : if (m_poFillArrowArray->nCountRows == 0)
8699 : {
8700 22 : m_poFillArrowArray->psHelper->ClearArray();
8701 : }
8702 : }
8703 50 : m_poFillArrowArray->bThreadReady = true;
8704 50 : m_poFillArrowArray->oCV.notify_one();
8705 50 : }
8706 :
8707 : /************************************************************************/
8708 : /* GetNextArrowArray() */
8709 : /************************************************************************/
8710 :
8711 370 : int OGRGeoPackageTableLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
8712 : struct ArrowArray *out_array)
8713 : {
8714 370 : if (!m_bFeatureDefnCompleted)
8715 1 : GetLayerDefn();
8716 370 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
8717 : {
8718 0 : memset(out_array, 0, sizeof(*out_array));
8719 0 : return EIO;
8720 : }
8721 :
8722 370 : if (m_poFilterGeom != nullptr)
8723 : {
8724 : // Both are exclusive
8725 18 : CreateSpatialIndexIfNecessary();
8726 18 : if (!RunDeferredSpatialIndexUpdate())
8727 : {
8728 0 : memset(out_array, 0, sizeof(*out_array));
8729 0 : return EIO;
8730 : }
8731 : }
8732 :
8733 370 : if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
8734 : {
8735 6 : return OGRGeoPackageLayer::GetNextArrowArray(stream, out_array);
8736 : }
8737 :
8738 959 : if (m_nIsCompatOfOptimizedGetNextArrowArray == FALSE ||
8739 413 : m_pszFidColumn == nullptr || !m_soFilter.empty() ||
8740 777 : m_poFillArrowArray ||
8741 181 : (!m_bGetNextArrowArrayCalledSinceResetReading && m_iNextShapeId > 0))
8742 : {
8743 184 : return GetNextArrowArrayAsynchronous(stream, out_array);
8744 : }
8745 :
8746 : // We can use this optimized version only if there is no hole in FID
8747 : // numbering. That is min(fid) == 1 and max(fid) == m_nTotalFeatureCount
8748 180 : if (m_nIsCompatOfOptimizedGetNextArrowArray < 0)
8749 : {
8750 72 : m_nIsCompatOfOptimizedGetNextArrowArray = FALSE;
8751 72 : const auto nTotalFeatureCount = GetTotalFeatureCount();
8752 72 : if (nTotalFeatureCount < 0)
8753 3 : return GetNextArrowArrayAsynchronous(stream, out_array);
8754 : {
8755 69 : char *pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
8756 : m_pszFidColumn, m_pszTableName);
8757 : OGRErr err;
8758 69 : const auto nMaxFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8759 69 : sqlite3_free(pszSQL);
8760 69 : if (nMaxFID != nTotalFeatureCount)
8761 3 : return GetNextArrowArrayAsynchronous(stream, out_array);
8762 : }
8763 : {
8764 66 : char *pszSQL = sqlite3_mprintf("SELECT MIN(\"%w\") FROM \"%w\"",
8765 : m_pszFidColumn, m_pszTableName);
8766 : OGRErr err;
8767 66 : const auto nMinFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8768 66 : sqlite3_free(pszSQL);
8769 66 : if (nMinFID != 1)
8770 12 : return GetNextArrowArrayAsynchronous(stream, out_array);
8771 : }
8772 54 : m_nIsCompatOfOptimizedGetNextArrowArray = TRUE;
8773 : }
8774 :
8775 162 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8776 :
8777 : // CPLDebug("GPKG", "m_iNextShapeId = " CPL_FRMT_GIB, m_iNextShapeId);
8778 :
8779 324 : const int nMaxBatchSize = OGRArrowArrayHelper::GetMaxFeaturesInBatch(
8780 162 : m_aosArrowArrayStreamOptions);
8781 :
8782 : // Fetch the answer from a potentially queued asynchronous task
8783 162 : if (!m_oQueueArrowArrayPrefetchTasks.empty())
8784 : {
8785 44 : const size_t nTasks = m_oQueueArrowArrayPrefetchTasks.size();
8786 44 : auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
8787 44 : m_oQueueArrowArrayPrefetchTasks.pop();
8788 :
8789 : // Wait for thread to be ready
8790 : {
8791 44 : std::unique_lock<std::mutex> oLock(task->m_oMutex);
8792 46 : while (!task->m_bArrayReady)
8793 : {
8794 2 : task->m_oCV.wait(oLock);
8795 : }
8796 44 : task->m_bArrayReady = false;
8797 : }
8798 44 : if (!task->m_osErrorMsg.empty())
8799 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8800 0 : task->m_osErrorMsg.c_str());
8801 :
8802 145 : const auto stopThread = [&task]()
8803 : {
8804 : {
8805 58 : std::lock_guard oLock(task->m_oMutex);
8806 29 : task->m_bStop = true;
8807 29 : task->m_oCV.notify_one();
8808 : }
8809 29 : if (task->m_oThread.joinable())
8810 29 : task->m_oThread.join();
8811 29 : };
8812 :
8813 44 : if (task->m_iStartShapeId != m_iNextShapeId)
8814 : {
8815 : // Should not normally happen, unless the user messes with
8816 : // GetNextFeature()
8817 0 : CPLError(CE_Failure, CPLE_AppDefined,
8818 : "Worker thread task has not expected m_iStartShapeId "
8819 : "value. Got " CPL_FRMT_GIB ", expected " CPL_FRMT_GIB,
8820 0 : task->m_iStartShapeId, m_iNextShapeId);
8821 0 : if (task->m_psArrowArray->release)
8822 0 : task->m_psArrowArray->release(task->m_psArrowArray.get());
8823 :
8824 0 : stopThread();
8825 : }
8826 44 : else if (task->m_psArrowArray->release)
8827 : {
8828 40 : m_iNextShapeId += task->m_psArrowArray->length;
8829 :
8830 : // Transfer the task ArrowArray to the client array
8831 40 : memcpy(out_array, task->m_psArrowArray.get(),
8832 : sizeof(struct ArrowArray));
8833 40 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8834 :
8835 80 : const bool bMemoryLimitReached = [&task]()
8836 : {
8837 40 : std::unique_lock oLock(task->m_oMutex);
8838 80 : return task->m_bMemoryLimitReached;
8839 40 : }();
8840 :
8841 40 : if (bMemoryLimitReached)
8842 : {
8843 2 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
8844 2 : stopThread();
8845 2 : CancelAsyncNextArrowArray();
8846 2 : return 0;
8847 : }
8848 : // Are the records still available for reading beyond the current
8849 : // queued tasks ? If so, recycle this task to read them
8850 38 : else if (task->m_iStartShapeId +
8851 38 : static_cast<GIntBig>(nTasks) * nMaxBatchSize <=
8852 38 : m_nTotalFeatureCount)
8853 : {
8854 15 : task->m_iStartShapeId +=
8855 15 : static_cast<GIntBig>(nTasks) * nMaxBatchSize;
8856 15 : task->m_poLayer->m_iNextShapeId = task->m_iStartShapeId;
8857 : try
8858 : {
8859 : // Wake-up thread with new task
8860 : {
8861 30 : std::lock_guard oLock(task->m_oMutex);
8862 15 : task->m_bFetchRows = true;
8863 15 : task->m_oCV.notify_one();
8864 : }
8865 15 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
8866 15 : return 0;
8867 : }
8868 0 : catch (const std::exception &e)
8869 : {
8870 0 : CPLError(CE_Failure, CPLE_AppDefined,
8871 0 : "Cannot start worker thread: %s", e.what());
8872 : }
8873 : }
8874 : else
8875 : {
8876 23 : stopThread();
8877 23 : return 0;
8878 : }
8879 : }
8880 :
8881 4 : stopThread();
8882 : }
8883 :
8884 32 : const auto GetThreadsAvailable = []()
8885 : {
8886 : const char *pszMaxThreads =
8887 32 : CPLGetConfigOption("OGR_GPKG_NUM_THREADS", nullptr);
8888 32 : if (pszMaxThreads == nullptr)
8889 32 : return std::min(4, CPLGetNumCPUs());
8890 0 : else if (EQUAL(pszMaxThreads, "ALL_CPUS"))
8891 0 : return CPLGetNumCPUs();
8892 : else
8893 0 : return atoi(pszMaxThreads);
8894 : };
8895 :
8896 : // Start asynchronous tasks to prefetch the next ArrowArray
8897 211 : if (m_poDS->GetAccess() == GA_ReadOnly &&
8898 89 : m_oQueueArrowArrayPrefetchTasks.empty() &&
8899 89 : m_iNextShapeId + 2 * static_cast<GIntBig>(nMaxBatchSize) <=
8900 89 : m_nTotalFeatureCount &&
8901 227 : sqlite3_threadsafe() != 0 && GetThreadsAvailable() >= 2 &&
8902 16 : CPLGetUsablePhysicalRAM() > 1024 * 1024 * 1024)
8903 : {
8904 16 : const int nMaxTasks = static_cast<int>(std::min<GIntBig>(
8905 16 : DIV_ROUND_UP(m_nTotalFeatureCount - nMaxBatchSize - m_iNextShapeId,
8906 : nMaxBatchSize),
8907 32 : GetThreadsAvailable()));
8908 16 : CPLDebug("GPKG", "Using %d threads", nMaxTasks);
8909 32 : GDALOpenInfo oOpenInfo(m_poDS->GetDescription(), GA_ReadOnly);
8910 16 : oOpenInfo.papszOpenOptions = m_poDS->GetOpenOptions();
8911 16 : oOpenInfo.nOpenFlags = GDAL_OF_VECTOR;
8912 59 : for (int iTask = 0; iTask < nMaxTasks; ++iTask)
8913 : {
8914 43 : auto task = std::make_unique<ArrowArrayPrefetchTask>();
8915 86 : task->m_iStartShapeId =
8916 43 : m_iNextShapeId +
8917 43 : static_cast<GIntBig>(iTask + 1) * nMaxBatchSize;
8918 43 : task->m_poDS = std::make_unique<GDALGeoPackageDataset>();
8919 43 : if (!task->m_poDS->Open(&oOpenInfo, m_poDS->m_osFilenameInZip))
8920 : {
8921 0 : break;
8922 : }
8923 0 : auto poOtherLayer = dynamic_cast<OGRGeoPackageTableLayer *>(
8924 43 : task->m_poDS->GetLayerByName(GetName()));
8925 86 : if (poOtherLayer == nullptr ||
8926 43 : poOtherLayer->GetLayerDefn()->GetFieldCount() !=
8927 43 : m_poFeatureDefn->GetFieldCount())
8928 : {
8929 0 : break;
8930 : }
8931 :
8932 : // Install query logging callback
8933 43 : if (m_poDS->pfnQueryLoggerFunc)
8934 : {
8935 0 : task->m_poDS->SetQueryLoggerFunc(m_poDS->pfnQueryLoggerFunc,
8936 0 : m_poDS->poQueryLoggerArg);
8937 : }
8938 :
8939 43 : task->m_poLayer = poOtherLayer;
8940 43 : task->m_psArrowArray = std::make_unique<struct ArrowArray>();
8941 43 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8942 :
8943 43 : poOtherLayer->m_nTotalFeatureCount = m_nTotalFeatureCount;
8944 : poOtherLayer->m_aosArrowArrayStreamOptions =
8945 43 : m_aosArrowArrayStreamOptions;
8946 43 : OGRLayer *poOtherLayerAsLayer = poOtherLayer;
8947 43 : auto poOtherFDefn = poOtherLayerAsLayer->GetLayerDefn();
8948 86 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
8949 : {
8950 86 : poOtherFDefn->GetGeomFieldDefn(i)->SetIgnored(
8951 43 : m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored());
8952 : }
8953 127 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
8954 : {
8955 168 : poOtherFDefn->GetFieldDefn(i)->SetIgnored(
8956 84 : m_poFeatureDefn->GetFieldDefn(i)->IsIgnored());
8957 : }
8958 :
8959 43 : poOtherLayer->m_iNextShapeId = task->m_iStartShapeId;
8960 :
8961 43 : auto taskPtr = task.get();
8962 462 : auto taskRunner = [taskPtr]()
8963 : {
8964 86 : std::unique_lock oLock(taskPtr->m_oMutex);
8965 15 : do
8966 : {
8967 58 : taskPtr->m_bFetchRows = false;
8968 58 : taskPtr->m_poLayer->GetNextArrowArrayInternal(
8969 58 : taskPtr->m_psArrowArray.get(), taskPtr->m_osErrorMsg,
8970 58 : taskPtr->m_bMemoryLimitReached);
8971 58 : taskPtr->m_bArrayReady = true;
8972 58 : taskPtr->m_oCV.notify_one();
8973 58 : if (taskPtr->m_bMemoryLimitReached)
8974 12 : break;
8975 : // cppcheck-suppress knownConditionTrueFalse
8976 : // Coverity apparently is confused by the fact that we
8977 : // use unique_lock here to guard access for m_bStop whereas
8978 : // in other places we use a lock_guard, but there's nothing
8979 : // wrong.
8980 : // coverity[missing_lock:FALSE]
8981 92 : while (!taskPtr->m_bStop && !taskPtr->m_bFetchRows)
8982 : {
8983 46 : taskPtr->m_oCV.wait(oLock);
8984 : }
8985 46 : } while (!taskPtr->m_bStop);
8986 43 : };
8987 :
8988 43 : task->m_bFetchRows = true;
8989 : try
8990 : {
8991 43 : task->m_oThread = std::thread(taskRunner);
8992 : }
8993 0 : catch (const std::exception &e)
8994 : {
8995 0 : CPLError(CE_Failure, CPLE_AppDefined,
8996 0 : "Cannot start worker thread: %s", e.what());
8997 0 : break;
8998 : }
8999 43 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
9000 : }
9001 : }
9002 :
9003 122 : std::string osErrorMsg;
9004 122 : bool bMemoryLimitReached = false;
9005 : int ret =
9006 122 : GetNextArrowArrayInternal(out_array, osErrorMsg, bMemoryLimitReached);
9007 122 : if (!osErrorMsg.empty())
9008 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
9009 122 : if (bMemoryLimitReached)
9010 : {
9011 1 : CancelAsyncNextArrowArray();
9012 1 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
9013 : }
9014 122 : return ret;
9015 : }
9016 :
9017 : /************************************************************************/
9018 : /* GetNextArrowArrayInternal() */
9019 : /************************************************************************/
9020 :
9021 180 : int OGRGeoPackageTableLayer::GetNextArrowArrayInternal(
9022 : struct ArrowArray *out_array, std::string &osErrorMsg,
9023 : bool &bMemoryLimitReached)
9024 : {
9025 180 : bMemoryLimitReached = false;
9026 180 : memset(out_array, 0, sizeof(*out_array));
9027 :
9028 180 : if (m_iNextShapeId >= m_nTotalFeatureCount)
9029 : {
9030 61 : return 0;
9031 : }
9032 :
9033 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
9034 238 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
9035 119 : if (out_array->release == nullptr)
9036 : {
9037 0 : return ENOMEM;
9038 : }
9039 :
9040 238 : OGRGPKGTableLayerFillArrowArray sFillArrowArray;
9041 119 : sFillArrowArray.psHelper = std::move(psHelper);
9042 119 : sFillArrowArray.bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
9043 : GAS_OPT_DATETIME_AS_STRING, false);
9044 119 : sFillArrowArray.poFeatureDefn = m_poFeatureDefn;
9045 119 : sFillArrowArray.poLayer = this;
9046 119 : sFillArrowArray.hDB = m_poDS->GetDB();
9047 119 : memset(&sFillArrowArray.brokenDown, 0, sizeof(sFillArrowArray.brokenDown));
9048 :
9049 119 : sqlite3_create_function(
9050 119 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
9051 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, &sFillArrowArray, nullptr,
9052 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
9053 :
9054 238 : std::string osSQL;
9055 119 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
9056 119 : int nCountArgs = 1;
9057 :
9058 119 : osSQL += '"';
9059 119 : osSQL += SQLEscapeName(m_pszFidColumn);
9060 119 : osSQL += '"';
9061 119 : ++nCountArgs;
9062 :
9063 236 : if (!sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
9064 117 : sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
9065 : {
9066 116 : osSQL += ',';
9067 116 : osSQL += '"';
9068 116 : osSQL += SQLEscapeName(GetGeometryColumn());
9069 116 : osSQL += '"';
9070 116 : ++nCountArgs;
9071 : }
9072 : const int SQLITE_MAX_FUNCTION_ARG =
9073 119 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
9074 798 : for (int iField = 0; iField < sFillArrowArray.psHelper->m_nFieldCount;
9075 : iField++)
9076 : {
9077 : const int iArrowField =
9078 679 : sFillArrowArray.psHelper->m_mapOGRFieldToArrowField[iField];
9079 679 : if (iArrowField >= 0)
9080 : {
9081 664 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
9082 : {
9083 : // We cannot pass more than SQLITE_MAX_FUNCTION_ARG args
9084 : // to a function... So we have to split in several calls...
9085 3 : osSQL += "), OGR_GPKG_FillArrowArray_INTERNAL(";
9086 3 : osSQL += CPLSPrintf("%d", iField);
9087 3 : nCountArgs = 1;
9088 : }
9089 : const OGRFieldDefn *poFieldDefn =
9090 664 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
9091 664 : osSQL += ',';
9092 664 : osSQL += '"';
9093 664 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
9094 664 : osSQL += '"';
9095 664 : ++nCountArgs;
9096 : }
9097 : }
9098 119 : osSQL += ") FROM \"";
9099 119 : osSQL += SQLEscapeName(m_pszTableName);
9100 119 : osSQL += "\" WHERE \"";
9101 119 : osSQL += SQLEscapeName(m_pszFidColumn);
9102 119 : osSQL += "\" BETWEEN ";
9103 119 : osSQL += std::to_string(m_iNextShapeId + 1);
9104 119 : osSQL += " AND ";
9105 119 : osSQL += std::to_string(m_iNextShapeId +
9106 119 : sFillArrowArray.psHelper->m_nMaxBatchSize);
9107 :
9108 : // CPLDebug("GPKG", "%s", osSQL.c_str());
9109 :
9110 119 : char *pszErrMsg = nullptr;
9111 119 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
9112 119 : &pszErrMsg) != SQLITE_OK)
9113 : {
9114 13 : if (!sFillArrowArray.bErrorOccurred &&
9115 13 : !sFillArrowArray.bMemoryLimitReached)
9116 : {
9117 0 : osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
9118 : }
9119 : }
9120 119 : sqlite3_free(pszErrMsg);
9121 :
9122 119 : bMemoryLimitReached = sFillArrowArray.bMemoryLimitReached;
9123 :
9124 : // Delete function
9125 119 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
9126 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
9127 : nullptr, nullptr, nullptr);
9128 :
9129 119 : if (sFillArrowArray.bErrorOccurred)
9130 : {
9131 0 : sFillArrowArray.psHelper->ClearArray();
9132 0 : return ENOMEM;
9133 : }
9134 :
9135 119 : sFillArrowArray.psHelper->Shrink(sFillArrowArray.nCountRows);
9136 119 : if (sFillArrowArray.nCountRows == 0)
9137 : {
9138 0 : sFillArrowArray.psHelper->ClearArray();
9139 : }
9140 :
9141 119 : m_iNextShapeId += sFillArrowArray.nCountRows;
9142 :
9143 119 : return 0;
9144 : }
9145 :
9146 : /************************************************************************/
9147 : /* OGR_GPKG_GeometryExtent3DAggregate() */
9148 : /************************************************************************/
9149 :
9150 : namespace
9151 : {
9152 : struct GeometryExtent3DAggregateContext
9153 : {
9154 : sqlite3 *m_hDB = nullptr;
9155 : OGREnvelope3D m_oExtent3D;
9156 :
9157 18 : explicit GeometryExtent3DAggregateContext(sqlite3 *hDB)
9158 18 : : m_hDB(hDB), m_oExtent3D()
9159 : {
9160 18 : }
9161 :
9162 : GeometryExtent3DAggregateContext(const GeometryExtent3DAggregateContext &) =
9163 : delete;
9164 : GeometryExtent3DAggregateContext &
9165 : operator=(const GeometryExtent3DAggregateContext &) = delete;
9166 : };
9167 :
9168 : } // namespace
9169 :
9170 26 : static void OGR_GPKG_GeometryExtent3DAggregate_Step(sqlite3_context *pContext,
9171 : int /*argc*/,
9172 : sqlite3_value **argv)
9173 : {
9174 : const GByte *pabyBLOB =
9175 26 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
9176 :
9177 : auto poContext = static_cast<GeometryExtent3DAggregateContext *>(
9178 26 : sqlite3_user_data(pContext));
9179 :
9180 26 : if (pabyBLOB != nullptr)
9181 : {
9182 : GPkgHeader sHeader;
9183 26 : if (OGRGeoPackageGetHeader(pContext, 0, argv, &sHeader, true, true))
9184 : {
9185 26 : OGREnvelope3D extent3D;
9186 26 : extent3D.MinX = sHeader.MinX;
9187 26 : extent3D.MaxX = sHeader.MaxX;
9188 26 : extent3D.MinY = sHeader.MinY;
9189 26 : extent3D.MaxY = sHeader.MaxY;
9190 26 : extent3D.MinZ = sHeader.MinZ;
9191 26 : extent3D.MaxZ = sHeader.MaxZ;
9192 26 : poContext->m_oExtent3D.Merge(extent3D);
9193 : }
9194 0 : else if (!sHeader.bEmpty)
9195 : {
9196 : // Try also spatialite geometry blobs
9197 0 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
9198 0 : OGRGeometry *poGeom = nullptr;
9199 0 : if (OGRSQLiteImportSpatiaLiteGeometry(pabyBLOB, nBLOBLen,
9200 0 : &poGeom) == OGRERR_NONE &&
9201 0 : poGeom && !poGeom->IsEmpty())
9202 : {
9203 0 : OGREnvelope3D extent3D;
9204 0 : poGeom->getEnvelope(&extent3D);
9205 0 : poContext->m_oExtent3D.Merge(extent3D);
9206 : }
9207 0 : delete poGeom;
9208 : }
9209 : }
9210 26 : }
9211 :
9212 18 : static void OGR_GPKG_GeometryExtent3DAggregate_Finalize(sqlite3_context *)
9213 : {
9214 18 : }
9215 :
9216 : /************************************************************************/
9217 : /* IGetExtent3D */
9218 : /************************************************************************/
9219 20 : OGRErr OGRGeoPackageTableLayer::IGetExtent3D(int iGeomField,
9220 : OGREnvelope3D *psExtent3D,
9221 : bool bForce)
9222 : {
9223 :
9224 20 : const OGRFeatureDefn *poDefn = GetLayerDefn();
9225 :
9226 : /* -------------------------------------------------------------------- */
9227 : /* Deferred actions, reset state. */
9228 : /* -------------------------------------------------------------------- */
9229 20 : RunDeferredCreationIfNecessary();
9230 20 : if (!RunDeferredSpatialIndexUpdate())
9231 : {
9232 0 : return OGRERR_FAILURE;
9233 : }
9234 :
9235 20 : if (m_nZFlag == 0 && m_soFilter.empty())
9236 : {
9237 : // If the layer doesn't contain any 3D geometry and no filter is set,
9238 : // we can fallback to the fast 2D GetExtent()
9239 2 : const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
9240 2 : psExtent3D->MinZ = std::numeric_limits<double>::infinity();
9241 2 : psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
9242 2 : return retVal;
9243 : }
9244 : else
9245 : {
9246 18 : *psExtent3D = OGREnvelope3D();
9247 : }
9248 :
9249 : // For internal use only
9250 :
9251 18 : GeometryExtent3DAggregateContext sContext(m_poDS->hDB);
9252 :
9253 36 : CPLString osFuncName;
9254 : osFuncName.Printf("OGR_GPKG_GeometryExtent3DAggregate_INTERNAL_%p",
9255 18 : &sContext);
9256 :
9257 18 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
9258 : &sContext, nullptr,
9259 : OGR_GPKG_GeometryExtent3DAggregate_Step,
9260 : OGR_GPKG_GeometryExtent3DAggregate_Finalize);
9261 :
9262 36 : char *pszSQL = sqlite3_mprintf(
9263 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
9264 18 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
9265 45 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
9266 18 : char *pszErrMsg = nullptr;
9267 : const int rc =
9268 18 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
9269 :
9270 : // Delete function
9271 18 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
9272 : nullptr, nullptr, nullptr, nullptr);
9273 :
9274 18 : if (rc != SQLITE_OK)
9275 : {
9276 0 : if (rc != SQLITE_INTERRUPT)
9277 : {
9278 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
9279 : pszSQL, pszErrMsg);
9280 : }
9281 0 : sqlite3_free(pszErrMsg);
9282 0 : sqlite3_free(pszSQL);
9283 0 : return OGRERR_FAILURE;
9284 : }
9285 18 : sqlite3_free(pszErrMsg);
9286 18 : sqlite3_free(pszSQL);
9287 :
9288 18 : *psExtent3D = sContext.m_oExtent3D;
9289 :
9290 18 : return OGRERR_NONE;
9291 : }
9292 :
9293 : /************************************************************************/
9294 : /* Truncate() */
9295 : /************************************************************************/
9296 :
9297 : /** Implements "DELETE FROM {table_name}" in an optimized way.
9298 : *
9299 : * Disable triggers if we detect that the only triggers on the table are ones
9300 : * under our control (i.e. the ones for the gpkg_ogr_contents table and the
9301 : * ones updating the RTree)
9302 : * And even if we cannot disable triggers, truncate the RTree before the main
9303 : * table, as this dramatically speeds up truncating the main table.
9304 : */
9305 8 : OGRErr OGRGeoPackageTableLayer::Truncate()
9306 : {
9307 8 : if (!m_poDS->GetUpdate())
9308 : {
9309 2 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
9310 : "Truncate");
9311 2 : return OGRERR_FAILURE;
9312 : }
9313 :
9314 6 : ResetReading();
9315 6 : SyncToDisk();
9316 :
9317 6 : bool bOK = (m_poDS->SoftStartTransaction() == OGRERR_NONE);
9318 :
9319 : struct ReenableTriggers
9320 : {
9321 : sqlite3 *m_hDB = nullptr;
9322 :
9323 4 : explicit ReenableTriggers(sqlite3 *hDB) : m_hDB(hDB)
9324 : {
9325 4 : }
9326 :
9327 4 : ~ReenableTriggers()
9328 4 : {
9329 4 : sqlite3_db_config(m_hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 1,
9330 : nullptr);
9331 4 : }
9332 : CPL_DISALLOW_COPY_ASSIGN(ReenableTriggers)
9333 : };
9334 :
9335 : // to keep in top level scope!
9336 0 : std::unique_ptr<ReenableTriggers> reenableTriggers;
9337 :
9338 : // Temporarily disable triggers for greater speed if we detect that the
9339 : // only triggers on the table are the RTree ones and the ones for the
9340 : // gpkg_ogr_contents table
9341 6 : if (bOK && m_bIsTable)
9342 : {
9343 6 : char *pszSQL = sqlite3_mprintf(
9344 : "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' "
9345 : "AND tbl_name = '%q' "
9346 : "AND name NOT IN ('trigger_insert_feature_count_%q',"
9347 : "'trigger_delete_feature_count_%q') "
9348 : "AND name NOT LIKE 'rtree_%q_%%'",
9349 : m_pszTableName, m_pszTableName, m_pszTableName, m_pszTableName);
9350 6 : OGRErr eErr = OGRERR_NONE;
9351 10 : if (SQLGetInteger(m_poDS->GetDB(), pszSQL, &eErr) == 0 &&
9352 4 : eErr == OGRERR_NONE)
9353 : {
9354 4 : int nEnableTriggerOldVal = -1;
9355 4 : sqlite3_db_config(m_poDS->GetDB(), SQLITE_DBCONFIG_ENABLE_TRIGGER,
9356 : -1, &nEnableTriggerOldVal);
9357 4 : if (nEnableTriggerOldVal == 1)
9358 : {
9359 4 : int nNewVal = -1;
9360 4 : sqlite3_db_config(m_poDS->GetDB(),
9361 : SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &nNewVal);
9362 4 : if (nNewVal == 0)
9363 : {
9364 4 : CPLDebugOnly("GPKG",
9365 : "Disabling triggers during truncation of %s",
9366 : m_pszTableName);
9367 : reenableTriggers =
9368 4 : std::make_unique<ReenableTriggers>(m_poDS->GetDB());
9369 4 : CPL_IGNORE_RET_VAL(reenableTriggers); // to please cppcheck
9370 : }
9371 : }
9372 : }
9373 6 : sqlite3_free(pszSQL);
9374 : }
9375 :
9376 6 : char *pszErrMsg = nullptr;
9377 6 : if (bOK && m_bIsTable && HasSpatialIndex())
9378 : {
9379 : // Manually clean the 3 tables that are used by the RTree:
9380 : // - rtree_{tablename}_{geom}_node: all rows, but nodeno = 1 for which
9381 : // we reset the 'data' field to a zero blob of the same size
9382 : // - rtree_{tablename}_{geom}_parent: all rows
9383 : // - rtree_{tablename}_{geom}_rowid: all rows
9384 :
9385 2 : const char *pszT = m_pszTableName;
9386 2 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
9387 :
9388 2 : m_osRTreeName = "rtree_";
9389 2 : m_osRTreeName += pszT;
9390 2 : m_osRTreeName += "_";
9391 2 : m_osRTreeName += pszC;
9392 :
9393 : {
9394 : char *pszSQL =
9395 2 : sqlite3_mprintf("DELETE FROM \"%w_node\" WHERE nodeno > 1;"
9396 : "DELETE FROM \"%w_parent\"; "
9397 : "DELETE FROM \"%w_rowid\"",
9398 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
9399 : m_osRTreeName.c_str());
9400 2 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9401 : &pszErrMsg) == SQLITE_OK;
9402 2 : sqlite3_free(pszSQL);
9403 : }
9404 :
9405 2 : if (bOK)
9406 : {
9407 2 : char *pszSQL = sqlite3_mprintf(
9408 : "SELECT length(data) FROM \"%w_node\" WHERE nodeno = 1",
9409 : m_osRTreeName.c_str());
9410 : const int nBlobSize =
9411 2 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr);
9412 2 : sqlite3_free(pszSQL);
9413 :
9414 2 : pszSQL = sqlite3_mprintf(
9415 : "UPDATE \"%w_node\" SET data = zeroblob(%d) WHERE nodeno = 1",
9416 : m_osRTreeName.c_str(), nBlobSize);
9417 2 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9418 : &pszErrMsg) == SQLITE_OK;
9419 2 : sqlite3_free(pszSQL);
9420 : }
9421 : }
9422 :
9423 6 : if (bOK)
9424 : {
9425 : // Truncate main table
9426 6 : char *pszSQL = sqlite3_mprintf("DELETE FROM \"%w\"", m_pszTableName);
9427 6 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9428 : SQLITE_OK;
9429 6 : sqlite3_free(pszSQL);
9430 : }
9431 :
9432 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9433 : // Reset feature count
9434 6 : if (bOK && m_poDS->m_bHasGPKGOGRContents)
9435 : {
9436 : char *pszSQL =
9437 5 : sqlite3_mprintf("UPDATE gpkg_ogr_contents SET feature_count = 0 "
9438 : "WHERE lower(table_name) = lower('%q')",
9439 : m_pszTableName);
9440 5 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9441 : SQLITE_OK;
9442 5 : sqlite3_free(pszSQL);
9443 : }
9444 :
9445 6 : if (bOK)
9446 : {
9447 5 : m_nTotalFeatureCount = 0;
9448 : }
9449 : #endif
9450 :
9451 6 : if (bOK)
9452 : {
9453 5 : m_poDS->SoftCommitTransaction();
9454 : }
9455 : else
9456 : {
9457 1 : m_poDS->SoftRollbackTransaction();
9458 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9459 1 : DisableFeatureCount();
9460 : #endif
9461 1 : CPLError(CE_Failure, CPLE_AppDefined, "Truncate(%s) failed: %s",
9462 1 : m_pszTableName, pszErrMsg ? pszErrMsg : "(unknown reason)");
9463 : }
9464 6 : sqlite3_free(pszErrMsg);
9465 :
9466 6 : return bOK ? OGRERR_NONE : OGRERR_FAILURE;
9467 : }
|