Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoPackage Translator
4 : * Purpose: Implements OGRGeoPackageTableLayer class
5 : * Author: Paul Ramsey <pramsey@boundlessgeo.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
9 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_geopackage.h"
15 : #include "ogrgeopackageutility.h"
16 : #include "ogrlayerarrow.h"
17 : #include "ogrsqliteutility.h"
18 : #include "cpl_md5.h"
19 : #include "cpl_time.h"
20 : #include "ogr_p.h"
21 : #include "sqlite_rtree_bulk_load/wrapper.h"
22 : #include "gdal_priv_templates.hpp"
23 :
24 : #include <algorithm>
25 : #include <cassert>
26 : #include <cmath>
27 : #include <limits>
28 :
29 : #undef SQLITE_STATIC
30 : #define SQLITE_STATIC static_cast<sqlite3_destructor_type>(nullptr)
31 : #undef SQLITE_TRANSIENT
32 : #define SQLITE_TRANSIENT reinterpret_cast<sqlite3_destructor_type>(-1)
33 :
34 : static const char UNSUPPORTED_OP_READ_ONLY[] =
35 : "%s : unsupported operation on a read-only datasource.";
36 :
37 : //----------------------------------------------------------------------
38 : // SaveExtent()
39 : //
40 : // Write the current contents of the layer envelope down to the
41 : // gpkg_contents metadata table.
42 : //
43 4191 : OGRErr OGRGeoPackageTableLayer::SaveExtent()
44 : {
45 4191 : if (!m_poDS->GetUpdate() || !m_bExtentChanged || !m_poExtent)
46 3728 : return OGRERR_NONE;
47 :
48 463 : sqlite3 *poDb = m_poDS->GetDB();
49 :
50 463 : if (!poDb)
51 0 : return OGRERR_FAILURE;
52 :
53 : char *pszSQL =
54 926 : sqlite3_mprintf("UPDATE gpkg_contents SET "
55 : "min_x = %.17g, min_y = %.17g, "
56 : "max_x = %.17g, max_y = %.17g "
57 : "WHERE lower(table_name) = lower('%q') AND "
58 : "Lower(data_type) = 'features'",
59 463 : m_poExtent->MinX, m_poExtent->MinY, m_poExtent->MaxX,
60 463 : m_poExtent->MaxY, m_pszTableName);
61 :
62 463 : OGRErr err = SQLCommand(poDb, pszSQL);
63 463 : sqlite3_free(pszSQL);
64 463 : m_bExtentChanged = false;
65 :
66 463 : return err;
67 : }
68 :
69 : //----------------------------------------------------------------------
70 : // SaveTimestamp()
71 : //
72 : // Update the last_change column of the gpkg_contents metadata table.
73 : //
74 4186 : OGRErr OGRGeoPackageTableLayer::SaveTimestamp()
75 : {
76 4186 : if (!m_poDS->GetUpdate() || !m_bContentChanged)
77 3612 : return OGRERR_NONE;
78 :
79 574 : m_bContentChanged = false;
80 :
81 574 : OGRErr err = m_poDS->UpdateGpkgContentsLastChange(m_pszTableName);
82 :
83 : #ifdef ENABLE_GPKG_OGR_CONTENTS
84 574 : if (m_bIsTable && err == OGRERR_NONE && m_poDS->m_bHasGPKGOGRContents &&
85 567 : !m_bOGRFeatureCountTriggersEnabled && m_nTotalFeatureCount >= 0)
86 : {
87 1040 : CPLString osFeatureCount;
88 520 : osFeatureCount.Printf(CPL_FRMT_GIB, m_nTotalFeatureCount);
89 520 : char *pszSQL = sqlite3_mprintf("UPDATE gpkg_ogr_contents SET "
90 : "feature_count = %s "
91 : "WHERE lower(table_name) = lower('%q')",
92 : osFeatureCount.c_str(), m_pszTableName);
93 520 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
94 520 : sqlite3_free(pszSQL);
95 : }
96 : #endif
97 :
98 574 : return err;
99 : }
100 :
101 : //----------------------------------------------------------------------
102 : // UpdateExtent()
103 : //
104 : // Expand the layer envelope if necessary to reflect the bounds
105 : // of new features being added to the layer.
106 : //
107 250808 : OGRErr OGRGeoPackageTableLayer::UpdateExtent(const OGREnvelope *poExtent)
108 : {
109 250808 : if (!m_poExtent)
110 : {
111 429 : m_poExtent = new OGREnvelope(*poExtent);
112 : }
113 250808 : m_poExtent->Merge(*poExtent);
114 250808 : m_bExtentChanged = true;
115 250808 : return OGRERR_NONE;
116 : }
117 :
118 : //----------------------------------------------------------------------
119 : // BuildColumns()
120 : //
121 : // Save a list of columns (fid, geometry, attributes) suitable
122 : // for use in a SELECT query that retrieves all fields.
123 : //
124 25375 : OGRErr OGRGeoPackageTableLayer::BuildColumns()
125 : {
126 25375 : m_anFieldOrdinals.resize(m_poFeatureDefn->GetFieldCount());
127 25375 : int iCurCol = 0;
128 :
129 : /* Always start with a primary key */
130 25375 : CPLString soColumns;
131 25375 : if (m_bIsTable || m_pszFidColumn != nullptr)
132 : {
133 25361 : soColumns += "m.";
134 25361 : soColumns += m_pszFidColumn
135 50722 : ? "\"" + SQLEscapeName(m_pszFidColumn) + "\""
136 25361 : : "_rowid_";
137 25361 : m_iFIDCol = iCurCol;
138 25361 : iCurCol++;
139 : }
140 :
141 : /* Add a geometry column if there is one (just one) */
142 25375 : if (m_poFeatureDefn->GetGeomFieldCount())
143 : {
144 25200 : const auto poFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
145 25200 : if (poFieldDefn->IsIgnored())
146 : {
147 10 : m_iGeomCol = -1;
148 : }
149 : else
150 : {
151 25190 : if (!soColumns.empty())
152 25179 : soColumns += ", ";
153 25190 : soColumns += "m.\"";
154 25190 : soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
155 25190 : soColumns += "\"";
156 25190 : m_iGeomCol = iCurCol;
157 25190 : iCurCol++;
158 : }
159 : }
160 :
161 : /* Add all the attribute columns */
162 34154 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
163 : {
164 8779 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
165 8779 : if (poFieldDefn->IsIgnored())
166 : {
167 28 : m_anFieldOrdinals[i] = -1;
168 : }
169 : else
170 : {
171 8751 : if (!soColumns.empty())
172 8750 : soColumns += ", ";
173 8751 : soColumns += "m.\"";
174 8751 : soColumns += SQLEscapeName(poFieldDefn->GetNameRef());
175 8751 : soColumns += "\"";
176 8751 : m_anFieldOrdinals[i] = iCurCol;
177 8751 : iCurCol++;
178 : }
179 : }
180 :
181 25375 : if (soColumns.empty())
182 : {
183 : // Can happen if ignoring all fields on a view...
184 2 : soColumns = "NULL";
185 : }
186 25375 : m_soColumns = std::move(soColumns);
187 50750 : return OGRERR_NONE;
188 : }
189 :
190 : //----------------------------------------------------------------------
191 : // IsGeomFieldSet()
192 : //
193 : // Utility method to determine if there is a non-Null geometry
194 : // in an OGRGeometry.
195 : //
196 253211 : bool OGRGeoPackageTableLayer::IsGeomFieldSet(OGRFeature *poFeature)
197 : {
198 506345 : return poFeature->GetDefnRef()->GetGeomFieldCount() &&
199 506345 : poFeature->GetGeomFieldRef(0);
200 : }
201 :
202 253231 : OGRErr OGRGeoPackageTableLayer::FeatureBindParameters(
203 : OGRFeature *poFeature, sqlite3_stmt *poStmt, int *pnColCount, bool bAddFID,
204 : bool bBindUnsetFields, int nUpdatedFieldsCount,
205 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
206 : const int * /*panUpdatedGeomFieldsIdx*/)
207 : {
208 253231 : OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
209 :
210 253231 : int nColCount = 1;
211 253231 : if (bAddFID)
212 : {
213 62302 : int err = sqlite3_bind_int64(poStmt, nColCount++, poFeature->GetFID());
214 62302 : if (err != SQLITE_OK)
215 : {
216 0 : CPLError(CE_Failure, CPLE_AppDefined,
217 : "sqlite3_bind_int64() failed");
218 0 : return OGRERR_FAILURE;
219 : }
220 : }
221 :
222 : // Bind data values to the statement, here bind the blob for geometry.
223 : // We bind only if there's a geometry column (poFeatureDefn->GetGeomFieldCount() > 0)
224 : // and if we are:
225 : // - either in CreateFeature/SetFeature mode: nUpdatedGeomFieldsCount < 0
226 : // - or in UpdateFeature mode with nUpdatedGeomFieldsCount == 1, which
227 : // implicitly involves that panUpdatedGeomFieldsIdx[0] == 0, so we don't
228 : // need to test this condition.
229 506454 : if ((nUpdatedGeomFieldsCount < 0 || nUpdatedGeomFieldsCount == 1) &&
230 253223 : poFeatureDefn->GetGeomFieldCount())
231 : {
232 : // Non-NULL geometry.
233 253144 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
234 253144 : if (poGeom)
235 : {
236 251840 : size_t szWkb = 0;
237 503680 : GByte *pabyWkb = GPkgGeometryFromOGR(poGeom, m_iSrs,
238 251840 : &m_sBinaryPrecision, &szWkb);
239 251840 : if (!pabyWkb)
240 0 : return OGRERR_FAILURE;
241 251840 : int err = sqlite3_bind_blob(poStmt, nColCount++, pabyWkb,
242 : static_cast<int>(szWkb), CPLFree);
243 251840 : if (err != SQLITE_OK)
244 : {
245 0 : if (err == SQLITE_TOOBIG)
246 : {
247 0 : CPLError(CE_Failure, CPLE_AppDefined,
248 : "sqlite3_bind_blob() failed: too big");
249 : }
250 : else
251 : {
252 0 : CPLError(CE_Failure, CPLE_AppDefined,
253 : "sqlite3_bind_blob() failed");
254 : }
255 0 : return OGRERR_FAILURE;
256 : }
257 251840 : CreateGeometryExtensionIfNecessary(poGeom);
258 : }
259 : /* NULL geometry */
260 : else
261 : {
262 1304 : int err = sqlite3_bind_null(poStmt, nColCount++);
263 1304 : if (err != SQLITE_OK)
264 : {
265 0 : CPLError(CE_Failure, CPLE_AppDefined,
266 : "sqlite3_bind_null() failed");
267 0 : return OGRERR_FAILURE;
268 : }
269 : }
270 : }
271 :
272 : /* Bind the attributes using appropriate SQLite data types */
273 253231 : const int nFieldCount = poFeatureDefn->GetFieldCount();
274 :
275 253231 : size_t nInsertionBufferPos = 0;
276 253231 : if (m_osInsertionBuffer.empty())
277 28854 : m_osInsertionBuffer.resize(OGR_SIZEOF_ISO8601_DATETIME_BUFFER *
278 : nFieldCount);
279 :
280 4662160 : for (int idx = 0;
281 4662160 : idx < (nUpdatedFieldsCount < 0 ? nFieldCount : nUpdatedFieldsCount);
282 : idx++)
283 : {
284 4408930 : const int iField =
285 4408930 : nUpdatedFieldsCount < 0 ? idx : panUpdatedFieldsIdx[idx];
286 4408930 : assert(iField >= 0);
287 8817840 : if (iField == m_iFIDAsRegularColumnIndex ||
288 8817840 : m_abGeneratedColumns[iField])
289 27 : continue;
290 4408900 : if (!poFeature->IsFieldSetUnsafe(iField))
291 : {
292 1094 : if (bBindUnsetFields)
293 : {
294 1080 : int err = sqlite3_bind_null(poStmt, nColCount++);
295 1080 : if (err != SQLITE_OK)
296 : {
297 0 : CPLError(CE_Failure, CPLE_AppDefined,
298 : "sqlite3_bind_null() failed");
299 0 : return OGRERR_FAILURE;
300 : }
301 : }
302 1094 : continue;
303 : }
304 :
305 : const OGRFieldDefn *poFieldDefn =
306 4407810 : poFeatureDefn->GetFieldDefnUnsafe(iField);
307 4407810 : int err = SQLITE_OK;
308 :
309 4407810 : if (!poFeature->IsFieldNullUnsafe(iField))
310 : {
311 4407770 : const auto eType = poFieldDefn->GetType();
312 4407770 : switch (eType)
313 : {
314 2562 : case OFTInteger:
315 : {
316 2562 : err = sqlite3_bind_int(
317 : poStmt, nColCount++,
318 : poFeature->GetFieldAsIntegerUnsafe(iField));
319 2562 : break;
320 : }
321 496 : case OFTInteger64:
322 : {
323 496 : err = sqlite3_bind_int64(
324 : poStmt, nColCount++,
325 : poFeature->GetFieldAsInteger64Unsafe(iField));
326 496 : break;
327 : }
328 711 : case OFTReal:
329 : {
330 711 : err = sqlite3_bind_double(
331 : poStmt, nColCount++,
332 : poFeature->GetFieldAsDoubleUnsafe(iField));
333 711 : break;
334 : }
335 2311 : case OFTBinary:
336 : {
337 2311 : int szBlob = 0;
338 : GByte *pabyBlob =
339 2311 : poFeature->GetFieldAsBinary(iField, &szBlob);
340 2311 : err = sqlite3_bind_blob(poStmt, nColCount++, pabyBlob,
341 : szBlob, SQLITE_STATIC);
342 2311 : break;
343 : }
344 4401690 : default:
345 : {
346 4401690 : const char *pszVal = "";
347 4401690 : CPL_IGNORE_RET_VAL(pszVal); // Make CSA happy
348 4401690 : int nValLengthBytes = -1;
349 4401690 : sqlite3_destructor_type destructorType = SQLITE_TRANSIENT;
350 4401690 : if (eType == OFTDate)
351 : {
352 141 : destructorType = SQLITE_STATIC;
353 : const auto psFieldRaw =
354 141 : poFeature->GetRawFieldRef(iField);
355 : char *pszValEdit =
356 141 : &m_osInsertionBuffer[nInsertionBufferPos];
357 141 : pszVal = pszValEdit;
358 141 : if (psFieldRaw->Date.Year < 0 ||
359 141 : psFieldRaw->Date.Year >= 10000)
360 : {
361 0 : CPLError(
362 : CE_Failure, CPLE_AppDefined,
363 : "OGRGetISO8601DateTime(): year %d unsupported ",
364 0 : psFieldRaw->Date.Year);
365 0 : nValLengthBytes = 0;
366 : }
367 : else
368 : {
369 141 : int nYear = psFieldRaw->Date.Year;
370 141 : pszValEdit[3] = (nYear % 10) + '0';
371 141 : nYear /= 10;
372 141 : pszValEdit[2] = (nYear % 10) + '0';
373 141 : nYear /= 10;
374 141 : pszValEdit[1] = (nYear % 10) + '0';
375 141 : nYear /= 10;
376 141 : pszValEdit[0] =
377 141 : static_cast<char>(nYear /*% 10*/ + '0');
378 141 : pszValEdit[4] = '-';
379 141 : pszValEdit[5] =
380 141 : ((psFieldRaw->Date.Month / 10) % 10) + '0';
381 141 : pszValEdit[6] = (psFieldRaw->Date.Month % 10) + '0';
382 141 : pszValEdit[7] = '-';
383 141 : pszValEdit[8] =
384 141 : ((psFieldRaw->Date.Day / 10) % 10) + '0';
385 141 : pszValEdit[9] = (psFieldRaw->Date.Day % 10) + '0';
386 141 : nValLengthBytes = 10;
387 141 : nInsertionBufferPos += 10;
388 : }
389 : }
390 4401550 : else if (eType == OFTDateTime)
391 : {
392 166 : destructorType = SQLITE_STATIC;
393 : const auto psFieldRaw =
394 166 : poFeature->GetRawFieldRef(iField);
395 : char *pszValEdit =
396 166 : &m_osInsertionBuffer[nInsertionBufferPos];
397 166 : pszVal = pszValEdit;
398 166 : if (m_poDS->m_bDateTimeWithTZ ||
399 3 : psFieldRaw->Date.TZFlag == 100)
400 : {
401 164 : nValLengthBytes = OGRGetISO8601DateTime(
402 164 : psFieldRaw, m_sDateTimeFormat, pszValEdit);
403 : }
404 : else
405 : {
406 2 : OGRField sField(*psFieldRaw);
407 2 : if (sField.Date.TZFlag == 0 ||
408 1 : sField.Date.TZFlag == 1)
409 : {
410 1 : sField.Date.TZFlag = 100;
411 : }
412 : else
413 : {
414 : struct tm brokendowntime;
415 1 : brokendowntime.tm_year =
416 1 : sField.Date.Year - 1900;
417 1 : brokendowntime.tm_mon = sField.Date.Month - 1;
418 1 : brokendowntime.tm_mday = sField.Date.Day;
419 1 : brokendowntime.tm_hour = sField.Date.Hour;
420 1 : brokendowntime.tm_min = sField.Date.Minute;
421 1 : brokendowntime.tm_sec = 0;
422 : GIntBig nDT =
423 1 : CPLYMDHMSToUnixTime(&brokendowntime);
424 1 : const int TZOffset =
425 1 : std::abs(sField.Date.TZFlag - 100) * 15;
426 1 : nDT -= TZOffset * 60;
427 1 : CPLUnixTimeToYMDHMS(nDT, &brokendowntime);
428 1 : sField.Date.Year = static_cast<GInt16>(
429 1 : brokendowntime.tm_year + 1900);
430 1 : sField.Date.Month = static_cast<GByte>(
431 1 : brokendowntime.tm_mon + 1);
432 1 : sField.Date.Day =
433 1 : static_cast<GByte>(brokendowntime.tm_mday);
434 1 : sField.Date.Hour =
435 1 : static_cast<GByte>(brokendowntime.tm_hour);
436 1 : sField.Date.Minute =
437 1 : static_cast<GByte>(brokendowntime.tm_min);
438 1 : sField.Date.TZFlag = 100;
439 : }
440 :
441 2 : nValLengthBytes = OGRGetISO8601DateTime(
442 2 : &sField, m_sDateTimeFormat, pszValEdit);
443 : }
444 166 : nInsertionBufferPos += nValLengthBytes;
445 : }
446 4401390 : else if (eType == OFTString)
447 : {
448 4401390 : pszVal = poFeature->GetFieldAsStringUnsafe(iField);
449 4401390 : if (poFieldDefn->GetWidth() > 0)
450 : {
451 419 : if (!CPLIsUTF8(pszVal, -1))
452 : {
453 4 : CPLError(CE_Warning, CPLE_AppDefined,
454 : "Value of field '%s' is not a valid "
455 : "UTF-8 string.%s",
456 2 : poFeatureDefn->GetFieldDefn(iField)
457 : ->GetNameRef(),
458 2 : m_bTruncateFields
459 : ? " Value will be laundered."
460 : : "");
461 2 : if (m_bTruncateFields)
462 : {
463 1 : pszVal = CPLForceToASCII(pszVal, -1, '_');
464 1 : destructorType = CPLFree;
465 : }
466 : }
467 :
468 419 : if (CPLStrlenUTF8(pszVal) > poFieldDefn->GetWidth())
469 : {
470 4 : CPLError(
471 : CE_Warning, CPLE_AppDefined,
472 : "Value of field '%s' has %d characters, "
473 : "whereas maximum allowed is %d.%s",
474 2 : poFeatureDefn->GetFieldDefn(iField)
475 : ->GetNameRef(),
476 : CPLStrlenUTF8(pszVal),
477 : poFieldDefn->GetWidth(),
478 2 : m_bTruncateFields
479 : ? " Value will be truncated."
480 : : "");
481 2 : if (m_bTruncateFields)
482 : {
483 1 : int countUTF8Chars = 0;
484 1 : nValLengthBytes = 0;
485 3 : while (pszVal[nValLengthBytes])
486 : {
487 3 : if ((pszVal[nValLengthBytes] & 0xc0) !=
488 : 0x80)
489 : {
490 : // Stop at the start of the
491 : // character just beyond the maximum
492 : // accepted
493 3 : if (countUTF8Chars ==
494 3 : poFieldDefn->GetWidth())
495 1 : break;
496 2 : countUTF8Chars++;
497 : }
498 2 : nValLengthBytes++;
499 : }
500 : }
501 : }
502 : }
503 : else
504 : {
505 4400970 : destructorType = SQLITE_STATIC;
506 : }
507 : }
508 : else
509 : {
510 0 : pszVal = poFeature->GetFieldAsString(iField);
511 : }
512 :
513 4401690 : err = sqlite3_bind_text(poStmt, nColCount++, pszVal,
514 : nValLengthBytes, destructorType);
515 4401690 : break;
516 : }
517 : }
518 : }
519 : else
520 : {
521 36 : err = sqlite3_bind_null(poStmt, nColCount++);
522 : }
523 4407810 : if (err != SQLITE_OK)
524 : {
525 0 : CPLError(CE_Failure, CPLE_AppDefined,
526 : "sqlite3_bind_() for column %s failed: %s",
527 : poFieldDefn->GetNameRef(),
528 0 : sqlite3_errmsg(m_poDS->GetDB()));
529 0 : return OGRERR_FAILURE;
530 : }
531 : }
532 :
533 253231 : if (pnColCount != nullptr)
534 65 : *pnColCount = nColCount;
535 253231 : return OGRERR_NONE;
536 : }
537 :
538 : //----------------------------------------------------------------------
539 : // FeatureBindUpdateParameters()
540 : //
541 : // Selectively bind the values of an OGRFeature to a prepared
542 : // statement, prior to execution. Carefully binds exactly the
543 : // same parameters that have been set up by FeatureGenerateUpdateSQL()
544 : // as bindable.
545 : //
546 : OGRErr
547 55 : OGRGeoPackageTableLayer::FeatureBindUpdateParameters(OGRFeature *poFeature,
548 : sqlite3_stmt *poStmt)
549 : {
550 :
551 55 : int nColCount = 0;
552 55 : const OGRErr err = FeatureBindParameters(
553 : poFeature, poStmt, &nColCount, false, false, -1, nullptr, -1, nullptr);
554 55 : if (err != OGRERR_NONE)
555 0 : return err;
556 :
557 : // Bind the FID to the "WHERE" clause.
558 : const int sqlite_err =
559 55 : sqlite3_bind_int64(poStmt, nColCount, poFeature->GetFID());
560 55 : if (sqlite_err != SQLITE_OK)
561 : {
562 0 : CPLError(CE_Failure, CPLE_AppDefined,
563 : "failed to bind FID '" CPL_FRMT_GIB "' to statement",
564 : poFeature->GetFID());
565 0 : return OGRERR_FAILURE;
566 : }
567 :
568 55 : return OGRERR_NONE;
569 : }
570 :
571 : //----------------------------------------------------------------------
572 : // FeatureBindInsertParameters()
573 : //
574 : // Selectively bind the values of an OGRFeature to a prepared
575 : // statement, prior to execution. Carefully binds exactly the
576 : // same parameters that have been set up by FeatureGenerateInsertSQL()
577 : // as bindable.
578 : //
579 253166 : OGRErr OGRGeoPackageTableLayer::FeatureBindInsertParameters(
580 : OGRFeature *poFeature, sqlite3_stmt *poStmt, bool bAddFID,
581 : bool bBindUnsetFields)
582 : {
583 253166 : return FeatureBindParameters(poFeature, poStmt, nullptr, bAddFID,
584 253166 : bBindUnsetFields, -1, nullptr, -1, nullptr);
585 : }
586 :
587 : //----------------------------------------------------------------------
588 : // FeatureGenerateInsertSQL()
589 : //
590 : // Build a SQL INSERT statement that references all the columns in
591 : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
592 : // statement. All statements start off with geometry (if it exists)
593 : // then reference each column in the order it appears in the OGRFeatureDefn.
594 : // FeatureBindParameters operates on the expectation of this
595 : // column ordering.
596 : //
597 553 : CPLString OGRGeoPackageTableLayer::FeatureGenerateInsertSQL(
598 : OGRFeature *poFeature, bool bAddFID, bool bBindUnsetFields, bool bUpsert,
599 : const std::string &osUpsertUniqueColumnName)
600 : {
601 553 : bool bNeedComma = false;
602 553 : OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
603 :
604 553 : if (poFeatureDefn->GetFieldCount() ==
605 553 : ((m_iFIDAsRegularColumnIndex >= 0) ? 1 : 0) &&
606 553 : poFeatureDefn->GetGeomFieldCount() == 0 && !bAddFID)
607 : return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
608 8 : SQLEscapeName(m_pszTableName).c_str());
609 :
610 : /* Set up our SQL string basics */
611 1098 : CPLString osSQLFront("INSERT");
612 549 : if (bUpsert && osUpsertUniqueColumnName.empty())
613 8 : osSQLFront += " OR REPLACE";
614 : osSQLFront +=
615 549 : CPLSPrintf(" INTO \"%s\" ( ", SQLEscapeName(m_pszTableName).c_str());
616 :
617 1098 : CPLString osSQLBack;
618 549 : osSQLBack = ") VALUES (";
619 :
620 1098 : CPLString osSQLColumn;
621 :
622 549 : if (bAddFID)
623 : {
624 45 : osSQLColumn.Printf("\"%s\"", SQLEscapeName(GetFIDColumn()).c_str());
625 45 : osSQLFront += osSQLColumn;
626 45 : osSQLBack += "?";
627 45 : bNeedComma = true;
628 : }
629 :
630 549 : if (poFeatureDefn->GetGeomFieldCount())
631 : {
632 519 : if (bNeedComma)
633 : {
634 43 : osSQLFront += ", ";
635 43 : osSQLBack += ", ";
636 : }
637 :
638 : osSQLColumn.Printf(
639 : "\"%s\"",
640 1038 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
641 519 : .c_str());
642 519 : osSQLFront += osSQLColumn;
643 519 : osSQLBack += "?";
644 519 : bNeedComma = true;
645 : }
646 :
647 : /* Add attribute column names (except FID) to the SQL */
648 2281 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
649 : {
650 1732 : if (i == m_iFIDAsRegularColumnIndex || m_abGeneratedColumns[i])
651 11 : continue;
652 1721 : if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
653 2 : continue;
654 :
655 1719 : if (!bNeedComma)
656 : {
657 28 : bNeedComma = true;
658 : }
659 : else
660 : {
661 1691 : osSQLFront += ", ";
662 1691 : osSQLBack += ", ";
663 : }
664 :
665 : osSQLColumn.Printf(
666 : "\"%s\"",
667 3438 : SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
668 1719 : .c_str());
669 1719 : osSQLFront += osSQLColumn;
670 1719 : osSQLBack += "?";
671 : }
672 :
673 549 : osSQLBack += ")";
674 :
675 549 : if (!bNeedComma)
676 : return CPLSPrintf("INSERT INTO \"%s\" DEFAULT VALUES",
677 0 : SQLEscapeName(m_pszTableName).c_str());
678 :
679 549 : if (bUpsert && !osUpsertUniqueColumnName.empty())
680 : {
681 5 : osSQLBack += " ON CONFLICT ";
682 : #if SQLITE_VERSION_NUMBER < 3035000L
683 : osSQLBack += "(\"";
684 : osSQLBack += SQLEscapeName(osUpsertUniqueColumnName.c_str());
685 : osSQLBack += "\") ";
686 : #endif
687 5 : osSQLBack += "DO UPDATE SET ";
688 5 : bNeedComma = false;
689 5 : if (poFeatureDefn->GetGeomFieldCount())
690 : {
691 : osSQLBack += CPLSPrintf(
692 : "\"%s\" = excluded.\"%s\"",
693 6 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
694 : .c_str(),
695 6 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef())
696 6 : .c_str());
697 3 : bNeedComma = true;
698 : }
699 15 : for (int i = 0; i < poFeatureDefn->GetFieldCount(); i++)
700 : {
701 10 : if (i == m_iFIDAsRegularColumnIndex)
702 0 : continue;
703 10 : if (!bBindUnsetFields && !poFeature->IsFieldSet(i))
704 0 : continue;
705 :
706 10 : if (!bNeedComma)
707 : {
708 2 : bNeedComma = true;
709 : }
710 : else
711 : {
712 8 : osSQLBack += ", ";
713 : }
714 :
715 : osSQLBack += CPLSPrintf(
716 : "\"%s\" = excluded.\"%s\"",
717 20 : SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
718 : .c_str(),
719 20 : SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef())
720 20 : .c_str());
721 : }
722 : #if SQLITE_VERSION_NUMBER >= 3035000L
723 5 : osSQLBack += " RETURNING \"";
724 5 : osSQLBack += SQLEscapeName(GetFIDColumn()).c_str();
725 5 : osSQLBack += "\"";
726 : #endif
727 : }
728 :
729 1098 : return osSQLFront + osSQLBack;
730 : }
731 :
732 : //----------------------------------------------------------------------
733 : // FeatureGenerateUpdateSQL()
734 : //
735 : // Build a SQL UPDATE statement that references all the columns in
736 : // the OGRFeatureDefn, then prepare it for repeated use in a prepared
737 : // statement. All statements start off with geometry (if it exists)
738 : // then reference each column in the order it appears in the OGRFeatureDefn.
739 : // FeatureBindParameters operates on the expectation of this
740 : // column ordering.
741 :
742 : //
743 45 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
744 : const OGRFeature *poFeature) const
745 : {
746 45 : bool bNeedComma = false;
747 45 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
748 :
749 : /* Set up our SQL string basics */
750 90 : std::string osUpdate("UPDATE \"");
751 45 : osUpdate += SQLEscapeName(m_pszTableName);
752 45 : osUpdate += "\" SET ";
753 :
754 45 : if (poFeatureDefn->GetGeomFieldCount() > 0)
755 : {
756 38 : osUpdate += '"';
757 : osUpdate +=
758 38 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
759 38 : osUpdate += "\"=?";
760 38 : bNeedComma = true;
761 : }
762 :
763 : /* Add attribute column names (except FID) to the SQL */
764 45 : const int nFieldCount = poFeatureDefn->GetFieldCount();
765 110 : for (int i = 0; i < nFieldCount; i++)
766 : {
767 65 : if (i == m_iFIDAsRegularColumnIndex || m_abGeneratedColumns[i])
768 8 : continue;
769 57 : if (!poFeature->IsFieldSet(i))
770 11 : continue;
771 46 : if (!bNeedComma)
772 6 : bNeedComma = true;
773 : else
774 40 : osUpdate += ", ";
775 :
776 46 : osUpdate += '"';
777 46 : osUpdate += SQLEscapeName(poFeatureDefn->GetFieldDefn(i)->GetNameRef());
778 46 : osUpdate += "\"=?";
779 : }
780 45 : if (!bNeedComma)
781 1 : return CPLString();
782 :
783 44 : osUpdate += " WHERE \"";
784 44 : osUpdate += SQLEscapeName(m_pszFidColumn);
785 44 : osUpdate += "\" = ?";
786 :
787 44 : return osUpdate;
788 : }
789 :
790 : /************************************************************************/
791 : /* GetLayerDefn() */
792 : /************************************************************************/
793 :
794 35429 : OGRFeatureDefn *OGRGeoPackageTableLayer::GetLayerDefn()
795 : {
796 35429 : if (!m_bFeatureDefnCompleted)
797 : {
798 769 : m_bFeatureDefnCompleted = true;
799 769 : ReadTableDefinition();
800 769 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
801 : }
802 35429 : return m_poFeatureDefn;
803 : }
804 :
805 : /************************************************************************/
806 : /* GetFIDColumn() */
807 : /************************************************************************/
808 :
809 2478 : const char *OGRGeoPackageTableLayer::GetFIDColumn()
810 : {
811 2478 : if (!m_bFeatureDefnCompleted)
812 15 : GetLayerDefn();
813 2478 : return OGRGeoPackageLayer::GetFIDColumn();
814 : }
815 :
816 : /************************************************************************/
817 : /* GetGeomType() */
818 : /************************************************************************/
819 :
820 256073 : OGRwkbGeometryType OGRGeoPackageTableLayer::GetGeomType()
821 : {
822 256073 : return m_poFeatureDefn->GetGeomType();
823 : }
824 :
825 : /************************************************************************/
826 : /* GetGeometryColumn() */
827 : /************************************************************************/
828 :
829 5671 : const char *OGRGeoPackageTableLayer::GetGeometryColumn()
830 :
831 : {
832 5671 : if (m_poFeatureDefn->GetGeomFieldCount() > 0)
833 5660 : return m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
834 : else
835 11 : return "";
836 : }
837 :
838 : //----------------------------------------------------------------------
839 : // ReadTableDefinition()
840 : //
841 : // Initialization routine. Read all the metadata about a table,
842 : // starting from just the table name. Reads information from GPKG
843 : // metadata tables and from SQLite table metadata. Uses it to
844 : // populate OGRSpatialReference information and OGRFeatureDefn objects,
845 : // among others.
846 : //
847 769 : OGRErr OGRGeoPackageTableLayer::ReadTableDefinition()
848 : {
849 769 : m_poDS->IncrementReadTableDefCounter();
850 :
851 769 : bool bReadExtent = false;
852 769 : sqlite3 *poDb = m_poDS->GetDB();
853 769 : OGREnvelope oExtent;
854 1538 : CPLString osGeomColumnName;
855 1538 : CPLString osGeomColsType;
856 769 : bool bHasZ = false;
857 769 : bool bHasM = false;
858 :
859 : #ifdef ENABLE_GPKG_OGR_CONTENTS
860 769 : if (m_poDS->m_bHasGPKGOGRContents)
861 : {
862 : CPLString osTrigger1Name(
863 1536 : CPLSPrintf("trigger_insert_feature_count_%s", m_pszTableName));
864 : CPLString osTrigger2Name(
865 1536 : CPLSPrintf("trigger_delete_feature_count_%s", m_pszTableName));
866 : const std::map<CPLString, CPLString> &oMap =
867 768 : m_poDS->GetNameTypeMapFromSQliteMaster();
868 1499 : if (cpl::contains(oMap, osTrigger1Name.toupper()) &&
869 731 : cpl::contains(oMap, osTrigger2Name.toupper()))
870 : {
871 731 : m_bOGRFeatureCountTriggersEnabled = true;
872 : }
873 37 : else if (m_bIsTable)
874 : {
875 29 : CPLDebug("GPKG",
876 : "Insert/delete feature_count triggers "
877 : "missing on %s",
878 : m_pszTableName);
879 : }
880 : }
881 : #endif
882 :
883 : #ifdef ENABLE_GPKG_OGR_CONTENTS
884 769 : if (m_poDS->m_bHasGPKGOGRContents)
885 : {
886 768 : char *pszSQL = sqlite3_mprintf("SELECT feature_count "
887 : "FROM gpkg_ogr_contents "
888 : "WHERE table_name = '%q'"
889 : #ifdef WORKAROUND_SQLITE3_BUGS
890 : " OR 0"
891 : #endif
892 : " LIMIT 2",
893 : m_pszTableName);
894 1536 : auto oResultFeatureCount = SQLQuery(poDb, pszSQL);
895 768 : sqlite3_free(pszSQL);
896 768 : if (oResultFeatureCount && oResultFeatureCount->RowCount() == 0)
897 : {
898 31 : pszSQL = sqlite3_mprintf("SELECT feature_count "
899 : "FROM gpkg_ogr_contents "
900 : "WHERE lower(table_name) = lower('%q')"
901 : #ifdef WORKAROUND_SQLITE3_BUGS
902 : " OR 0"
903 : #endif
904 : " LIMIT 2",
905 : m_pszTableName);
906 31 : oResultFeatureCount = SQLQuery(poDb, pszSQL);
907 31 : sqlite3_free(pszSQL);
908 : }
909 :
910 768 : if (oResultFeatureCount && oResultFeatureCount->RowCount() == 1)
911 : {
912 737 : const char *pszFeatureCount = oResultFeatureCount->GetValue(0, 0);
913 737 : if (pszFeatureCount)
914 : {
915 723 : m_nTotalFeatureCount = CPLAtoGIntBig(pszFeatureCount);
916 : }
917 : }
918 : }
919 : #endif
920 :
921 : bool bHasPreexistingSingleGeomColumn =
922 769 : m_poFeatureDefn->GetGeomFieldCount() == 1;
923 769 : bool bHasMultipleGeomColsInGpkgGeometryColumns = false;
924 :
925 769 : if (m_bIsInGpkgContents)
926 : {
927 : /* Check that the table name is registered in gpkg_contents */
928 : const std::map<CPLString, GPKGContentsDesc> &oMapContents =
929 752 : m_poDS->GetContents();
930 : const auto oIterContents =
931 752 : oMapContents.find(CPLString(m_pszTableName).toupper());
932 752 : if (oIterContents == oMapContents.end())
933 : {
934 0 : CPLError(CE_Failure, CPLE_AppDefined,
935 : "layer '%s' is not registered in gpkg_contents",
936 : m_pszTableName);
937 0 : return OGRERR_FAILURE;
938 : }
939 :
940 752 : const GPKGContentsDesc &oContents = oIterContents->second;
941 :
942 752 : const char *pszIdentifier = oContents.osIdentifier.c_str();
943 752 : if (pszIdentifier[0] != 0 && strcmp(pszIdentifier, m_pszTableName) != 0)
944 6 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
945 752 : const char *pszDescription = oContents.osDescription.c_str();
946 752 : if (pszDescription[0])
947 6 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
948 :
949 752 : if (m_bIsSpatial)
950 : {
951 : /* All the extrema have to be non-NULL for this to make sense */
952 1247 : if (!oContents.osMinX.empty() && !oContents.osMinY.empty() &&
953 1247 : !oContents.osMaxX.empty() && !oContents.osMaxY.empty())
954 : {
955 541 : oExtent.MinX = CPLAtof(oContents.osMinX);
956 541 : oExtent.MinY = CPLAtof(oContents.osMinY);
957 541 : oExtent.MaxX = CPLAtof(oContents.osMaxX);
958 541 : oExtent.MaxY = CPLAtof(oContents.osMaxY);
959 1082 : bReadExtent = oExtent.MinX <= oExtent.MaxX &&
960 541 : oExtent.MinY <= oExtent.MaxY;
961 : }
962 :
963 : /* Check that the table name is registered in gpkg_geometry_columns
964 : */
965 706 : char *pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
966 : "geometry_type_name, srs_id, z, m "
967 : "FROM gpkg_geometry_columns "
968 : "WHERE table_name = '%q'"
969 : #ifdef WORKAROUND_SQLITE3_BUGS
970 : " OR 0"
971 : #endif
972 : " LIMIT 2000",
973 : m_pszTableName);
974 :
975 1412 : auto oResultGeomCols = SQLQuery(poDb, pszSQL);
976 706 : sqlite3_free(pszSQL);
977 706 : if (oResultGeomCols && oResultGeomCols->RowCount() == 0)
978 : {
979 0 : pszSQL = sqlite3_mprintf("SELECT table_name, column_name, "
980 : "geometry_type_name, srs_id, z, m "
981 : "FROM gpkg_geometry_columns "
982 : "WHERE lower(table_name) = lower('%q')"
983 : #ifdef WORKAROUND_SQLITE3_BUGS
984 : " OR 0"
985 : #endif
986 : " LIMIT 2000",
987 : m_pszTableName);
988 :
989 0 : oResultGeomCols = SQLQuery(poDb, pszSQL);
990 0 : sqlite3_free(pszSQL);
991 : }
992 :
993 : /* gpkg_geometry_columns query has to work */
994 706 : if (!(oResultGeomCols && oResultGeomCols->RowCount() > 0))
995 : {
996 0 : CPLError(
997 : CE_Warning, CPLE_AppDefined,
998 : "layer '%s' is not registered in gpkg_geometry_columns",
999 : m_pszTableName);
1000 : }
1001 : else
1002 : {
1003 706 : int iRow = -1;
1004 706 : bHasMultipleGeomColsInGpkgGeometryColumns =
1005 706 : oResultGeomCols->RowCount() > 1;
1006 707 : for (int i = 0; i < oResultGeomCols->RowCount(); ++i)
1007 : {
1008 : const char *pszGeomColName =
1009 707 : oResultGeomCols->GetValue(1, i);
1010 707 : if (!pszGeomColName)
1011 0 : continue;
1012 1414 : if (!bHasPreexistingSingleGeomColumn ||
1013 707 : strcmp(pszGeomColName,
1014 707 : m_poFeatureDefn->GetGeomFieldDefn(0)
1015 : ->GetNameRef()) == 0)
1016 : {
1017 706 : iRow = i;
1018 706 : break;
1019 : }
1020 : }
1021 :
1022 706 : if (iRow >= 0)
1023 : {
1024 : const char *pszGeomColName =
1025 706 : oResultGeomCols->GetValue(1, iRow);
1026 706 : if (pszGeomColName != nullptr)
1027 706 : osGeomColumnName = pszGeomColName;
1028 : const char *pszGeomColsType =
1029 706 : oResultGeomCols->GetValue(2, iRow);
1030 706 : if (pszGeomColsType != nullptr)
1031 706 : osGeomColsType = pszGeomColsType;
1032 706 : m_iSrs = oResultGeomCols->GetValueAsInteger(3, iRow);
1033 706 : m_nZFlag = oResultGeomCols->GetValueAsInteger(4, iRow);
1034 706 : m_nMFlag = oResultGeomCols->GetValueAsInteger(5, iRow);
1035 706 : if (!(EQUAL(osGeomColsType, "GEOMETRY") && m_nZFlag == 2))
1036 : {
1037 700 : bHasZ = CPL_TO_BOOL(m_nZFlag);
1038 700 : bHasM = CPL_TO_BOOL(m_nMFlag);
1039 : }
1040 : }
1041 : else
1042 : {
1043 0 : CPLError(
1044 : CE_Warning, CPLE_AppDefined,
1045 : "Cannot find record for layer '%s' and geometry column "
1046 : "'%s' in gpkg_geometry_columns",
1047 : m_pszTableName,
1048 : bHasPreexistingSingleGeomColumn
1049 0 : ? m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()
1050 : : "unknown");
1051 : }
1052 : }
1053 : }
1054 : }
1055 :
1056 : // set names (in upper case) of fields with unique constraint
1057 1538 : std::set<std::string> uniqueFieldsUC;
1058 769 : if (m_bIsTable)
1059 : {
1060 : // If resolving the layer definition of a substantial number of tables,
1061 : // fetch in a single time the content of the sqlite_master to increase
1062 : // performance
1063 : // Threshold somewhat arbitrary. If changing it, change
1064 : // ogr_gpkg.py::test_ogr_gpkg_unique_many_layers as well
1065 761 : constexpr int THRESHOLD_GET_SQLITE_MASTER = 10;
1066 761 : if (m_poDS->GetReadTableDefCounter() >= THRESHOLD_GET_SQLITE_MASTER)
1067 : {
1068 2 : uniqueFieldsUC = SQLGetUniqueFieldUCConstraints(
1069 2 : poDb, m_pszTableName, m_poDS->GetSqliteMasterContent());
1070 : }
1071 : else
1072 : {
1073 : uniqueFieldsUC =
1074 759 : SQLGetUniqueFieldUCConstraints(poDb, m_pszTableName);
1075 : }
1076 : }
1077 :
1078 : /* Use the "PRAGMA TABLE_INFO()" call to get table definition */
1079 : /* #|name|type|notnull|default|pk */
1080 : /* 0|id|integer|0||1 */
1081 : /* 1|name|varchar|0||0 */
1082 769 : char *pszSQL = sqlite3_mprintf("pragma table_xinfo('%q')", m_pszTableName);
1083 1538 : auto oResultTable = SQLQuery(poDb, pszSQL);
1084 769 : sqlite3_free(pszSQL);
1085 :
1086 769 : if (!oResultTable || oResultTable->RowCount() == 0)
1087 : {
1088 0 : if (oResultTable)
1089 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find table %s",
1090 : m_pszTableName);
1091 0 : return OGRERR_FAILURE;
1092 : }
1093 :
1094 : /* Populate feature definition from table description */
1095 :
1096 : // First pass to determine if we have a single PKID column
1097 769 : int nCountPKIDColumns = 0;
1098 3421 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1099 : {
1100 2652 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1101 2652 : if (nPKIDIndex > 0)
1102 755 : nCountPKIDColumns++;
1103 : }
1104 769 : if (nCountPKIDColumns > 1)
1105 : {
1106 1 : CPLDebug("GPKG",
1107 : "For table %s, multiple columns make "
1108 : "the primary key. Ignoring them",
1109 : m_pszTableName);
1110 : }
1111 :
1112 769 : m_abGeneratedColumns.resize(oResultTable->RowCount());
1113 3421 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1114 : {
1115 2652 : const char *pszName = oResultTable->GetValue(1, iRecord);
1116 5304 : std::string osType = oResultTable->GetValue(2, iRecord);
1117 2652 : int bNotNull = oResultTable->GetValueAsInteger(3, iRecord);
1118 2652 : const char *pszDefault = oResultTable->GetValue(4, iRecord);
1119 2652 : int nPKIDIndex = oResultTable->GetValueAsInteger(5, iRecord);
1120 2652 : int nHiddenValue = oResultTable->GetValueAsInteger(6, iRecord);
1121 :
1122 2652 : OGRFieldSubType eSubType = OFSTNone;
1123 2652 : int nMaxWidth = 0;
1124 2652 : int nType = OFTMaxType + 1;
1125 :
1126 : // SQLite 3.31 has a " GENERATED ALWAYS" suffix in the type column,
1127 : // but more recent versions no longer have it.
1128 2652 : bool bIsGenerated = false;
1129 2652 : constexpr const char *GENERATED_ALWAYS_SUFFIX = " GENERATED ALWAYS";
1130 2652 : if (osType.size() > strlen(GENERATED_ALWAYS_SUFFIX) &&
1131 2652 : CPLString(osType).toupper().compare(
1132 0 : osType.size() - strlen(GENERATED_ALWAYS_SUFFIX),
1133 : strlen(GENERATED_ALWAYS_SUFFIX), GENERATED_ALWAYS_SUFFIX) == 0)
1134 : {
1135 0 : bIsGenerated = true;
1136 0 : osType.resize(osType.size() - strlen(GENERATED_ALWAYS_SUFFIX));
1137 : }
1138 2652 : constexpr int GENERATED_VIRTUAL = 2;
1139 2652 : constexpr int GENERATED_STORED = 3;
1140 2652 : if (nHiddenValue == GENERATED_VIRTUAL ||
1141 : nHiddenValue == GENERATED_STORED)
1142 : {
1143 2 : bIsGenerated = true;
1144 : }
1145 :
1146 2652 : if (!osType.empty() || m_bIsTable)
1147 : {
1148 2649 : nType = GPkgFieldToOGR(osType.c_str(), eSubType, nMaxWidth);
1149 : }
1150 : else
1151 : {
1152 : // For a view, if the geometry column is computed, we don't
1153 : // get a type, so trust the one from gpkg_geometry_columns
1154 3 : if (EQUAL(osGeomColumnName, pszName))
1155 : {
1156 1 : osType = osGeomColsType;
1157 : }
1158 : }
1159 :
1160 : /* Not a standard field type... */
1161 5298 : if (!osType.empty() && !EQUAL(pszName, "OGC_FID") &&
1162 710 : ((nType > OFTMaxType && !osGeomColsType.empty()) ||
1163 1938 : EQUAL(osGeomColumnName, pszName)))
1164 : {
1165 : /* Maybe it is a geometry type? */
1166 : OGRwkbGeometryType oGeomType;
1167 708 : if (nType > OFTMaxType)
1168 708 : oGeomType = GPkgGeometryTypeToWKB(osType.c_str(), bHasZ, bHasM);
1169 : else
1170 0 : oGeomType = wkbUnknown;
1171 708 : if (oGeomType != wkbNone)
1172 : {
1173 1415 : if ((bHasPreexistingSingleGeomColumn &&
1174 707 : (!bHasMultipleGeomColsInGpkgGeometryColumns ||
1175 3 : strcmp(pszName, m_poFeatureDefn->GetGeomFieldDefn(0)
1176 1416 : ->GetNameRef()) == 0)) ||
1177 2 : m_poFeatureDefn->GetGeomFieldCount() == 0)
1178 : {
1179 : OGRwkbGeometryType oGeomTypeGeomCols =
1180 706 : GPkgGeometryTypeToWKB(osGeomColsType.c_str(), bHasZ,
1181 : bHasM);
1182 : /* Enforce consistency between table and metadata */
1183 706 : if (wkbFlatten(oGeomType) == wkbUnknown)
1184 442 : oGeomType = oGeomTypeGeomCols;
1185 706 : if (oGeomType != oGeomTypeGeomCols)
1186 : {
1187 0 : CPLError(CE_Warning, CPLE_AppDefined,
1188 : "geometry column type for layer '%s' in "
1189 : "'%s.%s' (%s) is not "
1190 : "consistent with type in "
1191 : "gpkg_geometry_columns (%s)",
1192 : GetName(), m_pszTableName, pszName,
1193 : osType.c_str(), osGeomColsType.c_str());
1194 : }
1195 :
1196 706 : if (!bHasPreexistingSingleGeomColumn)
1197 : {
1198 0 : OGRGeomFieldDefn oGeomField(pszName, oGeomType);
1199 0 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1200 : }
1201 706 : bHasPreexistingSingleGeomColumn = false;
1202 706 : if (bNotNull)
1203 3 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetNullable(
1204 : FALSE);
1205 :
1206 : /* Read the SRS */
1207 1412 : auto poSRS = m_poDS->GetSpatialRef(m_iSrs);
1208 706 : if (poSRS)
1209 : {
1210 614 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
1211 307 : poSRS.get());
1212 : }
1213 : }
1214 2 : else if (!STARTS_WITH(
1215 : GetName(),
1216 : (std::string(m_pszTableName) + " (").c_str()))
1217 : {
1218 0 : CPLError(CE_Warning, CPLE_AppDefined,
1219 : "table '%s' has multiple geometry fields. "
1220 : "Ignoring field '%s' for this layer",
1221 : m_pszTableName, pszName);
1222 : }
1223 : }
1224 : else
1225 : {
1226 0 : CPLError(CE_Warning, CPLE_AppDefined,
1227 : "geometry column '%s' of type '%s' ignored", pszName,
1228 : osType.c_str());
1229 : }
1230 : }
1231 : else
1232 : {
1233 1944 : if (nType > OFTMaxType)
1234 : {
1235 5 : CPLDebug("GPKG",
1236 : "For table %s, unrecognized type name %s for "
1237 : "column %s. Using string type",
1238 : m_pszTableName, osType.c_str(), pszName);
1239 5 : nType = OFTString;
1240 : }
1241 :
1242 : /* Is this the FID column? */
1243 1944 : if (nPKIDIndex > 0 && nCountPKIDColumns == 1 &&
1244 753 : (nType == OFTInteger || nType == OFTInteger64))
1245 : {
1246 753 : m_pszFidColumn = CPLStrdup(pszName);
1247 : }
1248 : else
1249 : {
1250 2382 : OGRFieldDefn oField(pszName, static_cast<OGRFieldType>(nType));
1251 1191 : oField.SetSubType(eSubType);
1252 1191 : oField.SetWidth(nMaxWidth);
1253 1191 : if (bNotNull)
1254 10 : oField.SetNullable(FALSE);
1255 :
1256 1191 : if (cpl::contains(uniqueFieldsUC, CPLString(pszName).toupper()))
1257 : {
1258 43 : oField.SetUnique(TRUE);
1259 : }
1260 :
1261 1191 : if (pszDefault != nullptr)
1262 : {
1263 13 : int nYear = 0;
1264 13 : int nMonth = 0;
1265 13 : int nDay = 0;
1266 13 : int nHour = 0;
1267 13 : int nMinute = 0;
1268 13 : float fSecond = 0.0f;
1269 13 : if (oField.GetType() == OFTString &&
1270 3 : !EQUAL(pszDefault, "NULL") &&
1271 3 : !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
1272 16 : pszDefault[0] != '(' && pszDefault[0] != '\'' &&
1273 0 : CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
1274 : {
1275 0 : CPLString osDefault("'");
1276 : char *pszTmp =
1277 0 : CPLEscapeString(pszDefault, -1, CPLES_SQL);
1278 0 : osDefault += pszTmp;
1279 0 : CPLFree(pszTmp);
1280 0 : osDefault += "'";
1281 0 : oField.SetDefault(osDefault);
1282 : }
1283 20 : else if (nType == OFTDateTime &&
1284 7 : sscanf(pszDefault, "'%d-%d-%dT%d:%d:%fZ'", &nYear,
1285 : &nMonth, &nDay, &nHour, &nMinute,
1286 : &fSecond) == 6)
1287 : {
1288 2 : if (strchr(pszDefault, '.') == nullptr)
1289 1 : oField.SetDefault(
1290 : CPLSPrintf("'%04d/%02d/%02d %02d:%02d:%02d'",
1291 : nYear, nMonth, nDay, nHour, nMinute,
1292 1 : static_cast<int>(fSecond + 0.5)));
1293 : else
1294 1 : oField.SetDefault(CPLSPrintf(
1295 : "'%04d/%02d/%02d %02d:%02d:%06.3f'", nYear,
1296 : nMonth, nDay, nHour, nMinute, fSecond));
1297 : }
1298 21 : else if ((oField.GetType() == OFTDate ||
1299 10 : oField.GetType() == OFTDateTime) &&
1300 6 : !EQUAL(pszDefault, "NULL") &&
1301 6 : !STARTS_WITH_CI(pszDefault, "CURRENT_") &&
1302 5 : pszDefault[0] != '(' && pszDefault[0] != '\'' &&
1303 25 : !(pszDefault[0] >= '0' && pszDefault[0] <= '9') &&
1304 3 : CPLGetValueType(pszDefault) == CPL_VALUE_STRING)
1305 : {
1306 6 : CPLString osDefault("(");
1307 3 : osDefault += pszDefault;
1308 3 : osDefault += ")";
1309 3 : if (EQUAL(osDefault,
1310 : "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))"))
1311 3 : oField.SetDefault("CURRENT_TIMESTAMP");
1312 : else
1313 0 : oField.SetDefault(osDefault);
1314 : }
1315 : else
1316 : {
1317 8 : oField.SetDefault(pszDefault);
1318 : }
1319 : }
1320 1191 : m_abGeneratedColumns[m_poFeatureDefn->GetFieldCount()] =
1321 2382 : bIsGenerated;
1322 1191 : m_poFeatureDefn->AddFieldDefn(&oField);
1323 : }
1324 : }
1325 : }
1326 :
1327 769 : m_abGeneratedColumns.resize(m_poFeatureDefn->GetFieldCount());
1328 :
1329 : /* Wait, we didn't find a FID? Some operations will not be possible */
1330 769 : if (m_bIsTable && m_pszFidColumn == nullptr)
1331 : {
1332 8 : CPLDebug("GPKG", "no integer primary key defined for table '%s'",
1333 : m_pszTableName);
1334 : }
1335 :
1336 769 : if (bReadExtent)
1337 : {
1338 541 : m_poExtent = new OGREnvelope(oExtent);
1339 : }
1340 :
1341 : // Look for sub-types such as JSON
1342 769 : if (m_poDS->HasDataColumnsTable())
1343 : {
1344 36 : pszSQL = sqlite3_mprintf(
1345 : "SELECT column_name, name, mime_type, "
1346 : "constraint_name, description FROM gpkg_data_columns "
1347 : "WHERE table_name = '%q'",
1348 : m_pszTableName);
1349 36 : oResultTable = SQLQuery(poDb, pszSQL);
1350 36 : sqlite3_free(pszSQL);
1351 36 : if (oResultTable)
1352 : {
1353 116 : for (int iRecord = 0; iRecord < oResultTable->RowCount(); iRecord++)
1354 : {
1355 80 : const char *pszColumn = oResultTable->GetValue(0, iRecord);
1356 80 : if (pszColumn == nullptr)
1357 0 : continue;
1358 80 : const char *pszName = oResultTable->GetValue(1, iRecord);
1359 :
1360 : // We use the "name" attribute from gpkg_data_columns as the
1361 : // field alternative name, so long as it isn't just a copy
1362 : // of the column name
1363 80 : const char *pszAlias = nullptr;
1364 80 : if (pszName && !EQUAL(pszName, pszColumn))
1365 9 : pszAlias = pszName;
1366 :
1367 80 : if (pszAlias)
1368 : {
1369 9 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1370 9 : if (iIdx >= 0)
1371 : {
1372 9 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetAlternativeName(
1373 : pszAlias);
1374 : }
1375 : }
1376 :
1377 80 : if (const char *pszDescription =
1378 80 : oResultTable->GetValue(4, iRecord))
1379 : {
1380 6 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1381 6 : if (iIdx >= 0)
1382 : {
1383 6 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetComment(
1384 : pszDescription);
1385 : }
1386 : }
1387 :
1388 80 : const char *pszMimeType = oResultTable->GetValue(2, iRecord);
1389 : const char *pszConstraintName =
1390 80 : oResultTable->GetValue(3, iRecord);
1391 80 : if (pszMimeType && EQUAL(pszMimeType, "application/json"))
1392 : {
1393 5 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1394 10 : if (iIdx >= 0 &&
1395 5 : m_poFeatureDefn->GetFieldDefn(iIdx)->GetType() ==
1396 : OFTString)
1397 : {
1398 5 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetSubType(
1399 : OFSTJSON);
1400 5 : }
1401 : }
1402 75 : else if (pszConstraintName)
1403 : {
1404 63 : const int iIdx = m_poFeatureDefn->GetFieldIndex(pszColumn);
1405 63 : if (iIdx >= 0)
1406 : {
1407 63 : m_poFeatureDefn->GetFieldDefn(iIdx)->SetDomainName(
1408 : pszConstraintName);
1409 : }
1410 : }
1411 : }
1412 : }
1413 : }
1414 :
1415 : // Look for geometry column coordinate precision in gpkg_metadata
1416 769 : if (m_poDS->HasMetadataTables() && m_poFeatureDefn->GetGeomFieldCount() > 0)
1417 : {
1418 307 : pszSQL = sqlite3_mprintf(
1419 : "SELECT md.metadata, mdr.column_name "
1420 : "FROM gpkg_metadata md "
1421 : "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id) "
1422 : "WHERE lower(mdr.table_name) = lower('%q') "
1423 : "AND md.md_standard_uri = 'http://gdal.org' "
1424 : "AND md.mime_type = 'text/xml' "
1425 : "AND mdr.reference_scope = 'column' "
1426 : "AND md.metadata LIKE '<CoordinatePrecision%%' "
1427 : "ORDER BY md.id LIMIT 1000", // to avoid denial of service
1428 : m_pszTableName);
1429 :
1430 614 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
1431 307 : sqlite3_free(pszSQL);
1432 :
1433 323 : for (int i = 0; oResult && i < oResult->RowCount(); i++)
1434 : {
1435 16 : const char *pszMetadata = oResult->GetValue(0, i);
1436 16 : const char *pszColumn = oResult->GetValue(1, i);
1437 16 : if (pszMetadata && pszColumn)
1438 : {
1439 : const int iGeomCol =
1440 16 : m_poFeatureDefn->GetGeomFieldIndex(pszColumn);
1441 16 : if (iGeomCol >= 0)
1442 : {
1443 : auto psXMLNode =
1444 32 : CPLXMLTreeCloser(CPLParseXMLString(pszMetadata));
1445 16 : if (psXMLNode)
1446 : {
1447 32 : OGRGeomCoordinatePrecision sCoordPrec;
1448 16 : if (const char *pszVal = CPLGetXMLValue(
1449 16 : psXMLNode.get(), "xy_resolution", nullptr))
1450 : {
1451 16 : sCoordPrec.dfXYResolution = CPLAtof(pszVal);
1452 : }
1453 16 : if (const char *pszVal = CPLGetXMLValue(
1454 16 : psXMLNode.get(), "z_resolution", nullptr))
1455 : {
1456 12 : sCoordPrec.dfZResolution = CPLAtof(pszVal);
1457 : }
1458 16 : if (const char *pszVal = CPLGetXMLValue(
1459 16 : psXMLNode.get(), "m_resolution", nullptr))
1460 : {
1461 12 : sCoordPrec.dfMResolution = CPLAtof(pszVal);
1462 : }
1463 16 : m_poFeatureDefn->GetGeomFieldDefn(iGeomCol)
1464 16 : ->SetCoordinatePrecision(sCoordPrec);
1465 16 : if (CPLTestBool(CPLGetXMLValue(
1466 16 : psXMLNode.get(), "discard_coord_lsb", "false")))
1467 : {
1468 4 : m_sBinaryPrecision.SetFrom(sCoordPrec);
1469 4 : m_bUndoDiscardCoordLSBOnReading =
1470 4 : CPLTestBool(CPLGetXMLValue(
1471 4 : psXMLNode.get(),
1472 : "undo_discard_coord_lsb_on_reading",
1473 : "false"));
1474 : }
1475 : }
1476 : }
1477 : }
1478 : }
1479 : }
1480 :
1481 : /* Update the columns string */
1482 769 : BuildColumns();
1483 :
1484 769 : CheckUnknownExtensions();
1485 :
1486 769 : InitView();
1487 :
1488 769 : return OGRERR_NONE;
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* OGRGeoPackageTableLayer() */
1493 : /************************************************************************/
1494 :
1495 3676 : OGRGeoPackageTableLayer::OGRGeoPackageTableLayer(GDALGeoPackageDataset *poDS,
1496 3676 : const char *pszTableName)
1497 3676 : : OGRGeoPackageLayer(poDS), m_pszTableName(CPLStrdup(pszTableName))
1498 : {
1499 3676 : memset(m_abHasGeometryExtension, 0, sizeof(m_abHasGeometryExtension));
1500 :
1501 3676 : m_poFeatureDefn = new OGRFeatureDefn(m_pszTableName);
1502 3676 : SetDescription(m_poFeatureDefn->GetName());
1503 3676 : m_poFeatureDefn->SetGeomType(wkbNone);
1504 3676 : m_poFeatureDefn->Reference();
1505 3676 : }
1506 :
1507 : /************************************************************************/
1508 : /* ~OGRGeoPackageTableLayer() */
1509 : /************************************************************************/
1510 :
1511 7352 : OGRGeoPackageTableLayer::~OGRGeoPackageTableLayer()
1512 : {
1513 3676 : OGRGeoPackageTableLayer::SyncToDisk();
1514 :
1515 : /* Clean up resources in memory */
1516 3676 : if (m_pszTableName)
1517 3676 : CPLFree(m_pszTableName);
1518 :
1519 3676 : if (m_poExtent)
1520 971 : delete m_poExtent;
1521 :
1522 3676 : if (m_poUpdateStatement)
1523 22 : sqlite3_finalize(m_poUpdateStatement);
1524 :
1525 3676 : if (m_poInsertStatement)
1526 434 : sqlite3_finalize(m_poInsertStatement);
1527 :
1528 3676 : if (m_poGetFeatureStatement)
1529 22 : sqlite3_finalize(m_poGetFeatureStatement);
1530 :
1531 3676 : CancelAsyncNextArrowArray();
1532 7352 : }
1533 :
1534 : /************************************************************************/
1535 : /* CancelAsyncNextArrowArray() */
1536 : /************************************************************************/
1537 :
1538 317307 : void OGRGeoPackageTableLayer::CancelAsyncNextArrowArray()
1539 : {
1540 317307 : if (m_poFillArrowArray)
1541 : {
1542 94 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
1543 47 : m_poFillArrowArray->nCountRows = -1;
1544 47 : m_poFillArrowArray->oCV.notify_one();
1545 : }
1546 :
1547 317307 : if (m_oThreadNextArrowArray.joinable())
1548 : {
1549 2 : m_oThreadNextArrowArray.join();
1550 : }
1551 :
1552 317307 : m_poFillArrowArray.reset();
1553 :
1554 317321 : while (!m_oQueueArrowArrayPrefetchTasks.empty())
1555 : {
1556 28 : auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
1557 14 : m_oQueueArrowArrayPrefetchTasks.pop();
1558 :
1559 : {
1560 28 : std::lock_guard oLock(task->m_oMutex);
1561 14 : task->m_bStop = true;
1562 14 : task->m_oCV.notify_one();
1563 : }
1564 14 : if (task->m_oThread.joinable())
1565 14 : task->m_oThread.join();
1566 :
1567 14 : if (task->m_psArrowArray)
1568 : {
1569 14 : if (task->m_psArrowArray->release)
1570 14 : task->m_psArrowArray->release(task->m_psArrowArray.get());
1571 : }
1572 : }
1573 317307 : }
1574 :
1575 : /************************************************************************/
1576 : /* InitView() */
1577 : /************************************************************************/
1578 :
1579 769 : void OGRGeoPackageTableLayer::InitView()
1580 : {
1581 : #ifdef SQLITE_HAS_COLUMN_METADATA
1582 769 : if (!m_bIsTable)
1583 : {
1584 : /* Detect if the view columns have the FID and geom columns of a */
1585 : /* table that has itself a spatial index */
1586 8 : sqlite3_stmt *hStmt = nullptr;
1587 8 : char *pszSQL = sqlite3_mprintf("SELECT * FROM \"%w\"", m_pszTableName);
1588 8 : CPL_IGNORE_RET_VAL(
1589 8 : sqlite3_prepare_v2(m_poDS->GetDB(), pszSQL, -1, &hStmt, nullptr));
1590 8 : sqlite3_free(pszSQL);
1591 8 : if (hStmt)
1592 : {
1593 8 : if (sqlite3_step(hStmt) == SQLITE_ROW)
1594 : {
1595 8 : OGRGeoPackageTableLayer *poLayerGeom = nullptr;
1596 8 : const int nRawColumns = sqlite3_column_count(hStmt);
1597 33 : for (int iCol = 0; iCol < nRawColumns; iCol++)
1598 : {
1599 : CPLString osColName(
1600 50 : SQLUnescape(sqlite3_column_name(hStmt, iCol)));
1601 : const char *pszTableName =
1602 25 : sqlite3_column_table_name(hStmt, iCol);
1603 : const char *pszOriginName =
1604 25 : sqlite3_column_origin_name(hStmt, iCol);
1605 26 : if (EQUAL(osColName, "OGC_FID") &&
1606 1 : (pszOriginName == nullptr ||
1607 1 : osColName != pszOriginName))
1608 : {
1609 : // in the case we have a OGC_FID column, and that
1610 : // is not the name of the original column, then
1611 : // interpret this as an explicit intent to be a
1612 : // PKID.
1613 : // We cannot just take the FID of a source table as
1614 : // a FID because of potential joins that would result
1615 : // in multiple records with same source FID.
1616 1 : CPLFree(m_pszFidColumn);
1617 1 : m_pszFidColumn = CPLStrdup(osColName);
1618 1 : m_poFeatureDefn->DeleteFieldDefn(
1619 1 : m_poFeatureDefn->GetFieldIndex(osColName));
1620 : }
1621 32 : else if (iCol == 0 &&
1622 8 : sqlite3_column_type(hStmt, iCol) == SQLITE_INTEGER)
1623 : {
1624 : // Assume the first column of integer type is the FID
1625 : // column per the latest requirements of the GPKG spec
1626 6 : CPLFree(m_pszFidColumn);
1627 6 : m_pszFidColumn = CPLStrdup(osColName);
1628 6 : m_poFeatureDefn->DeleteFieldDefn(
1629 6 : m_poFeatureDefn->GetFieldIndex(osColName));
1630 : }
1631 18 : else if (pszTableName != nullptr &&
1632 : pszOriginName != nullptr)
1633 : {
1634 : OGRGeoPackageTableLayer *poLayer =
1635 0 : dynamic_cast<OGRGeoPackageTableLayer *>(
1636 16 : m_poDS->GetLayerByName(pszTableName));
1637 16 : if (poLayer != nullptr &&
1638 32 : osColName == GetGeometryColumn() &&
1639 6 : strcmp(pszOriginName,
1640 : poLayer->GetGeometryColumn()) == 0)
1641 : {
1642 6 : poLayerGeom = poLayer;
1643 : }
1644 : }
1645 : }
1646 :
1647 8 : if (poLayerGeom != nullptr && poLayerGeom->HasSpatialIndex())
1648 : {
1649 9 : for (int iCol = 0; iCol < nRawColumns; iCol++)
1650 : {
1651 : const std::string osColName(
1652 9 : SQLUnescape(sqlite3_column_name(hStmt, iCol)));
1653 : const char *pszTableName =
1654 9 : sqlite3_column_table_name(hStmt, iCol);
1655 : const char *pszOriginName =
1656 9 : sqlite3_column_origin_name(hStmt, iCol);
1657 9 : if (pszTableName != nullptr && pszOriginName != nullptr)
1658 : {
1659 : OGRGeoPackageTableLayer *poLayer =
1660 0 : dynamic_cast<OGRGeoPackageTableLayer *>(
1661 8 : m_poDS->GetLayerByName(pszTableName));
1662 16 : if (poLayer != nullptr && poLayer == poLayerGeom &&
1663 8 : strcmp(pszOriginName,
1664 : poLayer->GetFIDColumn()) == 0)
1665 : {
1666 6 : m_bHasSpatialIndex = true;
1667 6 : m_osRTreeName = poLayerGeom->m_osRTreeName;
1668 6 : m_osFIDForRTree = osColName;
1669 6 : break;
1670 : }
1671 : }
1672 : }
1673 : }
1674 : }
1675 8 : sqlite3_finalize(hStmt);
1676 : }
1677 :
1678 : /* Update the columns string */
1679 8 : BuildColumns();
1680 : }
1681 : #endif
1682 769 : }
1683 :
1684 : /************************************************************************/
1685 : /* CheckUpdatableTable() */
1686 : /************************************************************************/
1687 :
1688 4467 : bool OGRGeoPackageTableLayer::CheckUpdatableTable(const char *pszOperation)
1689 : {
1690 4467 : if (!m_poDS->GetUpdate())
1691 : {
1692 4 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
1693 : pszOperation);
1694 4 : return false;
1695 : }
1696 : /* -------------------------------------------------------------------- */
1697 : /* Check that is a table and not a view */
1698 : /* -------------------------------------------------------------------- */
1699 4463 : if (!m_bIsTable)
1700 : {
1701 6 : CPLError(CE_Failure, CPLE_AppDefined, "Layer %s is not a table",
1702 : m_pszTableName);
1703 6 : return false;
1704 : }
1705 4457 : return true;
1706 : }
1707 :
1708 : /************************************************************************/
1709 : /* CreateField() */
1710 : /************************************************************************/
1711 :
1712 3741 : OGRErr OGRGeoPackageTableLayer::CreateField(const OGRFieldDefn *poField,
1713 : int /* bApproxOK */)
1714 : {
1715 3741 : if (!m_bFeatureDefnCompleted)
1716 0 : GetLayerDefn();
1717 3741 : if (!CheckUpdatableTable("CreateField"))
1718 1 : return OGRERR_FAILURE;
1719 :
1720 7480 : OGRFieldDefn oFieldDefn(poField);
1721 3740 : int nMaxWidth = 0;
1722 3740 : if (m_bPreservePrecision && poField->GetType() == OFTString)
1723 2548 : nMaxWidth = poField->GetWidth();
1724 : else
1725 1192 : oFieldDefn.SetWidth(0);
1726 3740 : oFieldDefn.SetPrecision(0);
1727 :
1728 3740 : if (m_bLaunder)
1729 1 : oFieldDefn.SetName(
1730 2 : GDALGeoPackageDataset::LaunderName(oFieldDefn.GetNameRef())
1731 : .c_str());
1732 :
1733 3740 : if (m_poFeatureDefn->GetFieldIndex(oFieldDefn.GetNameRef()) >= 0)
1734 : {
1735 2 : CPLError(CE_Failure, CPLE_AppDefined,
1736 : "Cannot create field %s. "
1737 : "A field with the same name already exists.",
1738 : oFieldDefn.GetNameRef());
1739 2 : return OGRERR_FAILURE;
1740 : }
1741 :
1742 3738 : if (m_poFeatureDefn->GetGeomFieldIndex(oFieldDefn.GetNameRef()) >= 0)
1743 : {
1744 1 : CPLError(CE_Failure, CPLE_AppDefined,
1745 : "Cannot create field %s. "
1746 : "It has the same name as the geometry field.",
1747 : oFieldDefn.GetNameRef());
1748 1 : return OGRERR_FAILURE;
1749 : }
1750 :
1751 11211 : if (m_pszFidColumn != nullptr &&
1752 3742 : EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn) &&
1753 8 : poField->GetType() != OFTInteger &&
1754 7478 : poField->GetType() != OFTInteger64 &&
1755 : // typically a GeoPackage exported with QGIS as a shapefile and
1756 : // re-imported See https://github.com/qgis/QGIS/pull/43118
1757 4 : !(poField->GetType() == OFTReal && poField->GetWidth() == 20 &&
1758 1 : poField->GetPrecision() == 0))
1759 : {
1760 1 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s",
1761 : oFieldDefn.GetNameRef());
1762 1 : return OGRERR_FAILURE;
1763 : }
1764 :
1765 : const int nMaxColumns =
1766 3736 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_COLUMN, -1);
1767 : // + 1 for the FID column
1768 3736 : if (m_poFeatureDefn->GetFieldCount() +
1769 3736 : m_poFeatureDefn->GetGeomFieldCount() + 1 >=
1770 : nMaxColumns)
1771 : {
1772 1 : CPLError(CE_Failure, CPLE_AppDefined,
1773 : "Cannot add field %s. Limit of %d columns reached",
1774 : oFieldDefn.GetNameRef(), nMaxColumns);
1775 1 : return OGRERR_FAILURE;
1776 : }
1777 :
1778 3735 : if (!m_bDeferredCreation)
1779 : {
1780 13 : CPLString osCommand;
1781 :
1782 : // ADD COLUMN has several restrictions
1783 : // See https://www.sqlite.org/lang_altertable.html#altertabaddcol
1784 :
1785 : osCommand.Printf("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s",
1786 26 : SQLEscapeName(m_pszTableName).c_str(),
1787 26 : SQLEscapeName(oFieldDefn.GetNameRef()).c_str(),
1788 : GPkgFieldFromOGR(poField->GetType(),
1789 39 : poField->GetSubType(), nMaxWidth));
1790 13 : if (!poField->IsNullable())
1791 1 : osCommand += " NOT NULL";
1792 13 : if (poField->IsUnique())
1793 : {
1794 : // this will fail when SQLCommand() is run, as it is not allowed
1795 : // by SQLite. This is a bit of an artificial restriction.
1796 : // We could override it by rewriting the table.
1797 1 : osCommand += " UNIQUE";
1798 : }
1799 16 : if (poField->GetDefault() != nullptr &&
1800 3 : !poField->IsDefaultDriverSpecific())
1801 : {
1802 3 : osCommand += " DEFAULT ";
1803 3 : int nYear = 0;
1804 3 : int nMonth = 0;
1805 3 : int nDay = 0;
1806 3 : int nHour = 0;
1807 3 : int nMinute = 0;
1808 3 : float fSecond = 0.0f;
1809 5 : if (poField->GetType() == OFTDateTime &&
1810 2 : sscanf(poField->GetDefault(), "'%d/%d/%d %d:%d:%f'", &nYear,
1811 : &nMonth, &nDay, &nHour, &nMinute, &fSecond) == 6)
1812 : {
1813 2 : if (strchr(poField->GetDefault(), '.') == nullptr)
1814 : osCommand += CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%02dZ'",
1815 : nYear, nMonth, nDay, nHour, nMinute,
1816 1 : static_cast<int>(fSecond + 0.5));
1817 : else
1818 : osCommand +=
1819 : CPLSPrintf("'%04d-%02d-%02dT%02d:%02d:%06.3fZ'", nYear,
1820 1 : nMonth, nDay, nHour, nMinute, fSecond);
1821 : }
1822 : else
1823 : {
1824 : // This could fail if it is CURRENT_TIMESTAMP, etc.
1825 1 : osCommand += poField->GetDefault();
1826 : }
1827 : }
1828 10 : else if (!poField->IsNullable())
1829 : {
1830 : // SQLite mandates a DEFAULT value when adding a NOT NULL column in
1831 : // an ALTER TABLE ADD COLUMN.
1832 1 : osCommand += " DEFAULT ''";
1833 : }
1834 :
1835 13 : OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
1836 :
1837 13 : if (err != OGRERR_NONE)
1838 1 : return err;
1839 :
1840 12 : if (!DoSpecialProcessingForColumnCreation(poField))
1841 : {
1842 0 : return OGRERR_FAILURE;
1843 : }
1844 : }
1845 :
1846 3734 : whileUnsealing(m_poFeatureDefn)->AddFieldDefn(&oFieldDefn);
1847 :
1848 3734 : if (m_poDS->IsInTransaction())
1849 : {
1850 : m_apoFieldDefnChanges.emplace_back(
1851 362 : std::make_unique<OGRFieldDefn>(oFieldDefn),
1852 362 : m_poFeatureDefn->GetFieldCount() - 1, FieldChangeType::ADD_FIELD,
1853 1086 : /* bGenerated */ false);
1854 : }
1855 :
1856 3734 : m_abGeneratedColumns.resize(m_poFeatureDefn->GetFieldCount());
1857 3734 : m_abGeneratedColumns.back() = false; // explicit is better than implicit
1858 :
1859 7468 : if (m_pszFidColumn != nullptr &&
1860 3734 : EQUAL(oFieldDefn.GetNameRef(), m_pszFidColumn))
1861 : {
1862 4 : m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount() - 1;
1863 : }
1864 :
1865 3734 : if (!m_bDeferredCreation)
1866 : {
1867 12 : ResetReading();
1868 : }
1869 :
1870 3734 : return OGRERR_NONE;
1871 : }
1872 :
1873 : /************************************************************************/
1874 : /* DoSpecialProcessingForColumnCreation() */
1875 : /************************************************************************/
1876 :
1877 3742 : bool OGRGeoPackageTableLayer::DoSpecialProcessingForColumnCreation(
1878 : const OGRFieldDefn *poField)
1879 : {
1880 3742 : const std::string &osConstraintName(poField->GetDomainName());
1881 7484 : const std::string osName(poField->GetAlternativeNameRef());
1882 3742 : const std::string &osDescription(poField->GetComment());
1883 :
1884 7484 : std::string osMimeType;
1885 3742 : if (poField->GetType() == OFTString && poField->GetSubType() == OFSTJSON)
1886 : {
1887 13 : osMimeType = "application/json";
1888 : }
1889 :
1890 7465 : if (osConstraintName.empty() && osName.empty() && osDescription.empty() &&
1891 3723 : osMimeType.empty())
1892 : {
1893 : // no record required
1894 3710 : return true;
1895 : }
1896 :
1897 32 : if (!m_poDS->CreateColumnsTableAndColumnConstraintsTablesIfNecessary())
1898 0 : return false;
1899 :
1900 : /* Now let's register our column. */
1901 64 : std::string osNameSqlValue;
1902 32 : if (osName.empty())
1903 : {
1904 23 : osNameSqlValue = "NULL";
1905 : }
1906 : else
1907 : {
1908 9 : char *pszName = sqlite3_mprintf("'%q'", osName.c_str());
1909 9 : osNameSqlValue = std::string(pszName);
1910 9 : sqlite3_free(pszName);
1911 : }
1912 :
1913 64 : std::string osDescriptionSqlValue;
1914 32 : if (osDescription.empty())
1915 : {
1916 28 : osDescriptionSqlValue = "NULL";
1917 : }
1918 : else
1919 : {
1920 4 : char *pszDescription = sqlite3_mprintf("'%q'", osDescription.c_str());
1921 4 : osDescriptionSqlValue = std::string(pszDescription);
1922 4 : sqlite3_free(pszDescription);
1923 : }
1924 :
1925 64 : std::string osMimeTypeSqlValue;
1926 32 : if (osMimeType.empty())
1927 : {
1928 19 : osMimeTypeSqlValue = "NULL";
1929 : }
1930 : else
1931 : {
1932 13 : char *pszMimeType = sqlite3_mprintf("'%q'", osMimeType.c_str());
1933 13 : osMimeTypeSqlValue = std::string(pszMimeType);
1934 13 : sqlite3_free(pszMimeType);
1935 : }
1936 :
1937 32 : std::string osConstraintNameValue;
1938 32 : if (osConstraintName.empty())
1939 : {
1940 23 : osConstraintNameValue = "NULL";
1941 : }
1942 : else
1943 : {
1944 : char *pszConstraintName =
1945 9 : sqlite3_mprintf("'%q'", osConstraintName.c_str());
1946 9 : osConstraintNameValue = std::string(pszConstraintName);
1947 9 : sqlite3_free(pszConstraintName);
1948 : }
1949 :
1950 32 : char *pszSQL = sqlite3_mprintf(
1951 : "INSERT INTO gpkg_data_columns (table_name, column_name, name, "
1952 : "title, description, mime_type, constraint_name) VALUES ("
1953 : "'%q', '%q', %s, NULL, %s, %s, %s)",
1954 : m_pszTableName, poField->GetNameRef(), osNameSqlValue.c_str(),
1955 : osDescriptionSqlValue.c_str(), osMimeTypeSqlValue.c_str(),
1956 : osConstraintNameValue.c_str());
1957 :
1958 32 : bool ok = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
1959 32 : sqlite3_free(pszSQL);
1960 32 : return ok;
1961 : }
1962 :
1963 : /************************************************************************/
1964 : /* CreateGeomField() */
1965 : /************************************************************************/
1966 :
1967 : OGRErr
1968 5 : OGRGeoPackageTableLayer::CreateGeomField(const OGRGeomFieldDefn *poGeomFieldIn,
1969 : int /* bApproxOK */)
1970 : {
1971 5 : if (!m_bFeatureDefnCompleted)
1972 1 : GetLayerDefn();
1973 5 : if (!CheckUpdatableTable("CreateGeomField"))
1974 1 : return OGRERR_FAILURE;
1975 :
1976 4 : if (m_poFeatureDefn->GetGeomFieldCount() == 1)
1977 : {
1978 1 : CPLError(CE_Failure, CPLE_AppDefined,
1979 : "Cannot create more than one geometry field in GeoPackage");
1980 1 : return OGRERR_FAILURE;
1981 : }
1982 :
1983 3 : OGRwkbGeometryType eType = poGeomFieldIn->GetType();
1984 3 : if (eType == wkbNone)
1985 : {
1986 0 : CPLError(CE_Failure, CPLE_AppDefined,
1987 : "Cannot create geometry field of type wkbNone");
1988 0 : return OGRERR_FAILURE;
1989 : }
1990 :
1991 6 : OGRGeomFieldDefn oGeomField(poGeomFieldIn);
1992 3 : auto poSRSOri = poGeomFieldIn->GetSpatialRef();
1993 3 : if (poSRSOri)
1994 : {
1995 0 : auto poSRS = poSRSOri->Clone();
1996 0 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1997 0 : oGeomField.SetSpatialRef(poSRS);
1998 0 : poSRS->Release();
1999 : }
2000 3 : if (EQUAL(oGeomField.GetNameRef(), ""))
2001 : {
2002 1 : oGeomField.SetName("geom");
2003 : }
2004 :
2005 3 : const OGRSpatialReference *poSRS = oGeomField.GetSpatialRef();
2006 3 : m_iSrs = m_poDS->GetSrsId(poSRS);
2007 :
2008 : /* -------------------------------------------------------------------- */
2009 : /* Create the new field. */
2010 : /* -------------------------------------------------------------------- */
2011 3 : if (!m_bDeferredCreation)
2012 : {
2013 4 : char *pszSQL = sqlite3_mprintf(
2014 : "ALTER TABLE \"%w\" ADD COLUMN \"%w\" %s%s"
2015 : ";"
2016 : "UPDATE gpkg_contents SET data_type = 'features' "
2017 : "WHERE lower(table_name) = lower('%q')",
2018 : m_pszTableName, oGeomField.GetNameRef(),
2019 2 : m_poDS->GetGeometryTypeString(oGeomField.GetType()),
2020 2 : !oGeomField.IsNullable() ? " NOT NULL DEFAULT ''" : "",
2021 : m_pszTableName);
2022 2 : CPLString osSQL(pszSQL);
2023 2 : sqlite3_free(pszSQL);
2024 :
2025 2 : OGRErr err = SQLCommand(m_poDS->GetDB(), osSQL);
2026 2 : if (err != OGRERR_NONE)
2027 0 : return err;
2028 : }
2029 :
2030 3 : if (m_poDS->IsInTransaction())
2031 : {
2032 : m_apoGeomFieldDefnChanges.emplace_back(
2033 0 : std::make_unique<OGRGeomFieldDefn>(oGeomField),
2034 0 : m_poFeatureDefn->GetGeomFieldCount(), FieldChangeType::ADD_FIELD,
2035 0 : /* bGenerated */ false);
2036 : }
2037 :
2038 3 : whileUnsealing(m_poFeatureDefn)->AddGeomFieldDefn(&oGeomField);
2039 :
2040 3 : if (!m_bDeferredCreation)
2041 : {
2042 2 : OGRErr err = RegisterGeometryColumn();
2043 2 : if (err != OGRERR_NONE)
2044 0 : return err;
2045 :
2046 2 : ResetReading();
2047 : }
2048 :
2049 3 : return OGRERR_NONE;
2050 : }
2051 :
2052 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2053 :
2054 : /************************************************************************/
2055 : /* DisableFeatureCount() */
2056 : /************************************************************************/
2057 :
2058 191 : void OGRGeoPackageTableLayer::DisableFeatureCount()
2059 : {
2060 191 : m_nTotalFeatureCount = -1;
2061 191 : }
2062 :
2063 : /************************************************************************/
2064 : /* CreateFeatureCountTriggers() */
2065 : /************************************************************************/
2066 :
2067 4196 : void OGRGeoPackageTableLayer::CreateFeatureCountTriggers(
2068 : const char *pszTableName)
2069 : {
2070 4196 : if (m_bAddOGRFeatureCountTriggers)
2071 : {
2072 727 : if (pszTableName == nullptr)
2073 718 : pszTableName = m_pszTableName;
2074 :
2075 727 : m_bOGRFeatureCountTriggersEnabled = true;
2076 727 : m_bAddOGRFeatureCountTriggers = false;
2077 727 : m_bFeatureCountTriggersDeletedInTransaction = false;
2078 :
2079 727 : CPLDebug("GPKG", "Creating insert/delete feature_count triggers");
2080 727 : char *pszSQL = sqlite3_mprintf(
2081 : "CREATE TRIGGER \"trigger_insert_feature_count_%w\" "
2082 : "AFTER INSERT ON \"%w\" "
2083 : "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
2084 : "feature_count + 1 WHERE lower(table_name) = lower('%q'); END;",
2085 : pszTableName, pszTableName, pszTableName);
2086 727 : SQLCommand(m_poDS->GetDB(), pszSQL);
2087 727 : sqlite3_free(pszSQL);
2088 :
2089 727 : pszSQL = sqlite3_mprintf(
2090 : "CREATE TRIGGER \"trigger_delete_feature_count_%w\" "
2091 : "AFTER DELETE ON \"%w\" "
2092 : "BEGIN UPDATE gpkg_ogr_contents SET feature_count = "
2093 : "feature_count - 1 WHERE lower(table_name) = lower('%q'); END;",
2094 : pszTableName, pszTableName, pszTableName);
2095 727 : SQLCommand(m_poDS->GetDB(), pszSQL);
2096 727 : sqlite3_free(pszSQL);
2097 : }
2098 4196 : }
2099 :
2100 : /************************************************************************/
2101 : /* DisableFeatureCountTriggers() */
2102 : /************************************************************************/
2103 :
2104 58 : void OGRGeoPackageTableLayer::DisableFeatureCountTriggers(
2105 : bool bNullifyFeatureCount)
2106 : {
2107 58 : if (m_bOGRFeatureCountTriggersEnabled)
2108 : {
2109 58 : m_bOGRFeatureCountTriggersEnabled = false;
2110 58 : m_bAddOGRFeatureCountTriggers = true;
2111 58 : m_bFeatureCountTriggersDeletedInTransaction = m_poDS->IsInTransaction();
2112 :
2113 58 : CPLDebug("GPKG", "Deleting insert/delete feature_count triggers");
2114 :
2115 58 : char *pszSQL = sqlite3_mprintf(
2116 : "DROP TRIGGER \"trigger_insert_feature_count_%w\"", m_pszTableName);
2117 58 : SQLCommand(m_poDS->GetDB(), pszSQL);
2118 58 : sqlite3_free(pszSQL);
2119 :
2120 58 : pszSQL = sqlite3_mprintf(
2121 : "DROP TRIGGER \"trigger_delete_feature_count_%w\"", m_pszTableName);
2122 58 : SQLCommand(m_poDS->GetDB(), pszSQL);
2123 58 : sqlite3_free(pszSQL);
2124 :
2125 58 : if (m_poDS->m_bHasGPKGOGRContents && bNullifyFeatureCount)
2126 : {
2127 49 : pszSQL = sqlite3_mprintf(
2128 : "UPDATE gpkg_ogr_contents SET feature_count = NULL WHERE "
2129 : "lower(table_name )= lower('%q')",
2130 : m_pszTableName);
2131 49 : SQLCommand(m_poDS->GetDB(), pszSQL);
2132 49 : sqlite3_free(pszSQL);
2133 : }
2134 : }
2135 58 : }
2136 :
2137 : #endif // #ifdef ENABLE_GPKG_OGR_CONTENTS
2138 :
2139 : /************************************************************************/
2140 : /* CheckGeometryType() */
2141 : /************************************************************************/
2142 :
2143 : /** Check that the feature geometry type is consistent with the layer geometry
2144 : * type.
2145 : *
2146 : * And potentially update the Z and M flags of gpkg_geometry_columns to
2147 : * reflect the dimensionality of feature geometries.
2148 : */
2149 253238 : void OGRGeoPackageTableLayer::CheckGeometryType(const OGRFeature *poFeature)
2150 : {
2151 253238 : const OGRwkbGeometryType eLayerGeomType = GetGeomType();
2152 253238 : const OGRwkbGeometryType eFlattenLayerGeomType = wkbFlatten(eLayerGeomType);
2153 253238 : const OGRGeometry *poGeom = poFeature->GetGeometryRef();
2154 253238 : if (eFlattenLayerGeomType != wkbNone && eFlattenLayerGeomType != wkbUnknown)
2155 : {
2156 5682 : if (poGeom != nullptr)
2157 : {
2158 : OGRwkbGeometryType eGeomType =
2159 5566 : wkbFlatten(poGeom->getGeometryType());
2160 5582 : if (!OGR_GT_IsSubClassOf(eGeomType, eFlattenLayerGeomType) &&
2161 16 : !cpl::contains(m_eSetBadGeomTypeWarned, eGeomType))
2162 : {
2163 15 : CPLError(CE_Warning, CPLE_AppDefined,
2164 : "A geometry of type %s is inserted into layer %s "
2165 : "of geometry type %s, which is not normally allowed "
2166 : "by the GeoPackage specification, but the driver will "
2167 : "however do it. "
2168 : "To create a conformant GeoPackage, if using ogr2ogr, "
2169 : "the -nlt option can be used to override the layer "
2170 : "geometry type. "
2171 : "This warning will no longer be emitted for this "
2172 : "combination of layer and feature geometry type.",
2173 : OGRToOGCGeomType(eGeomType), GetName(),
2174 : OGRToOGCGeomType(eFlattenLayerGeomType));
2175 15 : m_eSetBadGeomTypeWarned.insert(eGeomType);
2176 : }
2177 : }
2178 : }
2179 :
2180 : // Make sure to update the z and m columns of gpkg_geometry_columns to 2
2181 : // if we have geometries with Z and M components
2182 253238 : if (m_nZFlag == 0 || m_nMFlag == 0)
2183 : {
2184 253234 : if (poGeom != nullptr)
2185 : {
2186 251841 : bool bUpdateGpkgGeometryColumnsTable = false;
2187 251841 : const OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
2188 251841 : if (m_nZFlag == 0 && wkbHasZ(eGeomType))
2189 : {
2190 11 : if (eLayerGeomType != wkbUnknown && !wkbHasZ(eLayerGeomType))
2191 : {
2192 2 : CPLError(
2193 : CE_Warning, CPLE_AppDefined,
2194 : "Layer '%s' has been declared with non-Z geometry type "
2195 : "%s, but it does contain geometries with Z. Setting "
2196 : "the Z=2 hint into gpkg_geometry_columns",
2197 : GetName(),
2198 : OGRToOGCGeomType(eLayerGeomType, true, true, true));
2199 : }
2200 11 : m_nZFlag = 2;
2201 11 : bUpdateGpkgGeometryColumnsTable = true;
2202 : }
2203 251841 : if (m_nMFlag == 0 && wkbHasM(eGeomType))
2204 : {
2205 8 : if (eLayerGeomType != wkbUnknown && !wkbHasM(eLayerGeomType))
2206 : {
2207 1 : CPLError(
2208 : CE_Warning, CPLE_AppDefined,
2209 : "Layer '%s' has been declared with non-M geometry type "
2210 : "%s, but it does contain geometries with M. Setting "
2211 : "the M=2 hint into gpkg_geometry_columns",
2212 : GetName(),
2213 : OGRToOGCGeomType(eLayerGeomType, true, true, true));
2214 : }
2215 8 : m_nMFlag = 2;
2216 8 : bUpdateGpkgGeometryColumnsTable = true;
2217 : }
2218 251841 : if (bUpdateGpkgGeometryColumnsTable)
2219 : {
2220 : /* Update gpkg_geometry_columns */
2221 14 : char *pszSQL = sqlite3_mprintf(
2222 : "UPDATE gpkg_geometry_columns SET z = %d, m = %d WHERE "
2223 : "table_name = '%q' AND column_name = '%q'",
2224 : m_nZFlag, m_nMFlag, GetName(), GetGeometryColumn());
2225 14 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
2226 14 : sqlite3_free(pszSQL);
2227 : }
2228 : }
2229 : }
2230 253238 : }
2231 :
2232 : /************************************************************************/
2233 : /* CheckFIDAndFIDColumnConsistency() */
2234 : /************************************************************************/
2235 :
2236 16 : static bool CheckFIDAndFIDColumnConsistency(const OGRFeature *poFeature,
2237 : int iFIDAsRegularColumnIndex)
2238 : {
2239 16 : bool ok = true;
2240 16 : if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
2241 : {
2242 : // nothing to do
2243 : }
2244 14 : else if (poFeature->GetDefnRef()
2245 14 : ->GetFieldDefn(iFIDAsRegularColumnIndex)
2246 14 : ->GetType() == OFTReal)
2247 : {
2248 : const double dfFID =
2249 4 : poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex);
2250 4 : if (GDALIsValueInRange<int64_t>(dfFID))
2251 : {
2252 4 : const auto nFID = static_cast<GIntBig>(dfFID);
2253 4 : if (nFID != poFeature->GetFID())
2254 : {
2255 1 : ok = false;
2256 1 : CPLError(CE_Failure, CPLE_AppDefined,
2257 : "Inconsistent values of FID (" CPL_FRMT_GIB
2258 : ") and field of same name (%g)",
2259 : poFeature->GetFID(),
2260 : poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex));
2261 : }
2262 : }
2263 : }
2264 20 : else if (poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) !=
2265 10 : poFeature->GetFID())
2266 : {
2267 3 : ok = false;
2268 3 : CPLError(CE_Failure, CPLE_AppDefined,
2269 : "Inconsistent values of FID (" CPL_FRMT_GIB
2270 : ") and field of same name (" CPL_FRMT_GIB ")",
2271 : poFeature->GetFID(),
2272 : poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex));
2273 : }
2274 16 : return ok;
2275 : }
2276 :
2277 : /************************************************************************/
2278 : /* ICreateFeature() */
2279 : /************************************************************************/
2280 :
2281 : // rtreeValueDown() / rtreeValueUp() come from SQLite3 source code
2282 : // SQLite3 RTree stores min/max values as float. So do the same for our
2283 : // GPKGRTreeEntry
2284 :
2285 : /*
2286 : ** Rounding constants for float->double conversion.
2287 : */
2288 : #define RNDTOWARDS (1.0 - 1.0 / 8388608.0) /* Round towards zero */
2289 : #define RNDAWAY (1.0 + 1.0 / 8388608.0) /* Round away from zero */
2290 :
2291 : /*
2292 : ** Convert an sqlite3_value into an RtreeValue (presumably a float)
2293 : ** while taking care to round toward negative or positive, respectively.
2294 : */
2295 474248 : static float rtreeValueDown(double d)
2296 : {
2297 474248 : float f = static_cast<float>(d);
2298 474248 : if (f > d)
2299 : {
2300 10047 : f = static_cast<float>(d * (d < 0 ? RNDAWAY : RNDTOWARDS));
2301 : }
2302 474248 : return f;
2303 : }
2304 :
2305 474248 : static float rtreeValueUp(double d)
2306 : {
2307 474248 : float f = static_cast<float>(d);
2308 474248 : if (f < d)
2309 : {
2310 10045 : f = static_cast<float>(d * (d < 0 ? RNDTOWARDS : RNDAWAY));
2311 : }
2312 474248 : return f;
2313 : }
2314 :
2315 253170 : OGRErr OGRGeoPackageTableLayer::CreateOrUpsertFeature(OGRFeature *poFeature,
2316 : bool bUpsert)
2317 : {
2318 253170 : if (!m_bFeatureDefnCompleted)
2319 0 : GetLayerDefn();
2320 253170 : if (!m_poDS->GetUpdate())
2321 : {
2322 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
2323 : "CreateFeature");
2324 0 : return OGRERR_FAILURE;
2325 : }
2326 :
2327 253170 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
2328 0 : return OGRERR_FAILURE;
2329 :
2330 253170 : CancelAsyncNextArrowArray();
2331 :
2332 506340 : std::string osUpsertUniqueColumnName;
2333 253170 : if (bUpsert && poFeature->GetFID() == OGRNullFID)
2334 : {
2335 13 : int nUniqueColumns = 0;
2336 13 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2337 39 : for (int i = 0; i < nFieldCount; ++i)
2338 : {
2339 26 : const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
2340 26 : if (poFieldDefn->IsUnique())
2341 : {
2342 13 : if (osUpsertUniqueColumnName.empty())
2343 13 : osUpsertUniqueColumnName = poFieldDefn->GetNameRef();
2344 13 : nUniqueColumns++;
2345 : }
2346 : }
2347 13 : if (nUniqueColumns == 0)
2348 : {
2349 : // This is just a regular INSERT
2350 0 : bUpsert = false;
2351 : }
2352 : }
2353 :
2354 253170 : if (bUpsert)
2355 : {
2356 25 : if (m_bThreadRTreeStarted)
2357 0 : CancelAsyncRTree();
2358 25 : if (!RunDeferredSpatialIndexUpdate())
2359 0 : return OGRERR_FAILURE;
2360 25 : if (!m_bUpdate1TriggerDisabled && HasSpatialIndex())
2361 17 : WorkaroundUpdate1TriggerIssue();
2362 : }
2363 :
2364 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2365 253170 : if (bUpsert)
2366 : {
2367 25 : if (m_nTotalFeatureCount >= 0)
2368 : {
2369 : // There's no reliable way of knowing if a new row has been inserted
2370 : // or just updated, so serialize known value and then
2371 : // invalidate feature count.
2372 9 : if (m_poDS->m_bHasGPKGOGRContents)
2373 : {
2374 : const char *pszCount =
2375 9 : CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
2376 9 : char *pszSQL = sqlite3_mprintf(
2377 : "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
2378 : "lower(table_name )= lower('%q')",
2379 : pszCount, m_pszTableName);
2380 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
2381 9 : sqlite3_free(pszSQL);
2382 : }
2383 9 : m_nTotalFeatureCount = -1;
2384 :
2385 9 : if (!m_bOGRFeatureCountTriggersEnabled)
2386 1 : CreateFeatureCountTriggers();
2387 : }
2388 : }
2389 : else
2390 : {
2391 : // To maximize performance of insertion, disable feature count triggers
2392 253145 : if (m_bOGRFeatureCountTriggersEnabled)
2393 : {
2394 33 : DisableFeatureCountTriggers();
2395 : }
2396 : }
2397 : #endif
2398 :
2399 253170 : CheckGeometryType(poFeature);
2400 :
2401 : /* Substitute default values for null Date/DateTime fields as the standard
2402 : */
2403 : /* format of SQLite is not the one mandated by GeoPackage */
2404 253170 : poFeature->FillUnsetWithDefault(FALSE, nullptr);
2405 253170 : bool bHasDefaultValue = false;
2406 253170 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
2407 4661980 : for (int iField = 0; iField < nFieldCount; iField++)
2408 : {
2409 4408810 : if (poFeature->IsFieldSetUnsafe(iField))
2410 4407720 : continue;
2411 : const char *pszDefault =
2412 1091 : m_poFeatureDefn->GetFieldDefnUnsafe(iField)->GetDefault();
2413 1091 : if (pszDefault != nullptr)
2414 : {
2415 1 : bHasDefaultValue = true;
2416 : }
2417 : }
2418 :
2419 : /* In case the FID column has also been created as a regular field */
2420 253170 : if (m_iFIDAsRegularColumnIndex >= 0)
2421 : {
2422 13 : if (poFeature->GetFID() == OGRNullFID)
2423 : {
2424 6 : if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
2425 : {
2426 10 : if (m_poFeatureDefn->GetFieldDefn(m_iFIDAsRegularColumnIndex)
2427 5 : ->GetType() == OFTReal)
2428 : {
2429 2 : bool ok = false;
2430 : const double dfFID =
2431 2 : poFeature->GetFieldAsDouble(m_iFIDAsRegularColumnIndex);
2432 4 : if (dfFID >= static_cast<double>(
2433 4 : std::numeric_limits<int64_t>::min()) &&
2434 2 : dfFID <= static_cast<double>(
2435 2 : std::numeric_limits<int64_t>::max()))
2436 : {
2437 2 : const auto nFID = static_cast<GIntBig>(dfFID);
2438 2 : if (static_cast<double>(nFID) == dfFID)
2439 : {
2440 1 : poFeature->SetFID(nFID);
2441 1 : ok = true;
2442 : }
2443 : }
2444 2 : if (!ok)
2445 : {
2446 1 : CPLError(
2447 : CE_Failure, CPLE_AppDefined,
2448 : "Value of FID %g cannot be parsed to an Integer64",
2449 : dfFID);
2450 1 : return OGRERR_FAILURE;
2451 : }
2452 : }
2453 : else
2454 : {
2455 3 : poFeature->SetFID(poFeature->GetFieldAsInteger64(
2456 3 : m_iFIDAsRegularColumnIndex));
2457 : }
2458 : }
2459 : }
2460 7 : else if (!CheckFIDAndFIDColumnConsistency(poFeature,
2461 : m_iFIDAsRegularColumnIndex))
2462 : {
2463 3 : return OGRERR_FAILURE;
2464 : }
2465 : }
2466 :
2467 : /* If there's a unset field with a default value, then we must create */
2468 : /* a specific INSERT statement to avoid unset fields to be bound to NULL */
2469 505781 : if (m_poInsertStatement &&
2470 252615 : (bHasDefaultValue ||
2471 252615 : m_bInsertStatementWithFID != (poFeature->GetFID() != OGRNullFID) ||
2472 505226 : m_bInsertStatementWithUpsert != bUpsert ||
2473 252613 : m_osInsertStatementUpsertUniqueColumnName != osUpsertUniqueColumnName))
2474 : {
2475 2 : sqlite3_finalize(m_poInsertStatement);
2476 2 : m_poInsertStatement = nullptr;
2477 : }
2478 :
2479 253166 : if (!m_poInsertStatement)
2480 : {
2481 : /* Construct a SQL INSERT statement from the OGRFeature */
2482 : /* Only work with fields that are set */
2483 : /* Do not stick values into SQL, use placeholder and bind values later
2484 : */
2485 553 : m_bInsertStatementWithFID = poFeature->GetFID() != OGRNullFID;
2486 553 : m_bInsertStatementWithUpsert = bUpsert;
2487 553 : m_osInsertStatementUpsertUniqueColumnName = osUpsertUniqueColumnName;
2488 : CPLString osCommand = FeatureGenerateInsertSQL(
2489 553 : poFeature, m_bInsertStatementWithFID, !bHasDefaultValue, bUpsert,
2490 553 : osUpsertUniqueColumnName);
2491 :
2492 : /* Prepare the SQL into a statement */
2493 553 : sqlite3 *poDb = m_poDS->GetDB();
2494 553 : int err = sqlite3_prepare_v2(poDb, osCommand, -1, &m_poInsertStatement,
2495 : nullptr);
2496 553 : if (err != SQLITE_OK)
2497 : {
2498 0 : CPLError(CE_Failure, CPLE_AppDefined,
2499 : "failed to prepare SQL: %s - %s", osCommand.c_str(),
2500 : sqlite3_errmsg(poDb));
2501 0 : return OGRERR_FAILURE;
2502 : }
2503 : }
2504 :
2505 : /* Bind values onto the statement now */
2506 506332 : OGRErr errOgr = FeatureBindInsertParameters(poFeature, m_poInsertStatement,
2507 253166 : m_bInsertStatementWithFID,
2508 253166 : !bHasDefaultValue);
2509 253166 : if (errOgr != OGRERR_NONE)
2510 : {
2511 0 : sqlite3_reset(m_poInsertStatement);
2512 0 : sqlite3_clear_bindings(m_poInsertStatement);
2513 0 : sqlite3_finalize(m_poInsertStatement);
2514 0 : m_poInsertStatement = nullptr;
2515 0 : return errOgr;
2516 : }
2517 :
2518 : /* From here execute the statement and check errors */
2519 253166 : const int err = sqlite3_step(m_poInsertStatement);
2520 253166 : if (!(err == SQLITE_OK || err == SQLITE_DONE
2521 : #if SQLITE_VERSION_NUMBER >= 3035000L
2522 : || err == SQLITE_ROW
2523 : #endif
2524 : ))
2525 : {
2526 6 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute insert : %s",
2527 6 : sqlite3_errmsg(m_poDS->GetDB())
2528 6 : ? sqlite3_errmsg(m_poDS->GetDB())
2529 : : "");
2530 6 : sqlite3_reset(m_poInsertStatement);
2531 6 : sqlite3_clear_bindings(m_poInsertStatement);
2532 6 : sqlite3_finalize(m_poInsertStatement);
2533 6 : m_poInsertStatement = nullptr;
2534 6 : return OGRERR_FAILURE;
2535 : }
2536 :
2537 : /* Read the latest FID value */
2538 25 : const GIntBig nFID = (bUpsert && !osUpsertUniqueColumnName.empty())
2539 253160 : ?
2540 : #if SQLITE_VERSION_NUMBER >= 3035000L
2541 13 : sqlite3_column_int64(m_poInsertStatement, 0)
2542 : #else
2543 : OGRNullFID
2544 : #endif
2545 253147 : : sqlite3_last_insert_rowid(m_poDS->GetDB());
2546 :
2547 253160 : sqlite3_reset(m_poInsertStatement);
2548 253160 : sqlite3_clear_bindings(m_poInsertStatement);
2549 :
2550 253160 : if (bHasDefaultValue)
2551 : {
2552 1 : sqlite3_finalize(m_poInsertStatement);
2553 1 : m_poInsertStatement = nullptr;
2554 : }
2555 :
2556 253160 : if (nFID != OGRNullFID)
2557 : {
2558 253160 : poFeature->SetFID(nFID);
2559 253160 : if (m_iFIDAsRegularColumnIndex >= 0)
2560 9 : poFeature->SetField(m_iFIDAsRegularColumnIndex, nFID);
2561 : }
2562 : else
2563 : {
2564 0 : poFeature->SetFID(OGRNullFID);
2565 : }
2566 :
2567 : /* Update the layer extents with this new object */
2568 253160 : if (IsGeomFieldSet(poFeature))
2569 : {
2570 251798 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
2571 251798 : if (!poGeom->IsEmpty())
2572 : {
2573 250771 : OGREnvelope oEnv;
2574 250771 : poGeom->getEnvelope(&oEnv);
2575 250771 : UpdateExtent(&oEnv);
2576 :
2577 250758 : if (!bUpsert && !m_bDeferredSpatialIndexCreation &&
2578 501529 : HasSpatialIndex() && m_poDS->IsInTransaction())
2579 : {
2580 315 : m_nCountInsertInTransaction++;
2581 315 : if (m_nCountInsertInTransactionThreshold < 0)
2582 : {
2583 9 : m_nCountInsertInTransactionThreshold =
2584 9 : atoi(CPLGetConfigOption(
2585 : "OGR_GPKG_DEFERRED_SPI_UPDATE_THRESHOLD", "100"));
2586 : }
2587 315 : if (m_nCountInsertInTransaction ==
2588 315 : m_nCountInsertInTransactionThreshold)
2589 : {
2590 7 : StartDeferredSpatialIndexUpdate();
2591 : }
2592 308 : else if (!m_aoRTreeTriggersSQL.empty())
2593 : {
2594 187 : if (m_aoRTreeEntries.size() == 1000 * 1000)
2595 : {
2596 0 : if (!FlushPendingSpatialIndexUpdate())
2597 0 : return OGRERR_FAILURE;
2598 : }
2599 : GPKGRTreeEntry sEntry;
2600 187 : sEntry.nId = nFID;
2601 187 : sEntry.fMinX = rtreeValueDown(oEnv.MinX);
2602 187 : sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
2603 187 : sEntry.fMinY = rtreeValueDown(oEnv.MinY);
2604 187 : sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
2605 187 : m_aoRTreeEntries.push_back(sEntry);
2606 : }
2607 : }
2608 250456 : else if (!bUpsert && m_bAllowedRTreeThread &&
2609 239464 : !m_bErrorDuringRTreeThread)
2610 : {
2611 : GPKGRTreeEntry sEntry;
2612 : #ifdef DEBUG_VERBOSE
2613 : if (m_aoRTreeEntries.empty())
2614 : CPLDebug("GPKG",
2615 : "Starting to fill m_aoRTreeEntries at "
2616 : "FID " CPL_FRMT_GIB,
2617 : nFID);
2618 : #endif
2619 236937 : sEntry.nId = nFID;
2620 236937 : sEntry.fMinX = rtreeValueDown(oEnv.MinX);
2621 236937 : sEntry.fMaxX = rtreeValueUp(oEnv.MaxX);
2622 236937 : sEntry.fMinY = rtreeValueDown(oEnv.MinY);
2623 236937 : sEntry.fMaxY = rtreeValueUp(oEnv.MaxY);
2624 : try
2625 : {
2626 236937 : m_aoRTreeEntries.push_back(sEntry);
2627 236937 : if (m_aoRTreeEntries.size() == m_nRTreeBatchSize)
2628 : {
2629 1004 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
2630 1004 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
2631 : }
2632 464509 : if (!m_bThreadRTreeStarted &&
2633 227572 : m_oQueueRTreeEntries.size() ==
2634 227572 : m_nRTreeBatchesBeforeStart)
2635 : {
2636 48 : StartAsyncRTree();
2637 : }
2638 : }
2639 0 : catch (const std::bad_alloc &)
2640 : {
2641 0 : CPLDebug("GPKG",
2642 : "Memory allocation error regarding RTree "
2643 : "structures. Falling back to slower method");
2644 0 : if (m_bThreadRTreeStarted)
2645 0 : CancelAsyncRTree();
2646 : else
2647 0 : m_bAllowedRTreeThread = false;
2648 : }
2649 : }
2650 : }
2651 : }
2652 :
2653 : #ifdef ENABLE_GPKG_OGR_CONTENTS
2654 253160 : if (m_nTotalFeatureCount >= 0)
2655 253096 : m_nTotalFeatureCount++;
2656 : #endif
2657 :
2658 253160 : m_bContentChanged = true;
2659 :
2660 : /* All done! */
2661 253160 : return OGRERR_NONE;
2662 : }
2663 :
2664 253145 : OGRErr OGRGeoPackageTableLayer::ICreateFeature(OGRFeature *poFeature)
2665 : {
2666 253145 : return CreateOrUpsertFeature(poFeature, /* bUpsert=*/false);
2667 : }
2668 :
2669 : /************************************************************************/
2670 : /* SetDeferredSpatialIndexCreation() */
2671 : /************************************************************************/
2672 :
2673 607 : void OGRGeoPackageTableLayer::SetDeferredSpatialIndexCreation(bool bFlag)
2674 : {
2675 607 : m_bDeferredSpatialIndexCreation = bFlag;
2676 607 : if (bFlag)
2677 : {
2678 : // This method is invoked before the layer is added to the dataset,
2679 : // so GetLayerCount() will return 0 for the first layer added.
2680 607 : m_bAllowedRTreeThread =
2681 1137 : m_poDS->GetLayerCount() == 0 && sqlite3_threadsafe() != 0 &&
2682 1667 : CPLGetNumCPUs() >= 2 &&
2683 530 : CPLTestBool(
2684 : CPLGetConfigOption("OGR_GPKG_ALLOW_THREADED_RTREE", "YES"));
2685 :
2686 : // For unit tests
2687 607 : if (CPLTestBool(CPLGetConfigOption(
2688 : "OGR_GPKG_THREADED_RTREE_AT_FIRST_FEATURE", "NO")))
2689 : {
2690 60 : m_nRTreeBatchSize = 10;
2691 60 : m_nRTreeBatchesBeforeStart = 1;
2692 : }
2693 : }
2694 607 : }
2695 :
2696 : /************************************************************************/
2697 : /* StartAsyncRTree() */
2698 : /************************************************************************/
2699 :
2700 : // We create a temporary database with only the RTree, and we insert
2701 : // records into it in a dedicated thread, in parallel of the main thread
2702 : // that inserts rows in the user table. When the layer is finalized, we
2703 : // just use bulk copy statements of the form
2704 : // INSERT INTO rtree_xxxx_rowid/node/parent SELECT * FROM
2705 : // temp_rtree.my_rtree_rowid/node/parent to copy the RTree auxiliary tables into
2706 : // the main database, which is a very fast operation.
2707 :
2708 48 : void OGRGeoPackageTableLayer::StartAsyncRTree()
2709 : {
2710 48 : m_osAsyncDBName = m_poDS->GetDescription();
2711 48 : m_osAsyncDBName += ".tmp_rtree_";
2712 48 : bool bCanUseTableName = false;
2713 48 : if (strlen(m_pszTableName) <= 32)
2714 : {
2715 36 : bCanUseTableName = true;
2716 36 : constexpr char DIGIT_ZERO = '0';
2717 144 : for (int i = 0; m_pszTableName[i] != '\0'; ++i)
2718 : {
2719 120 : const char ch = m_pszTableName[i];
2720 132 : if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
2721 12 : (ch >= DIGIT_ZERO && ch <= '9') || ch == '.' || ch == '_'))
2722 : {
2723 12 : bCanUseTableName = false;
2724 12 : break;
2725 : }
2726 : }
2727 : }
2728 48 : if (bCanUseTableName)
2729 24 : m_osAsyncDBName += m_pszTableName;
2730 : else
2731 : {
2732 24 : m_osAsyncDBName += CPLMD5String(m_pszTableName);
2733 : }
2734 48 : m_osAsyncDBName += ".db";
2735 :
2736 48 : m_osAsyncDBAttachName = "temp_rtree_";
2737 48 : m_osAsyncDBAttachName += CPLMD5String(m_pszTableName);
2738 :
2739 48 : VSIUnlink(m_osAsyncDBName.c_str());
2740 48 : CPLDebug("GPKG", "Creating background RTree DB %s",
2741 : m_osAsyncDBName.c_str());
2742 72 : if (sqlite3_open_v2(m_osAsyncDBName.c_str(), &m_hAsyncDBHandle,
2743 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
2744 120 : m_poDS->GetVFS() ? m_poDS->GetVFS()->zName : nullptr) !=
2745 : SQLITE_OK)
2746 : {
2747 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_open_v2() of %s failed",
2748 : m_osAsyncDBName.c_str());
2749 0 : sqlite3_close(m_hAsyncDBHandle);
2750 0 : m_hAsyncDBHandle = nullptr;
2751 : }
2752 48 : if (m_hAsyncDBHandle != nullptr)
2753 : {
2754 : /* Make sure our auxiliary DB has the same page size as the main one.
2755 : * Because the number of RTree cells depends on the SQLite page size.
2756 : * However the sqlite implementation limits to 51 cells maximum per page,
2757 : * which is reached starting with a page size of 2048 bytes.
2758 : * As the default SQLite page size is 4096 currently, having potentially
2759 : * different page sizes >= 4096 between the main and auxiliary DBs would
2760 : * not be a practical issue, but better be consistent.
2761 : */
2762 : const int nPageSize =
2763 48 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA page_size", nullptr);
2764 :
2765 48 : if (SQLCommand(m_hAsyncDBHandle,
2766 : CPLSPrintf("PRAGMA page_size = %d;\n"
2767 : "PRAGMA journal_mode = OFF;\n"
2768 : "PRAGMA synchronous = OFF;",
2769 48 : nPageSize)) == OGRERR_NONE)
2770 : {
2771 48 : char *pszSQL = sqlite3_mprintf("ATTACH DATABASE '%q' AS '%q'",
2772 : m_osAsyncDBName.c_str(),
2773 : m_osAsyncDBAttachName.c_str());
2774 48 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
2775 48 : sqlite3_free(pszSQL);
2776 :
2777 48 : if (eErr == OGRERR_NONE)
2778 : {
2779 48 : m_hRTree = gdal_sqlite_rtree_bl_new(nPageSize);
2780 : try
2781 : {
2782 : m_oThreadRTree =
2783 96 : std::thread([this]() { AsyncRTreeThreadFunction(); });
2784 48 : m_bThreadRTreeStarted = true;
2785 : }
2786 0 : catch (const std::exception &e)
2787 : {
2788 0 : CPLError(CE_Failure, CPLE_AppDefined,
2789 0 : "RTree thread cannot be created: %s", e.what());
2790 : }
2791 : }
2792 : }
2793 :
2794 48 : if (!m_bThreadRTreeStarted)
2795 : {
2796 0 : if (m_hRTree)
2797 : {
2798 0 : gdal_sqlite_rtree_bl_free(m_hRTree);
2799 0 : m_hRTree = nullptr;
2800 : }
2801 0 : m_oQueueRTreeEntries.clear();
2802 0 : m_bErrorDuringRTreeThread = true;
2803 0 : sqlite3_close(m_hAsyncDBHandle);
2804 0 : m_hAsyncDBHandle = nullptr;
2805 0 : VSIUnlink(m_osAsyncDBName.c_str());
2806 : }
2807 : }
2808 : else
2809 : {
2810 0 : m_oQueueRTreeEntries.clear();
2811 0 : m_bErrorDuringRTreeThread = true;
2812 : }
2813 48 : }
2814 :
2815 : /************************************************************************/
2816 : /* RemoveAsyncRTreeTempDB() */
2817 : /************************************************************************/
2818 :
2819 48 : void OGRGeoPackageTableLayer::RemoveAsyncRTreeTempDB()
2820 : {
2821 48 : if (!m_osAsyncDBAttachName.empty())
2822 : {
2823 96 : SQLCommand(
2824 48 : m_poDS->GetDB(),
2825 : CPLSPrintf("DETACH DATABASE \"%s\"",
2826 96 : SQLEscapeName(m_osAsyncDBAttachName.c_str()).c_str()));
2827 48 : m_osAsyncDBAttachName.clear();
2828 48 : VSIUnlink(m_osAsyncDBName.c_str());
2829 48 : m_osAsyncDBName.clear();
2830 : }
2831 48 : }
2832 :
2833 : /************************************************************************/
2834 : /* CancelAsyncRTree() */
2835 : /************************************************************************/
2836 :
2837 36 : void OGRGeoPackageTableLayer::CancelAsyncRTree()
2838 : {
2839 36 : CPLDebug("GPKG", "Cancel background RTree creation");
2840 36 : m_oQueueRTreeEntries.push({});
2841 36 : m_oThreadRTree.join();
2842 36 : m_bThreadRTreeStarted = false;
2843 36 : if (m_hAsyncDBHandle)
2844 : {
2845 36 : sqlite3_close(m_hAsyncDBHandle);
2846 36 : m_hAsyncDBHandle = nullptr;
2847 : }
2848 36 : gdal_sqlite_rtree_bl_free(m_hRTree);
2849 36 : m_hRTree = nullptr;
2850 36 : m_bErrorDuringRTreeThread = true;
2851 36 : RemoveAsyncRTreeTempDB();
2852 36 : }
2853 :
2854 : /************************************************************************/
2855 : /* FinishOrDisableThreadedRTree() */
2856 : /************************************************************************/
2857 :
2858 73 : void OGRGeoPackageTableLayer::FinishOrDisableThreadedRTree()
2859 : {
2860 73 : if (m_bThreadRTreeStarted)
2861 : {
2862 12 : CreateSpatialIndexIfNecessary();
2863 : }
2864 73 : m_bAllowedRTreeThread = false;
2865 73 : }
2866 :
2867 : /************************************************************************/
2868 : /* FlushInMemoryRTree() */
2869 : /************************************************************************/
2870 :
2871 12 : bool OGRGeoPackageTableLayer::FlushInMemoryRTree(sqlite3 *hRTreeDB,
2872 : const char *pszRTreeName)
2873 : {
2874 12 : if (hRTreeDB == m_hAsyncDBHandle)
2875 8 : SQLCommand(hRTreeDB, "BEGIN");
2876 :
2877 12 : char *pszErrMsg = nullptr;
2878 12 : bool bRet = gdal_sqlite_rtree_bl_serialize(m_hRTree, hRTreeDB, pszRTreeName,
2879 : "id", "minx", "miny", "maxx",
2880 : "maxy", &pszErrMsg);
2881 12 : if (hRTreeDB == m_hAsyncDBHandle)
2882 : {
2883 8 : if (bRet)
2884 8 : bRet = SQLCommand(hRTreeDB, "COMMIT") == OGRERR_NONE;
2885 : else
2886 0 : SQLCommand(hRTreeDB, "ROLLBACK");
2887 : }
2888 :
2889 12 : gdal_sqlite_rtree_bl_free(m_hRTree);
2890 12 : m_hRTree = nullptr;
2891 :
2892 12 : if (!bRet)
2893 : {
2894 0 : CPLError(CE_Failure, CPLE_AppDefined,
2895 : "sqlite_rtree_bl_serialize() failed with %s",
2896 0 : pszErrMsg ? pszErrMsg : "(null)");
2897 :
2898 0 : m_bErrorDuringRTreeThread = true;
2899 :
2900 0 : if (m_hAsyncDBHandle)
2901 : {
2902 0 : sqlite3_close(m_hAsyncDBHandle);
2903 0 : m_hAsyncDBHandle = nullptr;
2904 : }
2905 :
2906 0 : m_oQueueRTreeEntries.clear();
2907 : }
2908 12 : sqlite3_free(pszErrMsg);
2909 :
2910 12 : return bRet;
2911 : }
2912 :
2913 : /************************************************************************/
2914 : /* GetMaxRAMUsageAllowedForRTree() */
2915 : /************************************************************************/
2916 :
2917 654 : static size_t GetMaxRAMUsageAllowedForRTree()
2918 : {
2919 654 : const uint64_t nUsableRAM = CPLGetUsablePhysicalRAM();
2920 654 : uint64_t nMaxRAMUsageAllowed =
2921 654 : (nUsableRAM ? nUsableRAM / 10 : 100 * 1024 * 1024);
2922 : const char *pszMaxRAMUsageAllowed =
2923 654 : CPLGetConfigOption("OGR_GPKG_MAX_RAM_USAGE_RTREE", nullptr);
2924 654 : if (pszMaxRAMUsageAllowed)
2925 : {
2926 : nMaxRAMUsageAllowed = static_cast<uint64_t>(
2927 20 : std::strtoull(pszMaxRAMUsageAllowed, nullptr, 10));
2928 : }
2929 654 : if (nMaxRAMUsageAllowed > std::numeric_limits<size_t>::max() - 1U)
2930 : {
2931 0 : nMaxRAMUsageAllowed = std::numeric_limits<size_t>::max() - 1U;
2932 : }
2933 654 : return static_cast<size_t>(nMaxRAMUsageAllowed);
2934 : }
2935 :
2936 : /************************************************************************/
2937 : /* AsyncRTreeThreadFunction() */
2938 : /************************************************************************/
2939 :
2940 48 : void OGRGeoPackageTableLayer::AsyncRTreeThreadFunction()
2941 : {
2942 48 : CPLAssert(m_hRTree);
2943 :
2944 48 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
2945 48 : sqlite3_stmt *hStmt = nullptr;
2946 48 : GIntBig nCount = 0;
2947 : while (true)
2948 : {
2949 884 : const auto aoEntries = m_oQueueRTreeEntries.get_and_pop_front();
2950 884 : if (aoEntries.empty())
2951 44 : break;
2952 :
2953 840 : constexpr int NOTIFICATION_INTERVAL = 500 * 1000;
2954 :
2955 840 : auto oIter = aoEntries.begin();
2956 840 : if (m_hRTree)
2957 : {
2958 4808 : for (; oIter != aoEntries.end(); ++oIter)
2959 : {
2960 4372 : const auto &entry = *oIter;
2961 4372 : if (gdal_sqlite_rtree_bl_ram_usage(m_hRTree) >
2962 8736 : nMaxRAMUsageAllowed ||
2963 4364 : !gdal_sqlite_rtree_bl_insert(m_hRTree, entry.nId,
2964 4364 : entry.fMinX, entry.fMinY,
2965 4364 : entry.fMaxX, entry.fMaxY))
2966 : {
2967 8 : CPLDebug("GPKG", "Too large in-memory RTree. "
2968 : "Flushing it and using memory friendly "
2969 : "algorithm for the rest");
2970 8 : if (!FlushInMemoryRTree(m_hAsyncDBHandle, "my_rtree"))
2971 0 : return;
2972 8 : break;
2973 : }
2974 4364 : ++nCount;
2975 4364 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
2976 : {
2977 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree",
2978 : nCount);
2979 : }
2980 : }
2981 444 : if (oIter == aoEntries.end())
2982 436 : continue;
2983 : }
2984 :
2985 404 : if (hStmt == nullptr)
2986 : {
2987 : const char *pszInsertSQL =
2988 8 : CPLGetConfigOption(
2989 : "OGR_GPKG_SIMULATE_INSERT_INTO_MY_RTREE_PREPARATION_ERROR",
2990 : nullptr)
2991 8 : ? "INSERT INTO my_rtree_SIMULATE_ERROR VALUES (?,?,?,?,?)"
2992 8 : : "INSERT INTO my_rtree VALUES (?,?,?,?,?)";
2993 8 : if (sqlite3_prepare_v2(m_hAsyncDBHandle, pszInsertSQL, -1, &hStmt,
2994 8 : nullptr) != SQLITE_OK)
2995 : {
2996 4 : CPLError(CE_Failure, CPLE_AppDefined,
2997 : "failed to prepare SQL: %s: %s", pszInsertSQL,
2998 : sqlite3_errmsg(m_hAsyncDBHandle));
2999 :
3000 4 : m_bErrorDuringRTreeThread = true;
3001 :
3002 4 : sqlite3_close(m_hAsyncDBHandle);
3003 4 : m_hAsyncDBHandle = nullptr;
3004 :
3005 4 : m_oQueueRTreeEntries.clear();
3006 4 : return;
3007 : }
3008 :
3009 4 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3010 : }
3011 :
3012 : #ifdef DEBUG_VERBOSE
3013 : CPLDebug("GPKG",
3014 : "AsyncRTreeThreadFunction(): "
3015 : "Processing batch of %d features, "
3016 : "starting at FID " CPL_FRMT_GIB " and ending "
3017 : "at FID " CPL_FRMT_GIB,
3018 : static_cast<int>(aoEntries.size()), aoEntries.front().nId,
3019 : aoEntries.back().nId);
3020 : #endif
3021 4398 : for (; oIter != aoEntries.end(); ++oIter)
3022 : {
3023 3998 : const auto &entry = *oIter;
3024 3998 : sqlite3_reset(hStmt);
3025 :
3026 3998 : sqlite3_bind_int64(hStmt, 1, entry.nId);
3027 3998 : sqlite3_bind_double(hStmt, 2, entry.fMinX);
3028 3998 : sqlite3_bind_double(hStmt, 3, entry.fMaxX);
3029 3998 : sqlite3_bind_double(hStmt, 4, entry.fMinY);
3030 3998 : sqlite3_bind_double(hStmt, 5, entry.fMaxY);
3031 3998 : int sqlite_err = sqlite3_step(hStmt);
3032 3998 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
3033 : {
3034 0 : CPLError(CE_Failure, CPLE_AppDefined,
3035 : "failed to execute insertion in RTree : %s",
3036 : sqlite3_errmsg(m_hAsyncDBHandle));
3037 0 : m_bErrorDuringRTreeThread = true;
3038 0 : break;
3039 : }
3040 3998 : ++nCount;
3041 3998 : if ((nCount % NOTIFICATION_INTERVAL) == 0)
3042 : {
3043 0 : CPLDebug("GPKG", CPL_FRMT_GIB " rows indexed in rtree", nCount);
3044 0 : if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3045 : {
3046 0 : m_bErrorDuringRTreeThread = true;
3047 0 : break;
3048 : }
3049 0 : SQLCommand(m_hAsyncDBHandle, "BEGIN");
3050 : }
3051 : }
3052 836 : }
3053 44 : if (!m_hRTree)
3054 : {
3055 4 : if (m_bErrorDuringRTreeThread)
3056 : {
3057 0 : SQLCommand(m_hAsyncDBHandle, "ROLLBACK");
3058 : }
3059 4 : else if (SQLCommand(m_hAsyncDBHandle, "COMMIT") != OGRERR_NONE)
3060 : {
3061 0 : m_bErrorDuringRTreeThread = true;
3062 : }
3063 :
3064 4 : sqlite3_finalize(hStmt);
3065 :
3066 4 : if (m_bErrorDuringRTreeThread)
3067 : {
3068 0 : sqlite3_close(m_hAsyncDBHandle);
3069 0 : m_hAsyncDBHandle = nullptr;
3070 :
3071 0 : VSIUnlink(m_osAsyncDBName.c_str());
3072 :
3073 0 : m_oQueueRTreeEntries.clear();
3074 : }
3075 : }
3076 44 : CPLDebug("GPKG",
3077 : "AsyncRTreeThreadFunction(): " CPL_FRMT_GIB
3078 : " rows inserted into RTree",
3079 : nCount);
3080 : }
3081 :
3082 : /************************************************************************/
3083 : /* ISetFeature() */
3084 : /************************************************************************/
3085 :
3086 58 : OGRErr OGRGeoPackageTableLayer::ISetFeature(OGRFeature *poFeature)
3087 : {
3088 58 : if (!m_bFeatureDefnCompleted)
3089 0 : GetLayerDefn();
3090 58 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3091 : {
3092 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3093 : "SetFeature");
3094 0 : return OGRERR_FAILURE;
3095 : }
3096 :
3097 : /* No FID? */
3098 58 : if (poFeature->GetFID() == OGRNullFID)
3099 : {
3100 0 : CPLError(CE_Failure, CPLE_AppDefined,
3101 : "FID required on features given to SetFeature().");
3102 0 : return OGRERR_FAILURE;
3103 : }
3104 :
3105 : /* In case the FID column has also been created as a regular field */
3106 67 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3107 9 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3108 : {
3109 1 : return OGRERR_FAILURE;
3110 : }
3111 :
3112 57 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3113 0 : return OGRERR_FAILURE;
3114 :
3115 57 : CancelAsyncNextArrowArray();
3116 :
3117 57 : if (m_bThreadRTreeStarted)
3118 12 : CancelAsyncRTree();
3119 57 : if (!RunDeferredSpatialIndexUpdate())
3120 0 : return OGRERR_FAILURE;
3121 :
3122 : const sqlite3_int64 nTotalChangesBefore =
3123 : #if SQLITE_VERSION_NUMBER >= 3037000L
3124 57 : sqlite3_total_changes64(m_poDS->GetDB());
3125 : #else
3126 : sqlite3_total_changes(m_poDS->GetDB());
3127 : #endif
3128 :
3129 57 : CheckGeometryType(poFeature);
3130 :
3131 57 : if (!m_osUpdateStatementSQL.empty())
3132 : {
3133 1 : m_osUpdateStatementSQL.clear();
3134 1 : if (m_poUpdateStatement)
3135 1 : sqlite3_finalize(m_poUpdateStatement);
3136 1 : m_poUpdateStatement = nullptr;
3137 : }
3138 57 : if (!m_poUpdateStatement)
3139 : {
3140 : /* Construct a SQL UPDATE statement from the OGRFeature */
3141 : /* Only work with fields that are set */
3142 : /* Do not stick values into SQL, use placeholder and bind values later
3143 : */
3144 45 : const std::string osCommand = FeatureGenerateUpdateSQL(poFeature);
3145 45 : if (osCommand.empty())
3146 1 : return OGRERR_NONE;
3147 :
3148 : /* Prepare the SQL into a statement */
3149 44 : int err = sqlite3_prepare_v2(m_poDS->GetDB(), osCommand.c_str(),
3150 44 : static_cast<int>(osCommand.size()),
3151 : &m_poUpdateStatement, nullptr);
3152 44 : if (err != SQLITE_OK)
3153 : {
3154 1 : CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s",
3155 : osCommand.c_str());
3156 1 : return OGRERR_FAILURE;
3157 : }
3158 : }
3159 :
3160 : /* Bind values onto the statement now */
3161 55 : OGRErr errOgr = FeatureBindUpdateParameters(poFeature, m_poUpdateStatement);
3162 55 : if (errOgr != OGRERR_NONE)
3163 : {
3164 0 : sqlite3_reset(m_poUpdateStatement);
3165 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3166 0 : return errOgr;
3167 : }
3168 :
3169 : /* From here execute the statement and check errors */
3170 55 : int err = sqlite3_step(m_poUpdateStatement);
3171 55 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3172 : {
3173 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3174 0 : sqlite3_errmsg(m_poDS->GetDB()));
3175 0 : sqlite3_reset(m_poUpdateStatement);
3176 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3177 0 : return OGRERR_FAILURE;
3178 : }
3179 :
3180 55 : sqlite3_reset(m_poUpdateStatement);
3181 55 : sqlite3_clear_bindings(m_poUpdateStatement);
3182 :
3183 : const sqlite3_int64 nTotalChangesAfter =
3184 : #if SQLITE_VERSION_NUMBER >= 3037000L
3185 55 : sqlite3_total_changes64(m_poDS->GetDB());
3186 : #else
3187 : sqlite3_total_changes(m_poDS->GetDB());
3188 : #endif
3189 :
3190 : /* Only update the envelope if we changed something */
3191 55 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3192 55 : ? OGRERR_NONE
3193 : : OGRERR_NON_EXISTING_FEATURE;
3194 55 : if (eErr == OGRERR_NONE)
3195 : {
3196 : /* Update the layer extents with this new object */
3197 49 : if (IsGeomFieldSet(poFeature))
3198 : {
3199 36 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3200 36 : if (!poGeom->IsEmpty())
3201 : {
3202 36 : OGREnvelope oEnv;
3203 36 : poGeom->getEnvelope(&oEnv);
3204 36 : UpdateExtent(&oEnv);
3205 : }
3206 : }
3207 :
3208 49 : m_bContentChanged = true;
3209 : }
3210 :
3211 : /* All done! */
3212 55 : return eErr;
3213 : }
3214 :
3215 : /************************************************************************/
3216 : /* IUpsertFeature() */
3217 : /************************************************************************/
3218 :
3219 25 : OGRErr OGRGeoPackageTableLayer::IUpsertFeature(OGRFeature *poFeature)
3220 :
3221 : {
3222 25 : return CreateOrUpsertFeature(poFeature, /* bUpsert = */ true);
3223 : }
3224 :
3225 : //----------------------------------------------------------------------
3226 : // FeatureGenerateUpdateSQL()
3227 : //
3228 : // Build a SQL UPDATE statement that references all the columns in
3229 : // the OGRFeatureDefn that the user asked to be updated, then prepare it for
3230 : // repeated use in a prepared statement. All statements start off with geometry
3231 : // (if it exists, and if it is asked to be updated), then reference each column
3232 : // in the order it appears in the OGRFeatureDefn.
3233 : // FeatureBindParameters operates on the expectation of this
3234 : // column ordering.
3235 :
3236 : //
3237 11 : std::string OGRGeoPackageTableLayer::FeatureGenerateUpdateSQL(
3238 : const OGRFeature *poFeature, int nUpdatedFieldsCount,
3239 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3240 : const int * /*panUpdatedGeomFieldsIdx*/) const
3241 : {
3242 11 : bool bNeedComma = false;
3243 11 : const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
3244 :
3245 : /* Set up our SQL string basics */
3246 22 : std::string osUpdate("UPDATE \"");
3247 11 : osUpdate += SQLEscapeName(m_pszTableName);
3248 11 : osUpdate += "\" SET ";
3249 :
3250 11 : if (nUpdatedGeomFieldsCount == 1 && poFeatureDefn->GetGeomFieldCount() > 0)
3251 : {
3252 2 : osUpdate += '"';
3253 : osUpdate +=
3254 2 : SQLEscapeName(poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
3255 2 : osUpdate += "\"=?";
3256 2 : bNeedComma = true;
3257 : }
3258 :
3259 : /* Add attribute column names (except FID) to the SQL */
3260 20 : for (int i = 0; i < nUpdatedFieldsCount; i++)
3261 : {
3262 9 : const int iField = panUpdatedFieldsIdx[i];
3263 18 : if (iField == m_iFIDAsRegularColumnIndex ||
3264 9 : m_abGeneratedColumns[iField])
3265 0 : continue;
3266 9 : if (!poFeature->IsFieldSet(iField))
3267 1 : continue;
3268 8 : if (!bNeedComma)
3269 8 : bNeedComma = true;
3270 : else
3271 0 : osUpdate += ", ";
3272 :
3273 8 : osUpdate += '"';
3274 : osUpdate +=
3275 8 : SQLEscapeName(poFeatureDefn->GetFieldDefn(iField)->GetNameRef());
3276 8 : osUpdate += "\"=?";
3277 : }
3278 11 : if (!bNeedComma)
3279 1 : return CPLString();
3280 :
3281 10 : osUpdate += " WHERE \"";
3282 10 : osUpdate += SQLEscapeName(m_pszFidColumn);
3283 10 : osUpdate += "\" = ?";
3284 :
3285 10 : return osUpdate;
3286 : }
3287 :
3288 : /************************************************************************/
3289 : /* UpdateFeature() */
3290 : /************************************************************************/
3291 :
3292 11 : OGRErr OGRGeoPackageTableLayer::IUpdateFeature(
3293 : OGRFeature *poFeature, int nUpdatedFieldsCount,
3294 : const int *panUpdatedFieldsIdx, int nUpdatedGeomFieldsCount,
3295 : const int *panUpdatedGeomFieldsIdx, bool /* bUpdateStyleString*/)
3296 :
3297 : {
3298 11 : if (!m_bFeatureDefnCompleted)
3299 0 : GetLayerDefn();
3300 11 : if (!m_poDS->GetUpdate() || m_pszFidColumn == nullptr)
3301 : {
3302 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3303 : "UpdateFeature");
3304 0 : return OGRERR_FAILURE;
3305 : }
3306 :
3307 : /* No FID? */
3308 11 : if (poFeature->GetFID() == OGRNullFID)
3309 : {
3310 0 : CPLError(CE_Failure, CPLE_AppDefined,
3311 : "FID required on features given to SetFeature().");
3312 0 : return OGRERR_FAILURE;
3313 : }
3314 :
3315 : /* In case the FID column has also been created as a regular field */
3316 11 : if (m_iFIDAsRegularColumnIndex >= 0 &&
3317 0 : !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
3318 : {
3319 0 : return OGRERR_FAILURE;
3320 : }
3321 :
3322 11 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3323 0 : return OGRERR_FAILURE;
3324 :
3325 11 : CancelAsyncNextArrowArray();
3326 :
3327 11 : if (m_bThreadRTreeStarted)
3328 0 : CancelAsyncRTree();
3329 11 : if (!RunDeferredSpatialIndexUpdate())
3330 0 : return OGRERR_FAILURE;
3331 :
3332 11 : CheckGeometryType(poFeature);
3333 :
3334 : /* Construct a SQL UPDATE statement from the OGRFeature */
3335 : /* Only work with fields that are set */
3336 : /* Do not stick values into SQL, use placeholder and bind values later
3337 : */
3338 : const std::string osUpdateStatementSQL = FeatureGenerateUpdateSQL(
3339 : poFeature, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3340 22 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3341 11 : if (osUpdateStatementSQL.empty())
3342 1 : return OGRERR_NONE;
3343 :
3344 10 : if (m_osUpdateStatementSQL != osUpdateStatementSQL)
3345 : {
3346 8 : if (m_poUpdateStatement)
3347 5 : sqlite3_finalize(m_poUpdateStatement);
3348 8 : m_poUpdateStatement = nullptr;
3349 : /* Prepare the SQL into a statement */
3350 : int err =
3351 8 : sqlite3_prepare_v2(m_poDS->GetDB(), osUpdateStatementSQL.c_str(),
3352 8 : static_cast<int>(osUpdateStatementSQL.size()),
3353 : &m_poUpdateStatement, nullptr);
3354 8 : if (err != SQLITE_OK)
3355 : {
3356 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s",
3357 : osUpdateStatementSQL.c_str());
3358 0 : return OGRERR_FAILURE;
3359 : }
3360 8 : m_osUpdateStatementSQL = osUpdateStatementSQL;
3361 : }
3362 :
3363 : /* Bind values onto the statement now */
3364 10 : int nColCount = 0;
3365 : const OGRErr errOgr =
3366 10 : FeatureBindParameters(poFeature, m_poUpdateStatement, &nColCount, false,
3367 : false, nUpdatedFieldsCount, panUpdatedFieldsIdx,
3368 : nUpdatedGeomFieldsCount, panUpdatedGeomFieldsIdx);
3369 10 : if (errOgr != OGRERR_NONE)
3370 : {
3371 0 : sqlite3_reset(m_poUpdateStatement);
3372 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3373 0 : return errOgr;
3374 : }
3375 :
3376 : // Bind the FID to the "WHERE" clause.
3377 : const int sqlite_err =
3378 10 : sqlite3_bind_int64(m_poUpdateStatement, nColCount, poFeature->GetFID());
3379 10 : if (sqlite_err != SQLITE_OK)
3380 : {
3381 0 : CPLError(CE_Failure, CPLE_AppDefined,
3382 : "failed to bind FID '" CPL_FRMT_GIB "' to statement",
3383 : poFeature->GetFID());
3384 0 : sqlite3_reset(m_poUpdateStatement);
3385 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3386 0 : return OGRERR_FAILURE;
3387 : }
3388 :
3389 : const sqlite3_int64 nTotalChangesBefore =
3390 : #if SQLITE_VERSION_NUMBER >= 3037000L
3391 10 : sqlite3_total_changes64(m_poDS->GetDB());
3392 : #else
3393 : sqlite3_total_changes(m_poDS->GetDB());
3394 : #endif
3395 :
3396 : /* From here execute the statement and check errors */
3397 10 : int err = sqlite3_step(m_poUpdateStatement);
3398 10 : if (!(err == SQLITE_OK || err == SQLITE_DONE))
3399 : {
3400 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to execute update : %s",
3401 0 : sqlite3_errmsg(m_poDS->GetDB()));
3402 0 : sqlite3_reset(m_poUpdateStatement);
3403 0 : sqlite3_clear_bindings(m_poUpdateStatement);
3404 0 : return OGRERR_FAILURE;
3405 : }
3406 :
3407 10 : sqlite3_reset(m_poUpdateStatement);
3408 10 : sqlite3_clear_bindings(m_poUpdateStatement);
3409 :
3410 : const sqlite3_int64 nTotalChangesAfter =
3411 : #if SQLITE_VERSION_NUMBER >= 3037000L
3412 10 : sqlite3_total_changes64(m_poDS->GetDB());
3413 : #else
3414 : sqlite3_total_changes(m_poDS->GetDB());
3415 : #endif
3416 :
3417 : /* Only update the envelope if we changed something */
3418 10 : OGRErr eErr = nTotalChangesAfter != nTotalChangesBefore
3419 10 : ? OGRERR_NONE
3420 : : OGRERR_NON_EXISTING_FEATURE;
3421 10 : if (eErr == OGRERR_NONE)
3422 : {
3423 : /* Update the layer extents with this new object */
3424 9 : if (nUpdatedGeomFieldsCount == 1 && IsGeomFieldSet(poFeature))
3425 : {
3426 1 : OGRGeometry *poGeom = poFeature->GetGeomFieldRef(0);
3427 1 : if (!poGeom->IsEmpty())
3428 : {
3429 1 : OGREnvelope oEnv;
3430 1 : poGeom->getEnvelope(&oEnv);
3431 1 : UpdateExtent(&oEnv);
3432 : }
3433 : }
3434 :
3435 9 : m_bContentChanged = true;
3436 : }
3437 :
3438 : /* All done! */
3439 10 : return eErr;
3440 : }
3441 :
3442 : /************************************************************************/
3443 : /* SetAttributeFilter() */
3444 : /************************************************************************/
3445 :
3446 83 : OGRErr OGRGeoPackageTableLayer::SetAttributeFilter(const char *pszQuery)
3447 :
3448 : {
3449 83 : if (!m_bFeatureDefnCompleted)
3450 8 : GetLayerDefn();
3451 83 : CPLFree(m_pszAttrQueryString);
3452 83 : m_pszAttrQueryString = (pszQuery) ? CPLStrdup(pszQuery) : nullptr;
3453 :
3454 83 : if (pszQuery == nullptr)
3455 38 : osQuery = "";
3456 : else
3457 45 : osQuery = pszQuery;
3458 :
3459 83 : BuildWhere();
3460 :
3461 83 : ResetReading();
3462 :
3463 83 : return OGRERR_NONE;
3464 : }
3465 :
3466 : /************************************************************************/
3467 : /* ResetReading() */
3468 : /************************************************************************/
3469 :
3470 24598 : void OGRGeoPackageTableLayer::ResetReading()
3471 : {
3472 24598 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3473 0 : return;
3474 :
3475 24598 : OGRGeoPackageLayer::ResetReading();
3476 :
3477 24598 : if (m_poInsertStatement)
3478 : {
3479 110 : sqlite3_finalize(m_poInsertStatement);
3480 110 : m_poInsertStatement = nullptr;
3481 : }
3482 :
3483 24598 : if (m_poUpdateStatement)
3484 : {
3485 23 : sqlite3_finalize(m_poUpdateStatement);
3486 23 : m_poUpdateStatement = nullptr;
3487 : }
3488 24598 : m_osUpdateStatementSQL.clear();
3489 :
3490 24598 : if (m_poGetFeatureStatement)
3491 : {
3492 22 : sqlite3_finalize(m_poGetFeatureStatement);
3493 22 : m_poGetFeatureStatement = nullptr;
3494 : }
3495 :
3496 24598 : CancelAsyncNextArrowArray();
3497 :
3498 24598 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3499 :
3500 24598 : BuildColumns();
3501 : }
3502 :
3503 : /************************************************************************/
3504 : /* SetNextByIndex() */
3505 : /************************************************************************/
3506 :
3507 15 : OGRErr OGRGeoPackageTableLayer::SetNextByIndex(GIntBig nIndex)
3508 : {
3509 15 : if (nIndex < 0)
3510 3 : return OGRERR_FAILURE;
3511 12 : if (m_soColumns.empty())
3512 0 : BuildColumns();
3513 12 : return ResetStatementInternal(nIndex);
3514 : }
3515 :
3516 : /************************************************************************/
3517 : /* ResetStatement() */
3518 : /************************************************************************/
3519 :
3520 610 : OGRErr OGRGeoPackageTableLayer::ResetStatement()
3521 :
3522 : {
3523 610 : return ResetStatementInternal(0);
3524 : }
3525 :
3526 : /************************************************************************/
3527 : /* ResetStatementInternal() */
3528 : /************************************************************************/
3529 :
3530 622 : OGRErr OGRGeoPackageTableLayer::ResetStatementInternal(GIntBig nStartIndex)
3531 :
3532 : {
3533 622 : ClearStatement();
3534 :
3535 : /* There is no active query statement set up, */
3536 : /* so job #1 is to prepare the statement. */
3537 : /* Append the attribute filter, if there is one */
3538 1244 : CPLString soSQL;
3539 622 : if (!m_soFilter.empty())
3540 : {
3541 : soSQL.Printf("SELECT %s FROM \"%s\" m WHERE %s", m_soColumns.c_str(),
3542 198 : SQLEscapeName(m_pszTableName).c_str(), m_soFilter.c_str());
3543 :
3544 382 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
3545 184 : HasSpatialIndex())
3546 : {
3547 183 : OGREnvelope sEnvelope;
3548 :
3549 183 : m_poFilterGeom->getEnvelope(&sEnvelope);
3550 :
3551 183 : bool bUseSpatialIndex = true;
3552 183 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
3553 121 : sEnvelope.MinY <= m_poExtent->MinY &&
3554 105 : sEnvelope.MaxX >= m_poExtent->MaxX &&
3555 97 : sEnvelope.MaxY >= m_poExtent->MaxY)
3556 : {
3557 : // Selecting from spatial filter on whole extent can be rather
3558 : // slow. So use function based filtering, just in case the
3559 : // advertized global extent might be wrong. Otherwise we might
3560 : // just discard completely the spatial filter.
3561 94 : bUseSpatialIndex = false;
3562 : }
3563 :
3564 89 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
3565 361 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
3566 89 : !std::isinf(sEnvelope.MaxY))
3567 : {
3568 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3569 : "JOIN \"%s\" r "
3570 : "ON m.\"%s\" = r.id WHERE "
3571 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
3572 : "r.maxy >= %.12f AND r.miny <= %.12f",
3573 : m_soColumns.c_str(),
3574 178 : SQLEscapeName(m_pszTableName).c_str(),
3575 178 : SQLEscapeName(m_osRTreeName).c_str(),
3576 178 : SQLEscapeName(m_osFIDForRTree).c_str(),
3577 89 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
3578 356 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
3579 : }
3580 : }
3581 : }
3582 : else
3583 : soSQL.Printf("SELECT %s FROM \"%s\" m", m_soColumns.c_str(),
3584 424 : SQLEscapeName(m_pszTableName).c_str());
3585 622 : if (nStartIndex > 0)
3586 : {
3587 11 : soSQL += CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB, nStartIndex);
3588 : }
3589 :
3590 622 : CPLDebug("GPKG", "ResetStatement(%s)", soSQL.c_str());
3591 :
3592 622 : int err = sqlite3_prepare_v2(m_poDS->GetDB(), soSQL.c_str(), -1,
3593 : &m_poQueryStatement, nullptr);
3594 622 : if (err != SQLITE_OK)
3595 : {
3596 1 : CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s",
3597 : soSQL.c_str());
3598 1 : return OGRERR_FAILURE;
3599 : }
3600 :
3601 621 : m_iNextShapeId = nStartIndex;
3602 621 : m_bGetNextArrowArrayCalledSinceResetReading = false;
3603 :
3604 621 : return OGRERR_NONE;
3605 : }
3606 :
3607 : /************************************************************************/
3608 : /* GetNextFeature() */
3609 : /************************************************************************/
3610 :
3611 10825 : OGRFeature *OGRGeoPackageTableLayer::GetNextFeature()
3612 : {
3613 10825 : if (!m_bFeatureDefnCompleted)
3614 27 : GetLayerDefn();
3615 10825 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3616 0 : return nullptr;
3617 :
3618 10825 : CancelAsyncNextArrowArray();
3619 :
3620 10825 : if (m_poFilterGeom != nullptr)
3621 : {
3622 : // Both are exclusive
3623 10008 : CreateSpatialIndexIfNecessary();
3624 10008 : if (!RunDeferredSpatialIndexUpdate())
3625 0 : return nullptr;
3626 : }
3627 :
3628 10825 : OGRFeature *poFeature = OGRGeoPackageLayer::GetNextFeature();
3629 10825 : if (poFeature && m_iFIDAsRegularColumnIndex >= 0)
3630 : {
3631 1 : poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
3632 : }
3633 10825 : return poFeature;
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* GetFeature() */
3638 : /************************************************************************/
3639 :
3640 1289 : OGRFeature *OGRGeoPackageTableLayer::GetFeature(GIntBig nFID)
3641 : {
3642 1289 : if (!m_bFeatureDefnCompleted)
3643 9 : GetLayerDefn();
3644 1289 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3645 0 : return nullptr;
3646 1289 : CancelAsyncNextArrowArray();
3647 :
3648 1289 : if (m_pszFidColumn == nullptr)
3649 1 : return OGRLayer::GetFeature(nFID);
3650 :
3651 1288 : if (m_poGetFeatureStatement == nullptr)
3652 : {
3653 44 : CPLString soSQL;
3654 : soSQL.Printf("SELECT %s FROM \"%s\" m "
3655 : "WHERE \"%s\" = ?",
3656 88 : m_soColumns.c_str(), SQLEscapeName(m_pszTableName).c_str(),
3657 132 : SQLEscapeName(m_pszFidColumn).c_str());
3658 :
3659 44 : const int err = sqlite3_prepare_v2(m_poDS->GetDB(), soSQL.c_str(), -1,
3660 : &m_poGetFeatureStatement, nullptr);
3661 44 : if (err != SQLITE_OK)
3662 : {
3663 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s",
3664 : soSQL.c_str());
3665 0 : return nullptr;
3666 : }
3667 : }
3668 :
3669 1288 : CPL_IGNORE_RET_VAL(sqlite3_bind_int64(m_poGetFeatureStatement, 1, nFID));
3670 :
3671 : /* Should be only one or zero results */
3672 1288 : const int err = sqlite3_step(m_poGetFeatureStatement);
3673 :
3674 : /* Aha, got one */
3675 1288 : if (err == SQLITE_ROW)
3676 : {
3677 1276 : OGRFeature *poFeature = TranslateFeature(m_poGetFeatureStatement);
3678 1276 : if (m_iFIDAsRegularColumnIndex >= 0)
3679 : {
3680 7 : poFeature->SetField(m_iFIDAsRegularColumnIndex,
3681 : poFeature->GetFID());
3682 : }
3683 :
3684 1276 : sqlite3_reset(m_poGetFeatureStatement);
3685 1276 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3686 :
3687 1276 : return poFeature;
3688 : }
3689 :
3690 12 : sqlite3_reset(m_poGetFeatureStatement);
3691 12 : sqlite3_clear_bindings(m_poGetFeatureStatement);
3692 :
3693 : /* Error out on all other return codes */
3694 12 : return nullptr;
3695 : }
3696 :
3697 : /************************************************************************/
3698 : /* DeleteFeature() */
3699 : /************************************************************************/
3700 :
3701 50 : OGRErr OGRGeoPackageTableLayer::DeleteFeature(GIntBig nFID)
3702 : {
3703 50 : if (!m_bFeatureDefnCompleted)
3704 4 : GetLayerDefn();
3705 50 : if (!m_poDS->GetUpdate())
3706 : {
3707 0 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
3708 : "DeleteFeature");
3709 0 : return OGRERR_FAILURE;
3710 : }
3711 50 : if (m_pszFidColumn == nullptr)
3712 : {
3713 0 : return OGRERR_FAILURE;
3714 : }
3715 :
3716 50 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
3717 0 : return OGRERR_FAILURE;
3718 :
3719 50 : CancelAsyncNextArrowArray();
3720 :
3721 50 : if (m_bThreadRTreeStarted)
3722 12 : CancelAsyncRTree();
3723 :
3724 50 : if (!RunDeferredSpatialIndexUpdate())
3725 0 : return OGRERR_FAILURE;
3726 :
3727 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3728 50 : if (m_bOGRFeatureCountTriggersEnabled)
3729 : {
3730 16 : DisableFeatureCountTriggers();
3731 : }
3732 : #endif
3733 :
3734 : /* Clear out any existing query */
3735 50 : ResetReading();
3736 :
3737 : /* No filters apply, just use the FID */
3738 50 : CPLString soSQL;
3739 : soSQL.Printf("DELETE FROM \"%s\" WHERE \"%s\" = " CPL_FRMT_GIB,
3740 100 : SQLEscapeName(m_pszTableName).c_str(),
3741 150 : SQLEscapeName(m_pszFidColumn).c_str(), nFID);
3742 :
3743 : const sqlite3_int64 nTotalChangesBefore =
3744 : #if SQLITE_VERSION_NUMBER >= 3037000L
3745 50 : sqlite3_total_changes64(m_poDS->GetDB());
3746 : #else
3747 : sqlite3_total_changes(m_poDS->GetDB());
3748 : #endif
3749 :
3750 50 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), soSQL.c_str());
3751 50 : if (eErr == OGRERR_NONE)
3752 : {
3753 : const sqlite3_int64 nTotalChangesAfter =
3754 : #if SQLITE_VERSION_NUMBER >= 3037000L
3755 49 : sqlite3_total_changes64(m_poDS->GetDB());
3756 : #else
3757 : sqlite3_total_changes(m_poDS->GetDB());
3758 : #endif
3759 :
3760 49 : eErr = nTotalChangesAfter != nTotalChangesBefore
3761 49 : ? OGRERR_NONE
3762 : : OGRERR_NON_EXISTING_FEATURE;
3763 :
3764 49 : if (eErr == OGRERR_NONE)
3765 : {
3766 : #ifdef ENABLE_GPKG_OGR_CONTENTS
3767 41 : if (m_nTotalFeatureCount >= 0)
3768 33 : m_nTotalFeatureCount--;
3769 : #endif
3770 :
3771 41 : m_bContentChanged = true;
3772 : }
3773 : }
3774 50 : return eErr;
3775 : }
3776 :
3777 : /************************************************************************/
3778 : /* DoJobAtTransactionCommit() */
3779 : /************************************************************************/
3780 :
3781 222 : bool OGRGeoPackageTableLayer::DoJobAtTransactionCommit()
3782 : {
3783 222 : if (m_bAllowedRTreeThread)
3784 114 : return true;
3785 :
3786 216 : bool ret = RunDeferredCreationIfNecessary() == OGRERR_NONE &&
3787 108 : RunDeferredSpatialIndexUpdate();
3788 108 : m_nCountInsertInTransaction = 0;
3789 108 : m_aoRTreeTriggersSQL.clear();
3790 108 : m_aoRTreeEntries.clear();
3791 108 : return ret;
3792 : }
3793 :
3794 : /************************************************************************/
3795 : /* DoJobAtTransactionRollback() */
3796 : /************************************************************************/
3797 :
3798 35 : bool OGRGeoPackageTableLayer::DoJobAtTransactionRollback()
3799 : {
3800 35 : if (m_bThreadRTreeStarted)
3801 12 : CancelAsyncRTree();
3802 35 : m_nCountInsertInTransaction = 0;
3803 35 : m_aoRTreeTriggersSQL.clear();
3804 35 : m_aoRTreeEntries.clear();
3805 35 : if (m_bTableCreatedInTransaction)
3806 : {
3807 1 : SyncToDisk();
3808 : }
3809 : else
3810 : {
3811 34 : bool bDeferredSpatialIndexCreationBackup =
3812 : m_bDeferredSpatialIndexCreation;
3813 34 : m_bDeferredSpatialIndexCreation = false;
3814 34 : SyncToDisk();
3815 34 : m_bDeferredSpatialIndexCreation = bDeferredSpatialIndexCreationBackup;
3816 : }
3817 :
3818 : // If we are restoring any deleted field or removing any added one we have to
3819 : // rebuild the array of generated fields
3820 43 : for (int i = static_cast<int>(m_apoFieldDefnChanges.size()) - 1; i >= 0;
3821 : i--)
3822 : {
3823 8 : auto &oFieldChange = m_apoFieldDefnChanges[i];
3824 8 : switch (oFieldChange.eChangeType)
3825 : {
3826 3 : case FieldChangeType::ADD_FIELD:
3827 : {
3828 3 : CPLAssert(oFieldChange.iField <
3829 : static_cast<int>(m_abGeneratedColumns.size()));
3830 0 : m_abGeneratedColumns.erase(m_abGeneratedColumns.begin() +
3831 3 : oFieldChange.iField);
3832 3 : break;
3833 : };
3834 3 : case FieldChangeType::DELETE_FIELD:
3835 : {
3836 3 : CPLAssert(oFieldChange.iField <=
3837 : static_cast<int>(m_abGeneratedColumns.size()));
3838 0 : m_abGeneratedColumns.insert(m_abGeneratedColumns.begin() +
3839 3 : oFieldChange.iField,
3840 6 : oFieldChange.bGenerated);
3841 3 : break;
3842 : };
3843 2 : case FieldChangeType::ALTER_FIELD:
3844 : {
3845 2 : CPLAssert(oFieldChange.iField <
3846 : static_cast<int>(m_abGeneratedColumns.size()));
3847 4 : m_abGeneratedColumns[oFieldChange.iField] =
3848 2 : oFieldChange.bGenerated;
3849 2 : break;
3850 : };
3851 : }
3852 : }
3853 :
3854 35 : ResetReading();
3855 35 : return true;
3856 : }
3857 :
3858 : /************************************************************************/
3859 : /* StartDeferredSpatialIndexUpdate() */
3860 : /************************************************************************/
3861 :
3862 7 : bool OGRGeoPackageTableLayer::StartDeferredSpatialIndexUpdate()
3863 : {
3864 7 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
3865 0 : return true;
3866 :
3867 7 : RevertWorkaroundUpdate1TriggerIssue();
3868 :
3869 7 : m_aoRTreeTriggersSQL.clear();
3870 7 : m_aoRTreeEntries.clear();
3871 :
3872 7 : const char *pszT = m_pszTableName;
3873 7 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3874 7 : m_osRTreeName = "rtree_";
3875 7 : m_osRTreeName += pszT;
3876 7 : m_osRTreeName += "_";
3877 7 : m_osRTreeName += pszC;
3878 :
3879 63 : char *pszSQL = sqlite3_mprintf(
3880 : "SELECT sql FROM sqlite_master WHERE type = 'trigger' "
3881 : "AND name IN ('%q', '%q', '%q', '%q', '%q', '%q', "
3882 : "'%q', '%q', '%q')",
3883 14 : (m_osRTreeName + "_insert").c_str(),
3884 14 : (m_osRTreeName + "_update1").c_str(),
3885 14 : (m_osRTreeName + "_update2").c_str(),
3886 14 : (m_osRTreeName + "_update3").c_str(),
3887 14 : (m_osRTreeName + "_update4").c_str(),
3888 : // update5 replaces update3 in GPKG 1.4
3889 : // cf https://github.com/opengeospatial/geopackage/pull/661
3890 14 : (m_osRTreeName + "_update5").c_str(),
3891 : // update6 and update7 replace update1 in GPKG 1.4
3892 : // cf https://github.com/opengeospatial/geopackage/pull/661
3893 14 : (m_osRTreeName + "_update6").c_str(),
3894 14 : (m_osRTreeName + "_update7").c_str(),
3895 14 : (m_osRTreeName + "_delete").c_str());
3896 14 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
3897 7 : sqlite3_free(pszSQL);
3898 7 : if (oResult)
3899 : {
3900 52 : for (int iRecord = 0; iRecord < oResult->RowCount(); iRecord++)
3901 : {
3902 45 : const char *pszTriggerSQL = oResult->GetValue(0, iRecord);
3903 45 : if (pszTriggerSQL)
3904 : {
3905 45 : m_aoRTreeTriggersSQL.push_back(pszTriggerSQL);
3906 : }
3907 : }
3908 : }
3909 7 : if (m_aoRTreeTriggersSQL.size() != 6 && m_aoRTreeTriggersSQL.size() != 7)
3910 : {
3911 0 : CPLDebug("GPKG", "Could not find expected RTree triggers");
3912 0 : m_aoRTreeTriggersSQL.clear();
3913 0 : return false;
3914 : }
3915 :
3916 7 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers());
3917 :
3918 7 : return true;
3919 : }
3920 :
3921 : /************************************************************************/
3922 : /* FlushPendingSpatialIndexUpdate() */
3923 : /************************************************************************/
3924 :
3925 5 : bool OGRGeoPackageTableLayer::FlushPendingSpatialIndexUpdate()
3926 : {
3927 5 : bool ret = true;
3928 :
3929 : // CPLDebug("GPKG", "Insert %d features in spatial index",
3930 : // static_cast<int>(m_aoRTreeEntries.size()));
3931 :
3932 5 : const char *pszT = m_pszTableName;
3933 5 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
3934 :
3935 5 : m_osRTreeName = "rtree_";
3936 5 : m_osRTreeName += pszT;
3937 5 : m_osRTreeName += "_";
3938 5 : m_osRTreeName += pszC;
3939 :
3940 5 : char *pszSQL = sqlite3_mprintf("INSERT INTO \"%w\" VALUES (?,?,?,?,?)",
3941 : m_osRTreeName.c_str());
3942 5 : sqlite3_stmt *hInsertStmt = nullptr;
3943 5 : if (sqlite3_prepare_v2(m_poDS->GetDB(), pszSQL, -1, &hInsertStmt,
3944 5 : nullptr) != SQLITE_OK)
3945 : {
3946 0 : CPLError(CE_Failure, CPLE_AppDefined, "failed to prepare SQL: %s",
3947 : pszSQL);
3948 0 : sqlite3_free(pszSQL);
3949 0 : m_aoRTreeEntries.clear();
3950 0 : return false;
3951 : }
3952 5 : sqlite3_free(pszSQL);
3953 :
3954 190 : for (size_t i = 0; i < m_aoRTreeEntries.size(); ++i)
3955 : {
3956 185 : sqlite3_reset(hInsertStmt);
3957 :
3958 185 : sqlite3_bind_int64(hInsertStmt, 1, m_aoRTreeEntries[i].nId);
3959 185 : sqlite3_bind_double(hInsertStmt, 2, m_aoRTreeEntries[i].fMinX);
3960 185 : sqlite3_bind_double(hInsertStmt, 3, m_aoRTreeEntries[i].fMaxX);
3961 185 : sqlite3_bind_double(hInsertStmt, 4, m_aoRTreeEntries[i].fMinY);
3962 185 : sqlite3_bind_double(hInsertStmt, 5, m_aoRTreeEntries[i].fMaxY);
3963 185 : int sqlite_err = sqlite3_step(hInsertStmt);
3964 185 : if (sqlite_err != SQLITE_OK && sqlite_err != SQLITE_DONE)
3965 : {
3966 0 : CPLError(CE_Failure, CPLE_AppDefined,
3967 : "failed to execute insertion in RTree : %s",
3968 0 : sqlite3_errmsg(m_poDS->GetDB()));
3969 0 : ret = false;
3970 0 : break;
3971 : }
3972 : }
3973 5 : sqlite3_finalize(hInsertStmt);
3974 5 : m_aoRTreeEntries.clear();
3975 5 : return ret;
3976 : }
3977 :
3978 : /************************************************************************/
3979 : /* RunDeferredSpatialIndexUpdate() */
3980 : /************************************************************************/
3981 :
3982 14542 : bool OGRGeoPackageTableLayer::RunDeferredSpatialIndexUpdate()
3983 : {
3984 14542 : m_nCountInsertInTransaction = 0;
3985 14542 : if (m_aoRTreeTriggersSQL.empty())
3986 14537 : return true;
3987 :
3988 5 : bool ret = FlushPendingSpatialIndexUpdate();
3989 :
3990 5 : RevertWorkaroundUpdate1TriggerIssue();
3991 :
3992 37 : for (const auto &osSQL : m_aoRTreeTriggersSQL)
3993 : {
3994 32 : ret &= SQLCommand(m_poDS->GetDB(), osSQL) == OGRERR_NONE;
3995 : }
3996 5 : m_aoRTreeTriggersSQL.clear();
3997 5 : return ret;
3998 : }
3999 :
4000 : /************************************************************************/
4001 : /* SyncToDisk() */
4002 : /************************************************************************/
4003 :
4004 9068 : OGRErr OGRGeoPackageTableLayer::SyncToDisk()
4005 : {
4006 9068 : if (!m_bFeatureDefnCompleted)
4007 4882 : return OGRERR_NONE;
4008 :
4009 4186 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4010 0 : return OGRERR_FAILURE;
4011 :
4012 : // Both are exclusive
4013 4186 : CreateSpatialIndexIfNecessary();
4014 4186 : if (!RunDeferredSpatialIndexUpdate())
4015 0 : return OGRERR_FAILURE;
4016 4186 : RevertWorkaroundUpdate1TriggerIssue();
4017 :
4018 : /* Save metadata back to the database */
4019 4186 : SaveExtent();
4020 4186 : SaveTimestamp();
4021 :
4022 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4023 4186 : CreateFeatureCountTriggers();
4024 : #endif
4025 :
4026 4186 : return OGRERR_NONE;
4027 : }
4028 :
4029 : /************************************************************************/
4030 : /* StartTransaction() */
4031 : /************************************************************************/
4032 :
4033 109 : OGRErr OGRGeoPackageTableLayer::StartTransaction()
4034 : {
4035 109 : CancelAsyncNextArrowArray();
4036 109 : return m_poDS->StartTransaction();
4037 : }
4038 :
4039 : /************************************************************************/
4040 : /* CommitTransaction() */
4041 : /************************************************************************/
4042 :
4043 83 : OGRErr OGRGeoPackageTableLayer::CommitTransaction()
4044 : {
4045 83 : return m_poDS->CommitTransaction();
4046 : }
4047 :
4048 : /************************************************************************/
4049 : /* RollbackTransaction() */
4050 : /************************************************************************/
4051 :
4052 18 : OGRErr OGRGeoPackageTableLayer::RollbackTransaction()
4053 : {
4054 18 : return m_poDS->RollbackTransaction();
4055 : }
4056 :
4057 : /************************************************************************/
4058 : /* GetTotalFeatureCount() */
4059 : /************************************************************************/
4060 :
4061 404 : GIntBig OGRGeoPackageTableLayer::GetTotalFeatureCount()
4062 : {
4063 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4064 404 : if (m_nTotalFeatureCount < 0 && m_poDS->m_bHasGPKGOGRContents)
4065 : {
4066 : char *pszSQL =
4067 16 : sqlite3_mprintf("SELECT feature_count FROM gpkg_ogr_contents WHERE "
4068 : "lower(table_name) = lower('%q') LIMIT 2",
4069 : m_pszTableName);
4070 32 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4071 16 : sqlite3_free(pszSQL);
4072 16 : if (oResult && oResult->RowCount() == 1)
4073 : {
4074 13 : const char *pszFeatureCount = oResult->GetValue(0, 0);
4075 13 : if (pszFeatureCount)
4076 : {
4077 5 : m_nTotalFeatureCount = CPLAtoGIntBig(pszFeatureCount);
4078 : }
4079 : }
4080 : }
4081 404 : return m_nTotalFeatureCount;
4082 : #else
4083 : return 0;
4084 : #endif
4085 : }
4086 :
4087 : /************************************************************************/
4088 : /* GetFeatureCount() */
4089 : /************************************************************************/
4090 :
4091 23033 : GIntBig OGRGeoPackageTableLayer::GetFeatureCount(int /*bForce*/)
4092 : {
4093 23033 : if (!m_bFeatureDefnCompleted)
4094 48 : GetLayerDefn();
4095 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4096 23033 : if (m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr)
4097 : {
4098 154 : const auto nCount = GetTotalFeatureCount();
4099 154 : if (nCount >= 0)
4100 145 : return nCount;
4101 : }
4102 : #endif
4103 :
4104 22888 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4105 0 : return 0;
4106 :
4107 22888 : CancelAsyncNextArrowArray();
4108 :
4109 : /* Ignore bForce, because we always do a full count on the database */
4110 : OGRErr err;
4111 45776 : CPLString soSQL;
4112 22888 : bool bUnregisterSQLFunction = false;
4113 22887 : if (m_bIsTable && m_poFilterGeom != nullptr &&
4114 45775 : m_pszAttrQueryString == nullptr && HasSpatialIndex())
4115 : {
4116 22865 : OGREnvelope sEnvelope;
4117 :
4118 22865 : m_poFilterGeom->getEnvelope(&sEnvelope);
4119 :
4120 45730 : if (!std::isinf(sEnvelope.MinX) && !std::isinf(sEnvelope.MinY) &&
4121 45730 : !std::isinf(sEnvelope.MaxX) && !std::isinf(sEnvelope.MaxY))
4122 : {
4123 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" WHERE "
4124 : "maxx >= %.12f AND minx <= %.12f AND "
4125 : "maxy >= %.12f AND miny <= %.12f",
4126 45730 : SQLEscapeName(m_osRTreeName).c_str(),
4127 22865 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4128 45730 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
4129 :
4130 45730 : if (OGRGeometryFactory::haveGEOS() &&
4131 22865 : !(m_bFilterIsEnvelope &&
4132 22865 : wkbFlatten(
4133 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4134 : ->GetType()) == wkbPoint))
4135 : {
4136 22853 : bUnregisterSQLFunction = true;
4137 22853 : sqlite3_create_function(
4138 22853 : m_poDS->hDB, "OGR_GPKG_Intersects_Spatial_Filter", 1,
4139 : SQLITE_UTF8, this, OGR_GPKG_Intersects_Spatial_Filter,
4140 : nullptr, nullptr);
4141 : const char *pszC =
4142 22853 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomFieldFilter)
4143 22853 : ->GetNameRef();
4144 : soSQL.Printf("SELECT COUNT(*) FROM \"%s\" m "
4145 : "JOIN \"%s\" r "
4146 : "ON m.\"%s\" = r.id WHERE "
4147 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
4148 : "r.maxy >= %.12f AND r.miny <= %.12f AND "
4149 : "OGR_GPKG_Intersects_Spatial_Filter(m.\"%s\")",
4150 45706 : SQLEscapeName(m_pszTableName).c_str(),
4151 45706 : SQLEscapeName(m_osRTreeName).c_str(),
4152 45706 : SQLEscapeName(m_osFIDForRTree).c_str(),
4153 22853 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
4154 22853 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11,
4155 114265 : SQLEscapeName(pszC).c_str());
4156 : }
4157 : }
4158 : }
4159 :
4160 22888 : if (soSQL.empty())
4161 : {
4162 23 : if (!m_soFilter.empty())
4163 : soSQL.Printf("SELECT Count(*) FROM \"%s\" WHERE %s",
4164 28 : SQLEscapeName(m_pszTableName).c_str(),
4165 28 : m_soFilter.c_str());
4166 : else
4167 : soSQL.Printf("SELECT Count(*) FROM \"%s\"",
4168 9 : SQLEscapeName(m_pszTableName).c_str());
4169 : }
4170 :
4171 : /* Just run the query directly and get back integer */
4172 : GIntBig iFeatureCount =
4173 22888 : SQLGetInteger64(m_poDS->GetDB(), soSQL.c_str(), &err);
4174 :
4175 22888 : if (bUnregisterSQLFunction)
4176 : {
4177 22853 : sqlite3_create_function(m_poDS->hDB,
4178 : "OGR_GPKG_Intersects_Spatial_Filter", 1,
4179 : SQLITE_UTF8, this, nullptr, nullptr, nullptr);
4180 : }
4181 :
4182 : /* Generic implementation uses -1 for error condition, so we will too */
4183 22888 : if (err == OGRERR_NONE)
4184 : {
4185 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4186 22888 : if (m_bIsTable && m_poFilterGeom == nullptr &&
4187 15 : m_pszAttrQueryString == nullptr)
4188 : {
4189 9 : m_nTotalFeatureCount = iFeatureCount;
4190 :
4191 9 : if (m_poDS->GetUpdate() && m_poDS->m_bHasGPKGOGRContents)
4192 : {
4193 : const char *pszCount =
4194 4 : CPLSPrintf(CPL_FRMT_GIB, m_nTotalFeatureCount);
4195 4 : char *pszSQL = sqlite3_mprintf(
4196 : "UPDATE gpkg_ogr_contents SET feature_count = %s WHERE "
4197 : "lower(table_name )= lower('%q')",
4198 : pszCount, m_pszTableName);
4199 4 : SQLCommand(m_poDS->GetDB(), pszSQL);
4200 4 : sqlite3_free(pszSQL);
4201 : }
4202 : }
4203 : #endif
4204 22888 : return iFeatureCount;
4205 : }
4206 : else
4207 0 : return -1;
4208 : }
4209 :
4210 : /************************************************************************/
4211 : /* GetExtentFromRTree() */
4212 : /************************************************************************/
4213 :
4214 111 : static bool GetExtentFromRTree(sqlite3 *hDB, const std::string &osRTreeName,
4215 : double &minx, double &miny, double &maxx,
4216 : double &maxy)
4217 : {
4218 : // Cf https://github.com/sqlite/sqlite/blob/master/ext/rtree/rtree.c
4219 : // for the description of the content of the rtree _node table
4220 : // We fetch the root node (nodeno = 1) and iterates over its cells, to
4221 : // take the min/max of their minx/maxx/miny/maxy values.
4222 111 : char *pszSQL = sqlite3_mprintf(
4223 : "SELECT data FROM \"%w_node\" WHERE nodeno = 1", osRTreeName.c_str());
4224 111 : sqlite3_stmt *hStmt = nullptr;
4225 111 : CPL_IGNORE_RET_VAL(sqlite3_prepare_v2(hDB, pszSQL, -1, &hStmt, nullptr));
4226 111 : sqlite3_free(pszSQL);
4227 111 : bool bOK = false;
4228 111 : if (hStmt)
4229 : {
4230 222 : if (sqlite3_step(hStmt) == SQLITE_ROW &&
4231 111 : sqlite3_column_type(hStmt, 0) == SQLITE_BLOB)
4232 : {
4233 111 : const int nBytes = sqlite3_column_bytes(hStmt, 0);
4234 : // coverity[tainted_data_return]
4235 : const GByte *pabyData =
4236 111 : static_cast<const GByte *>(sqlite3_column_blob(hStmt, 0));
4237 111 : constexpr int BLOB_HEADER_SIZE = 4;
4238 111 : if (nBytes > BLOB_HEADER_SIZE)
4239 : {
4240 111 : const int nCellCount = (pabyData[2] << 8) | pabyData[3];
4241 111 : constexpr int SIZEOF_CELL = 24; // int64_t + 4 float
4242 111 : if (nCellCount >= 1 &&
4243 104 : nBytes >= BLOB_HEADER_SIZE + SIZEOF_CELL * nCellCount)
4244 : {
4245 104 : minx = std::numeric_limits<double>::max();
4246 104 : miny = std::numeric_limits<double>::max();
4247 104 : maxx = -std::numeric_limits<double>::max();
4248 104 : maxy = -std::numeric_limits<double>::max();
4249 104 : size_t offset = BLOB_HEADER_SIZE;
4250 394 : for (int i = 0; i < nCellCount; ++i)
4251 : {
4252 290 : offset += sizeof(int64_t);
4253 :
4254 : float fMinX;
4255 290 : memcpy(&fMinX, pabyData + offset, sizeof(float));
4256 290 : offset += sizeof(float);
4257 290 : CPL_MSBPTR32(&fMinX);
4258 290 : minx = std::min(minx, static_cast<double>(fMinX));
4259 :
4260 : float fMaxX;
4261 290 : memcpy(&fMaxX, pabyData + offset, sizeof(float));
4262 290 : offset += sizeof(float);
4263 290 : CPL_MSBPTR32(&fMaxX);
4264 290 : maxx = std::max(maxx, static_cast<double>(fMaxX));
4265 :
4266 : float fMinY;
4267 290 : memcpy(&fMinY, pabyData + offset, sizeof(float));
4268 290 : offset += sizeof(float);
4269 290 : CPL_MSBPTR32(&fMinY);
4270 290 : miny = std::min(miny, static_cast<double>(fMinY));
4271 :
4272 : float fMaxY;
4273 290 : memcpy(&fMaxY, pabyData + offset, sizeof(float));
4274 290 : offset += sizeof(float);
4275 290 : CPL_MSBPTR32(&fMaxY);
4276 290 : maxy = std::max(maxy, static_cast<double>(fMaxY));
4277 : }
4278 :
4279 104 : bOK = true;
4280 : }
4281 : }
4282 : }
4283 111 : sqlite3_finalize(hStmt);
4284 : }
4285 111 : return bOK;
4286 : }
4287 :
4288 : /************************************************************************/
4289 : /* GetExtent() */
4290 : /************************************************************************/
4291 :
4292 299 : OGRErr OGRGeoPackageTableLayer::GetExtent(OGREnvelope *psExtent, int bForce)
4293 : {
4294 299 : if (!m_bFeatureDefnCompleted)
4295 7 : GetLayerDefn();
4296 : /* Extent already calculated! We're done. */
4297 299 : if (m_poExtent != nullptr)
4298 : {
4299 284 : if (psExtent)
4300 : {
4301 284 : *psExtent = *m_poExtent;
4302 : }
4303 284 : return OGRERR_NONE;
4304 : }
4305 :
4306 15 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4307 0 : return OGRERR_FAILURE;
4308 :
4309 15 : CancelAsyncNextArrowArray();
4310 :
4311 24 : if (m_poFeatureDefn->GetGeomFieldCount() && HasSpatialIndex() &&
4312 9 : CPLTestBool(
4313 : CPLGetConfigOption("OGR_GPKG_USE_RTREE_FOR_GET_EXTENT", "TRUE")))
4314 : {
4315 9 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, psExtent->MinX,
4316 9 : psExtent->MinY, psExtent->MaxX, psExtent->MaxY))
4317 : {
4318 2 : m_poExtent = new OGREnvelope(*psExtent);
4319 2 : m_bExtentChanged = true;
4320 2 : SaveExtent();
4321 2 : return OGRERR_NONE;
4322 : }
4323 : else
4324 : {
4325 7 : UpdateContentsToNullExtent();
4326 7 : return OGRERR_FAILURE;
4327 : }
4328 : }
4329 :
4330 : /* User is OK with expensive calculation */
4331 6 : if (bForce && m_poFeatureDefn->GetGeomFieldCount())
4332 : {
4333 : /* fall back to default implementation (scan all features) and save */
4334 : /* the result for later */
4335 4 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4336 4 : char *pszSQL = sqlite3_mprintf(
4337 : "SELECT MIN(ST_MinX(\"%w\")), MIN(ST_MinY(\"%w\")), "
4338 : "MAX(ST_MaxX(\"%w\")), MAX(ST_MaxY(\"%w\")) FROM \"%w\" WHERE "
4339 : "\"%w\" IS NOT NULL AND NOT ST_IsEmpty(\"%w\")",
4340 : pszC, pszC, pszC, pszC, m_pszTableName, pszC, pszC);
4341 8 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4342 4 : sqlite3_free(pszSQL);
4343 4 : delete m_poExtent;
4344 4 : m_poExtent = nullptr;
4345 8 : if (oResult && oResult->RowCount() == 1 &&
4346 4 : oResult->GetValue(0, 0) != nullptr)
4347 : {
4348 3 : psExtent->MinX = CPLAtof(oResult->GetValue(0, 0));
4349 3 : psExtent->MinY = CPLAtof(oResult->GetValue(1, 0));
4350 3 : psExtent->MaxX = CPLAtof(oResult->GetValue(2, 0));
4351 3 : psExtent->MaxY = CPLAtof(oResult->GetValue(3, 0));
4352 3 : m_poExtent = new OGREnvelope(*psExtent);
4353 3 : m_bExtentChanged = true;
4354 3 : SaveExtent();
4355 : }
4356 : else
4357 : {
4358 1 : UpdateContentsToNullExtent();
4359 1 : return OGRERR_FAILURE; // we didn't get an extent
4360 : }
4361 3 : return OGRERR_NONE;
4362 : }
4363 :
4364 2 : return OGRERR_FAILURE;
4365 : }
4366 :
4367 : /************************************************************************/
4368 : /* UpdateContentsToNullExtent() */
4369 : /************************************************************************/
4370 :
4371 8 : void OGRGeoPackageTableLayer::UpdateContentsToNullExtent()
4372 : {
4373 8 : if (m_poDS->GetUpdate())
4374 : {
4375 : char *pszSQL =
4376 3 : sqlite3_mprintf("UPDATE gpkg_contents SET "
4377 : "min_x = NULL, min_y = NULL, "
4378 : "max_x = NULL, max_y = NULL "
4379 : "WHERE lower(table_name) = lower('%q') AND "
4380 : "Lower(data_type) = 'features'",
4381 : m_pszTableName);
4382 3 : SQLCommand(m_poDS->GetDB(), pszSQL);
4383 3 : sqlite3_free(pszSQL);
4384 : }
4385 8 : m_bExtentChanged = false;
4386 8 : }
4387 :
4388 : /************************************************************************/
4389 : /* RecomputeExtent() */
4390 : /************************************************************************/
4391 :
4392 4 : void OGRGeoPackageTableLayer::RecomputeExtent()
4393 : {
4394 4 : m_bExtentChanged = true;
4395 4 : delete m_poExtent;
4396 4 : m_poExtent = nullptr;
4397 4 : OGREnvelope sExtent;
4398 4 : GetExtent(&sExtent, true);
4399 4 : }
4400 :
4401 : /************************************************************************/
4402 : /* TestCapability() */
4403 : /************************************************************************/
4404 :
4405 1351 : int OGRGeoPackageTableLayer::TestCapability(const char *pszCap)
4406 : {
4407 1351 : if (!m_bFeatureDefnCompleted)
4408 4 : GetLayerDefn();
4409 1351 : if (EQUAL(pszCap, OLCSequentialWrite))
4410 : {
4411 24 : return m_poDS->GetUpdate();
4412 : }
4413 1327 : else if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
4414 1298 : EQUAL(pszCap, OLCAlterFieldDefn) ||
4415 1291 : EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
4416 1290 : EQUAL(pszCap, OLCReorderFields) || EQUAL(pszCap, OLCRename))
4417 : {
4418 45 : return m_poDS->GetUpdate() && m_bIsTable;
4419 : }
4420 1282 : else if (EQUAL(pszCap, OLCDeleteFeature) ||
4421 1275 : EQUAL(pszCap, OLCUpsertFeature) ||
4422 1275 : EQUAL(pszCap, OLCUpdateFeature) || EQUAL(pszCap, OLCRandomWrite))
4423 : {
4424 12 : return m_poDS->GetUpdate() && m_pszFidColumn != nullptr;
4425 : }
4426 1270 : else if (EQUAL(pszCap, OLCRandomRead))
4427 : {
4428 6 : return m_pszFidColumn != nullptr;
4429 : }
4430 1264 : else if (EQUAL(pszCap, OLCTransactions))
4431 : {
4432 7 : return TRUE;
4433 : }
4434 : #ifdef ENABLE_GPKG_OGR_CONTENTS
4435 1257 : else if (EQUAL(pszCap, OLCFastFeatureCount))
4436 : {
4437 4 : return m_poFilterGeom == nullptr && m_pszAttrQueryString == nullptr &&
4438 4 : m_nTotalFeatureCount >= 0;
4439 : }
4440 : #endif
4441 1255 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
4442 : {
4443 3 : return HasSpatialIndex() || m_bDeferredSpatialIndexCreation;
4444 : }
4445 1252 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
4446 : {
4447 : // Fast may not be that true on large layers, but better than the
4448 : // default implementation for sure...
4449 0 : return TRUE;
4450 : }
4451 1252 : else if (EQUAL(pszCap, OLCFastGetExtent))
4452 : {
4453 5 : return (m_poExtent != nullptr);
4454 : }
4455 1247 : else if (EQUAL(pszCap, OLCCurveGeometries))
4456 643 : return TRUE;
4457 604 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
4458 544 : return TRUE;
4459 60 : else if (EQUAL(pszCap, OLCZGeometries))
4460 9 : return TRUE;
4461 51 : if (EQUAL(pszCap, OLCFastGetExtent3D))
4462 0 : return TRUE;
4463 : else
4464 : {
4465 51 : return OGRGeoPackageLayer::TestCapability(pszCap);
4466 : }
4467 : }
4468 :
4469 : /************************************************************************/
4470 : /* CreateSpatialIndexIfNecessary() */
4471 : /************************************************************************/
4472 :
4473 17996 : void OGRGeoPackageTableLayer::CreateSpatialIndexIfNecessary()
4474 : {
4475 17996 : if (m_bDeferredSpatialIndexCreation)
4476 : {
4477 607 : CreateSpatialIndex();
4478 : }
4479 17996 : }
4480 :
4481 : /************************************************************************/
4482 : /* CreateSpatialIndex() */
4483 : /************************************************************************/
4484 :
4485 617 : bool OGRGeoPackageTableLayer::CreateSpatialIndex(const char *pszTableName)
4486 : {
4487 : OGRErr err;
4488 :
4489 617 : if (!m_bFeatureDefnCompleted)
4490 2 : GetLayerDefn();
4491 :
4492 617 : if (!CheckUpdatableTable("CreateSpatialIndex"))
4493 1 : return false;
4494 :
4495 616 : if (m_bDropRTreeTable)
4496 : {
4497 0 : CPLError(CE_Failure, CPLE_AppDefined,
4498 : "Cannot run CreateSpatialIndex() after non-completed deferred "
4499 : "DropSpatialIndex()");
4500 0 : return false;
4501 : }
4502 :
4503 616 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
4504 0 : return false;
4505 :
4506 616 : CancelAsyncNextArrowArray();
4507 :
4508 616 : m_bDeferredSpatialIndexCreation = false;
4509 :
4510 616 : if (m_pszFidColumn == nullptr)
4511 0 : return false;
4512 :
4513 616 : if (HasSpatialIndex())
4514 : {
4515 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index already existing");
4516 1 : return false;
4517 : }
4518 :
4519 615 : if (m_poFeatureDefn->GetGeomFieldCount() == 0)
4520 : {
4521 1 : CPLError(CE_Failure, CPLE_AppDefined, "No geometry column");
4522 1 : return false;
4523 : }
4524 614 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
4525 0 : return false;
4526 :
4527 614 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4528 614 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4529 614 : const char *pszI = GetFIDColumn();
4530 :
4531 614 : m_osRTreeName = "rtree_";
4532 614 : m_osRTreeName += pszT;
4533 614 : m_osRTreeName += "_";
4534 614 : m_osRTreeName += pszC;
4535 614 : m_osFIDForRTree = m_pszFidColumn;
4536 :
4537 614 : bool bPopulateFromThreadRTree = false;
4538 614 : if (m_bThreadRTreeStarted)
4539 : {
4540 12 : const bool bThreadHasFinished = m_oQueueRTreeEntries.empty();
4541 12 : if (!m_aoRTreeEntries.empty())
4542 4 : m_oQueueRTreeEntries.push(std::move(m_aoRTreeEntries));
4543 12 : m_aoRTreeEntries = std::vector<GPKGRTreeEntry>();
4544 12 : m_oQueueRTreeEntries.push(m_aoRTreeEntries);
4545 12 : if (!bThreadHasFinished)
4546 1 : CPLDebug("GPKG", "Waiting for background RTree building to finish");
4547 12 : m_oThreadRTree.join();
4548 12 : if (!bThreadHasFinished)
4549 : {
4550 1 : CPLDebug("GPKG", "Background RTree building finished");
4551 : }
4552 12 : m_bAllowedRTreeThread = false;
4553 12 : m_bThreadRTreeStarted = false;
4554 :
4555 12 : if (m_hAsyncDBHandle)
4556 : {
4557 8 : sqlite3_close(m_hAsyncDBHandle);
4558 8 : m_hAsyncDBHandle = nullptr;
4559 : }
4560 12 : if (m_bErrorDuringRTreeThread)
4561 : {
4562 4 : RemoveAsyncRTreeTempDB();
4563 : }
4564 : else
4565 : {
4566 8 : bPopulateFromThreadRTree = true;
4567 : }
4568 : }
4569 :
4570 614 : m_poDS->SoftStartTransaction();
4571 :
4572 614 : if (m_hRTree)
4573 : {
4574 4 : if (!FlushInMemoryRTree(m_poDS->GetDB(), m_osRTreeName.c_str()))
4575 : {
4576 0 : m_poDS->SoftRollbackTransaction();
4577 0 : return false;
4578 : }
4579 : }
4580 610 : else if (bPopulateFromThreadRTree)
4581 : {
4582 : /* Create virtual table */
4583 4 : char *pszSQL = sqlite3_mprintf("CREATE VIRTUAL TABLE \"%w\" USING "
4584 : "rtree(id, minx, maxx, miny, maxy)",
4585 : m_osRTreeName.c_str());
4586 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4587 4 : sqlite3_free(pszSQL);
4588 4 : if (err != OGRERR_NONE)
4589 : {
4590 0 : m_poDS->SoftRollbackTransaction();
4591 0 : return false;
4592 : }
4593 :
4594 4 : pszSQL = sqlite3_mprintf(
4595 : "DELETE FROM \"%w_node\";\n"
4596 : "INSERT INTO \"%w_node\" SELECT * FROM \"%w\".my_rtree_node;\n"
4597 : "INSERT INTO \"%w_rowid\" SELECT * FROM "
4598 : "\"%w\".my_rtree_rowid;\n"
4599 : "INSERT INTO \"%w_parent\" SELECT * FROM "
4600 : "\"%w\".my_rtree_parent;\n",
4601 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
4602 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4603 : m_osAsyncDBAttachName.c_str(), m_osRTreeName.c_str(),
4604 : m_osAsyncDBAttachName.c_str());
4605 4 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
4606 4 : sqlite3_free(pszSQL);
4607 4 : if (err != OGRERR_NONE)
4608 : {
4609 0 : m_poDS->SoftRollbackTransaction();
4610 0 : RemoveAsyncRTreeTempDB();
4611 0 : return false;
4612 : }
4613 : }
4614 : else
4615 : {
4616 : /* Populate the RTree */
4617 606 : const size_t nMaxRAMUsageAllowed = GetMaxRAMUsageAllowedForRTree();
4618 606 : char *pszErrMsg = nullptr;
4619 :
4620 : struct ProgressCbk
4621 : {
4622 418 : static bool progressCbk(const char *pszMessage, void *)
4623 : {
4624 418 : CPLDebug("GPKG", "%s", pszMessage);
4625 418 : return true;
4626 : }
4627 : };
4628 :
4629 1212 : if (!gdal_sqlite_rtree_bl_from_feature_table(
4630 606 : m_poDS->GetDB(), pszT, pszI, pszC, m_osRTreeName.c_str(), "id",
4631 : "minx", "miny", "maxx", "maxy", nMaxRAMUsageAllowed, &pszErrMsg,
4632 : ProgressCbk::progressCbk, nullptr))
4633 : {
4634 0 : CPLError(CE_Failure, CPLE_AppDefined,
4635 : "gdal_sqlite_rtree_bl_from_feature_table() failed "
4636 : "with %s",
4637 0 : pszErrMsg ? pszErrMsg : "(null)");
4638 0 : m_poDS->SoftRollbackTransaction();
4639 0 : sqlite3_free(pszErrMsg);
4640 0 : return false;
4641 : }
4642 : }
4643 :
4644 1228 : CPLString osSQL;
4645 :
4646 : /* Register the table in gpkg_extensions */
4647 614 : char *pszSQL = sqlite3_mprintf(
4648 : "INSERT INTO gpkg_extensions "
4649 : "(table_name,column_name,extension_name,definition,scope) "
4650 : "VALUES ('%q', '%q', 'gpkg_rtree_index', "
4651 : "'http://www.geopackage.org/spec120/#extension_rtree', 'write-only')",
4652 : pszT, pszC);
4653 614 : osSQL += pszSQL;
4654 614 : sqlite3_free(pszSQL);
4655 :
4656 : /* Define Triggers to Maintain Spatial Index Values */
4657 614 : osSQL += ";" + ReturnSQLCreateSpatialIndexTriggers(pszTableName, nullptr);
4658 :
4659 614 : err = SQLCommand(m_poDS->GetDB(), osSQL);
4660 614 : if (err != OGRERR_NONE)
4661 : {
4662 0 : m_poDS->SoftRollbackTransaction();
4663 0 : if (bPopulateFromThreadRTree)
4664 : {
4665 0 : RemoveAsyncRTreeTempDB();
4666 : }
4667 0 : return false;
4668 : }
4669 :
4670 614 : m_poDS->SoftCommitTransaction();
4671 :
4672 614 : if (bPopulateFromThreadRTree)
4673 : {
4674 8 : RemoveAsyncRTreeTempDB();
4675 : }
4676 :
4677 614 : m_bHasSpatialIndex = true;
4678 :
4679 614 : return true;
4680 : }
4681 :
4682 : /************************************************************************/
4683 : /* WorkaroundUpdate1TriggerIssue() */
4684 : /************************************************************************/
4685 :
4686 17 : void OGRGeoPackageTableLayer::WorkaroundUpdate1TriggerIssue()
4687 : {
4688 : // Workaround issue of https://sqlite.org/forum/forumpost/8c8de6ff91
4689 : // Basically the official _update1 spatial index trigger doesn't work
4690 : // with current versions of SQLite when invoked from an UPSERT statement.
4691 : // In GeoPackage 1.4, the update6 and update7 triggers replace update1
4692 :
4693 17 : if (m_bHasUpdate6And7Triggers || m_poFeatureDefn->GetGeomFieldCount() == 0)
4694 8 : return;
4695 :
4696 11 : const char *pszT = m_pszTableName;
4697 11 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4698 11 : const char *pszI = GetFIDColumn();
4699 :
4700 11 : CPLString osRTreeName = "rtree_";
4701 11 : osRTreeName += pszT;
4702 11 : osRTreeName += "_";
4703 11 : osRTreeName += pszC;
4704 :
4705 : // Check if update6 and update7 triggers are there
4706 : {
4707 22 : char *pszSQL = sqlite3_mprintf(
4708 : "SELECT * FROM sqlite_master WHERE type = 'trigger' "
4709 : "AND name IN ('%q', '%q')",
4710 22 : (m_osRTreeName + "_update6").c_str(),
4711 22 : (m_osRTreeName + "_update7").c_str());
4712 11 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4713 11 : sqlite3_free(pszSQL);
4714 11 : if (oResult && oResult->RowCount() == 2)
4715 : {
4716 2 : m_bHasUpdate6And7Triggers = true;
4717 2 : return;
4718 : }
4719 : }
4720 :
4721 : char *pszSQL =
4722 9 : sqlite3_mprintf("SELECT sql FROM sqlite_master WHERE type = 'trigger' "
4723 : "AND name = '%q'",
4724 18 : (m_osRTreeName + "_update1").c_str());
4725 9 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
4726 9 : sqlite3_free(pszSQL);
4727 9 : if (oResult && oResult->RowCount() == 1)
4728 : {
4729 9 : const char *pszTriggerSQL = oResult->GetValue(0, 0);
4730 9 : if (pszTriggerSQL)
4731 : {
4732 9 : m_osUpdate1Trigger = pszTriggerSQL;
4733 : }
4734 : }
4735 9 : if (m_osUpdate1Trigger.empty())
4736 0 : return;
4737 :
4738 9 : m_bUpdate1TriggerDisabled = true;
4739 :
4740 : pszSQL =
4741 9 : sqlite3_mprintf("DROP TRIGGER \"%w_update1\"", osRTreeName.c_str());
4742 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
4743 9 : sqlite3_free(pszSQL);
4744 :
4745 9 : pszSQL = sqlite3_mprintf(
4746 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4747 : "ON \"%w\" "
4748 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4749 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4750 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4751 : "BEGIN "
4752 : "UPDATE \"%w\" SET "
4753 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4754 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4755 : "WHERE id = NEW.\"%w\";"
4756 : "END",
4757 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4758 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4759 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
4760 9 : sqlite3_free(pszSQL);
4761 :
4762 9 : pszSQL = sqlite3_mprintf(
4763 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4764 : "\"%w\" "
4765 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4766 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4767 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4768 : "BEGIN "
4769 : "INSERT INTO \"%w\" VALUES ("
4770 : "NEW.\"%w\","
4771 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4772 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4773 : "); "
4774 : "END",
4775 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4776 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4777 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
4778 9 : sqlite3_free(pszSQL);
4779 : }
4780 :
4781 : /************************************************************************/
4782 : /* RevertWorkaroundUpdate1TriggerIssue() */
4783 : /************************************************************************/
4784 :
4785 4206 : void OGRGeoPackageTableLayer::RevertWorkaroundUpdate1TriggerIssue()
4786 : {
4787 4206 : if (!m_bUpdate1TriggerDisabled)
4788 4197 : return;
4789 9 : m_bUpdate1TriggerDisabled = false;
4790 9 : CPLAssert(!m_bHasUpdate6And7Triggers);
4791 :
4792 9 : const char *pszT = m_pszTableName;
4793 9 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4794 :
4795 18 : CPLString osRTreeName = "rtree_";
4796 9 : osRTreeName += pszT;
4797 9 : osRTreeName += "_";
4798 9 : osRTreeName += pszC;
4799 :
4800 : char *pszSQL;
4801 :
4802 9 : SQLCommand(m_poDS->GetDB(), m_osUpdate1Trigger.c_str());
4803 9 : m_osUpdate1Trigger.clear();
4804 :
4805 : pszSQL =
4806 9 : sqlite3_mprintf("DROP TRIGGER \"%w_update6\"", osRTreeName.c_str());
4807 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
4808 9 : sqlite3_free(pszSQL);
4809 :
4810 : pszSQL =
4811 9 : sqlite3_mprintf("DROP TRIGGER \"%w_update7\"", osRTreeName.c_str());
4812 9 : SQLCommand(m_poDS->GetDB(), pszSQL);
4813 9 : sqlite3_free(pszSQL);
4814 : }
4815 :
4816 : /************************************************************************/
4817 : /* ReturnSQLCreateSpatialIndexTriggers() */
4818 : /************************************************************************/
4819 :
4820 626 : CPLString OGRGeoPackageTableLayer::ReturnSQLCreateSpatialIndexTriggers(
4821 : const char *pszTableName, const char *pszGeomColName)
4822 : {
4823 : char *pszSQL;
4824 626 : CPLString osSQL;
4825 :
4826 626 : const char *pszT = (pszTableName) ? pszTableName : m_pszTableName;
4827 : const char *pszC = (pszGeomColName)
4828 626 : ? pszGeomColName
4829 622 : : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
4830 626 : const char *pszI = GetFIDColumn();
4831 :
4832 1252 : CPLString osRTreeName = "rtree_";
4833 626 : osRTreeName += pszT;
4834 626 : osRTreeName += "_";
4835 626 : osRTreeName += pszC;
4836 :
4837 : /* Conditions: Insertion of non-empty geometry
4838 : Actions : Insert record into rtree */
4839 626 : pszSQL = sqlite3_mprintf(
4840 : "CREATE TRIGGER \"%w_insert\" AFTER INSERT ON \"%w\" "
4841 : "WHEN (new.\"%w\" NOT NULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4842 : "BEGIN "
4843 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4844 : "NEW.\"%w\","
4845 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4846 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4847 : "); "
4848 : "END",
4849 : osRTreeName.c_str(), pszT, pszC, pszC, osRTreeName.c_str(), pszI, pszC,
4850 : pszC, pszC, pszC);
4851 626 : osSQL += pszSQL;
4852 626 : sqlite3_free(pszSQL);
4853 :
4854 626 : if (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
4855 625 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
4856 : {
4857 : /* Conditions: Update a non-empty geometry with another non-empty geometry
4858 : Actions : Replace record from R-tree
4859 : */
4860 15 : pszSQL = sqlite3_mprintf(
4861 : "CREATE TRIGGER \"%w_update6\" AFTER UPDATE OF \"%w\" "
4862 : "ON \"%w\" "
4863 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4864 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4865 : "(OLD.\"%w\" NOTNULL AND NOT ST_IsEmpty(OLD.\"%w\")) "
4866 : "BEGIN "
4867 : "UPDATE \"%w\" SET "
4868 : "minx = ST_MinX(NEW.\"%w\"), maxx = ST_MaxX(NEW.\"%w\"),"
4869 : "miny = ST_MinY(NEW.\"%w\"), maxy = ST_MaxY(NEW.\"%w\") "
4870 : "WHERE id = NEW.\"%w\";"
4871 : "END",
4872 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4873 : osRTreeName.c_str(), pszC, pszC, pszC, pszC, pszI);
4874 15 : osSQL += ";";
4875 15 : osSQL += pszSQL;
4876 15 : sqlite3_free(pszSQL);
4877 :
4878 : /* Conditions: Update a null/empty geometry with a non-empty geometry
4879 : Actions : Insert record into R-tree
4880 : */
4881 15 : pszSQL = sqlite3_mprintf(
4882 : "CREATE TRIGGER \"%w_update7\" AFTER UPDATE OF \"%w\" ON "
4883 : "\"%w\" "
4884 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4885 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) AND "
4886 : "(OLD.\"%w\" ISNULL OR ST_IsEmpty(OLD.\"%w\")) "
4887 : "BEGIN "
4888 : "INSERT INTO \"%w\" VALUES ("
4889 : "NEW.\"%w\","
4890 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4891 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4892 : "); "
4893 : "END",
4894 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC, pszC, pszC,
4895 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4896 15 : osSQL += ";";
4897 15 : osSQL += pszSQL;
4898 15 : sqlite3_free(pszSQL);
4899 : }
4900 : else
4901 : {
4902 : /* Conditions: Update of geometry column to non-empty geometry
4903 : No row ID change
4904 : Actions : Update record in rtree */
4905 611 : pszSQL = sqlite3_mprintf(
4906 : "CREATE TRIGGER \"%w_update1\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4907 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4908 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4909 : "BEGIN "
4910 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4911 : "NEW.\"%w\","
4912 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4913 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4914 : "); "
4915 : "END",
4916 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4917 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4918 611 : osSQL += ";";
4919 611 : osSQL += pszSQL;
4920 611 : sqlite3_free(pszSQL);
4921 : }
4922 :
4923 : /* Conditions: Update of geometry column to empty geometry
4924 : No row ID change
4925 : Actions : Remove record from rtree */
4926 626 : pszSQL = sqlite3_mprintf(
4927 : "CREATE TRIGGER \"%w_update2\" AFTER UPDATE OF \"%w\" ON \"%w\" "
4928 : "WHEN OLD.\"%w\" = NEW.\"%w\" AND "
4929 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
4930 : "BEGIN "
4931 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4932 : "END",
4933 : osRTreeName.c_str(), pszC, pszT, pszI, pszI, pszC, pszC,
4934 : osRTreeName.c_str(), pszI);
4935 626 : osSQL += ";";
4936 626 : osSQL += pszSQL;
4937 626 : sqlite3_free(pszSQL);
4938 :
4939 : /* Conditions: Update of any column
4940 : Row ID change
4941 : Non-empty geometry
4942 : Actions : Remove record from rtree for old <i>
4943 : Insert record into rtree for new <i> */
4944 : pszSQL =
4945 1252 : sqlite3_mprintf("CREATE TRIGGER \"%w_%s\" AFTER UPDATE ON \"%w\" "
4946 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
4947 : "(NEW.\"%w\" NOTNULL AND NOT ST_IsEmpty(NEW.\"%w\")) "
4948 : "BEGIN "
4949 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4950 : "INSERT OR REPLACE INTO \"%w\" VALUES ("
4951 : "NEW.\"%w\","
4952 : "ST_MinX(NEW.\"%w\"), ST_MaxX(NEW.\"%w\"),"
4953 : "ST_MinY(NEW.\"%w\"), ST_MaxY(NEW.\"%w\")"
4954 : "); "
4955 : "END",
4956 : osRTreeName.c_str(),
4957 626 : (m_poDS->m_nApplicationId == GPKG_APPLICATION_ID &&
4958 625 : m_poDS->m_nUserVersion >= GPKG_1_4_VERSION)
4959 : ? "update5"
4960 : : "update3",
4961 : pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(), pszI,
4962 : osRTreeName.c_str(), pszI, pszC, pszC, pszC, pszC);
4963 626 : osSQL += ";";
4964 626 : osSQL += pszSQL;
4965 626 : sqlite3_free(pszSQL);
4966 :
4967 : /* Conditions: Update of any column
4968 : Row ID change
4969 : Empty geometry
4970 : Actions : Remove record from rtree for old and new <i> */
4971 626 : pszSQL = sqlite3_mprintf(
4972 : "CREATE TRIGGER \"%w_update4\" AFTER UPDATE ON \"%w\" "
4973 : "WHEN OLD.\"%w\" != NEW.\"%w\" AND "
4974 : "(NEW.\"%w\" ISNULL OR ST_IsEmpty(NEW.\"%w\")) "
4975 : "BEGIN "
4976 : "DELETE FROM \"%w\" WHERE id IN (OLD.\"%w\", NEW.\"%w\"); "
4977 : "END",
4978 : osRTreeName.c_str(), pszT, pszI, pszI, pszC, pszC, osRTreeName.c_str(),
4979 : pszI, pszI);
4980 626 : osSQL += ";";
4981 626 : osSQL += pszSQL;
4982 626 : sqlite3_free(pszSQL);
4983 :
4984 : /* Conditions: Row deleted
4985 : Actions : Remove record from rtree for old <i> */
4986 626 : pszSQL = sqlite3_mprintf(
4987 : "CREATE TRIGGER \"%w_delete\" AFTER DELETE ON \"%w\" "
4988 : "WHEN old.\"%w\" NOT NULL "
4989 : "BEGIN "
4990 : "DELETE FROM \"%w\" WHERE id = OLD.\"%w\"; "
4991 : "END",
4992 : osRTreeName.c_str(), pszT, pszC, osRTreeName.c_str(), pszI);
4993 626 : osSQL += ";";
4994 626 : osSQL += pszSQL;
4995 626 : sqlite3_free(pszSQL);
4996 :
4997 1252 : return osSQL;
4998 : }
4999 :
5000 : /************************************************************************/
5001 : /* CheckUnknownExtensions() */
5002 : /************************************************************************/
5003 :
5004 769 : void OGRGeoPackageTableLayer::CheckUnknownExtensions()
5005 : {
5006 : const std::map<CPLString, std::vector<GPKGExtensionDesc>> &oMap =
5007 769 : m_poDS->GetUnknownExtensionsTableSpecific();
5008 769 : const auto oIter = oMap.find(CPLString(m_pszTableName).toupper());
5009 769 : if (oIter != oMap.end())
5010 : {
5011 14 : for (size_t i = 0; i < oIter->second.size(); i++)
5012 : {
5013 7 : const char *pszExtName = oIter->second[i].osExtensionName.c_str();
5014 7 : const char *pszDefinition = oIter->second[i].osDefinition.c_str();
5015 7 : const char *pszScope = oIter->second[i].osScope.c_str();
5016 7 : if (m_poDS->GetUpdate() && EQUAL(pszScope, "write-only"))
5017 : {
5018 1 : CPLError(
5019 : CE_Warning, CPLE_AppDefined,
5020 : "Layer %s relies on the '%s' (%s) extension that should "
5021 : "be implemented for safe write-support, but is not "
5022 : "currently. "
5023 : "Update of that layer are strongly discouraged to avoid "
5024 : "corruption.",
5025 : GetName(), pszExtName, pszDefinition);
5026 : }
5027 6 : else if (m_poDS->GetUpdate() && EQUAL(pszScope, "read-write"))
5028 : {
5029 1 : CPLError(
5030 : CE_Warning, CPLE_AppDefined,
5031 : "Layer %s relies on the '%s' (%s) extension that should "
5032 : "be implemented in order to read/write it safely, but is "
5033 : "not currently. "
5034 : "Some data may be missing while reading that layer, and "
5035 : "updates are strongly discouraged.",
5036 : GetName(), pszExtName, pszDefinition);
5037 : }
5038 5 : else if (EQUAL(pszScope, "read-write") &&
5039 : // None of the NGA extensions at
5040 : // http://ngageoint.github.io/GeoPackage/docs/extensions/
5041 : // affect read-only scenarios
5042 3 : !STARTS_WITH(pszExtName, "nga_"))
5043 : {
5044 3 : CPLError(
5045 : CE_Warning, CPLE_AppDefined,
5046 : "Layer %s relies on the '%s' (%s) extension that should "
5047 : "be implemented in order to read it safely, but is not "
5048 : "currently. "
5049 : "Some data may be missing while reading that layer.",
5050 : GetName(), pszExtName, pszDefinition);
5051 : }
5052 : }
5053 : }
5054 769 : }
5055 :
5056 : /************************************************************************/
5057 : /* CreateGeometryExtensionIfNecessary() */
5058 : /************************************************************************/
5059 :
5060 251861 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5061 : const OGRGeometry *poGeom)
5062 : {
5063 251861 : bool bRet = true;
5064 251861 : if (poGeom != nullptr)
5065 : {
5066 251861 : OGRwkbGeometryType eGType = wkbFlatten(poGeom->getGeometryType());
5067 251861 : if (eGType >= wkbGeometryCollection)
5068 : {
5069 22 : if (eGType > wkbGeometryCollection)
5070 12 : CreateGeometryExtensionIfNecessary(eGType);
5071 : const OGRGeometryCollection *poGC =
5072 22 : dynamic_cast<const OGRGeometryCollection *>(poGeom);
5073 22 : if (poGC != nullptr)
5074 : {
5075 11 : const int nSubGeoms = poGC->getNumGeometries();
5076 32 : for (int i = 0; i < nSubGeoms; i++)
5077 : {
5078 21 : bRet &= CreateGeometryExtensionIfNecessary(
5079 21 : poGC->getGeometryRef(i));
5080 : }
5081 : }
5082 : }
5083 : }
5084 251861 : return bRet;
5085 : }
5086 :
5087 : /************************************************************************/
5088 : /* CreateGeometryExtensionIfNecessary() */
5089 : /************************************************************************/
5090 :
5091 19 : bool OGRGeoPackageTableLayer::CreateGeometryExtensionIfNecessary(
5092 : OGRwkbGeometryType eGType)
5093 : {
5094 19 : eGType = wkbFlatten(eGType);
5095 19 : CPLAssert(eGType > wkbGeometryCollection && eGType <= wkbTriangle);
5096 19 : if (m_abHasGeometryExtension[eGType])
5097 3 : return true;
5098 :
5099 16 : if (m_poDS->CreateExtensionsTableIfNecessary() != OGRERR_NONE)
5100 0 : return false;
5101 :
5102 16 : const char *pszT = m_pszTableName;
5103 16 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5104 16 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5105 :
5106 : // Check first if the extension isn't registered
5107 16 : char *pszSQL = sqlite3_mprintf(
5108 : "SELECT 1 FROM gpkg_extensions WHERE lower(table_name) = lower('%q') "
5109 : "AND "
5110 : "lower(column_name) = lower('%q') AND extension_name = 'gpkg_geom_%s'",
5111 : pszT, pszC, pszGeometryType);
5112 16 : const bool bExists = SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5113 16 : sqlite3_free(pszSQL);
5114 :
5115 16 : if (!bExists)
5116 : {
5117 15 : if (eGType == wkbPolyhedralSurface || eGType == wkbTIN ||
5118 : eGType == wkbTriangle)
5119 : {
5120 2 : CPLError(CE_Warning, CPLE_AppDefined,
5121 : "Registering non-standard gpkg_geom_%s extension",
5122 : pszGeometryType);
5123 : }
5124 :
5125 : /* Register the table in gpkg_extensions */
5126 15 : pszSQL = sqlite3_mprintf(
5127 : "INSERT INTO gpkg_extensions "
5128 : "(table_name,column_name,extension_name,definition,scope) "
5129 : "VALUES ('%q', '%q', 'gpkg_geom_%s', "
5130 : "'http://www.geopackage.org/spec120/#extension_geometry_types', "
5131 : "'read-write')",
5132 : pszT, pszC, pszGeometryType);
5133 15 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5134 15 : sqlite3_free(pszSQL);
5135 15 : if (err != OGRERR_NONE)
5136 0 : return false;
5137 : }
5138 :
5139 16 : m_abHasGeometryExtension[eGType] = true;
5140 16 : return true;
5141 : }
5142 :
5143 : /************************************************************************/
5144 : /* HasSpatialIndex() */
5145 : /************************************************************************/
5146 :
5147 47318 : bool OGRGeoPackageTableLayer::HasSpatialIndex()
5148 : {
5149 47318 : if (!m_bFeatureDefnCompleted)
5150 16 : GetLayerDefn();
5151 47318 : if (m_bHasSpatialIndex >= 0)
5152 46476 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5153 842 : m_bHasSpatialIndex = false;
5154 :
5155 2526 : if (m_pszFidColumn == nullptr ||
5156 1674 : m_poFeatureDefn->GetGeomFieldCount() == 0 ||
5157 832 : !m_poDS->HasExtensionsTable())
5158 487 : return false;
5159 :
5160 355 : const char *pszT = m_pszTableName;
5161 355 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5162 : const CPLString osRTreeName(
5163 710 : CPLString("rtree_").append(pszT).append("_").append(pszC));
5164 : const std::map<CPLString, CPLString> &oMap =
5165 355 : m_poDS->GetNameTypeMapFromSQliteMaster();
5166 355 : if (cpl::contains(oMap, CPLString(osRTreeName).toupper()))
5167 : {
5168 199 : m_bHasSpatialIndex = true;
5169 199 : m_osRTreeName = osRTreeName;
5170 199 : m_osFIDForRTree = m_pszFidColumn;
5171 : }
5172 :
5173 : // Add heuristics to try to detect corrupted RTree generated by GDAL 3.6.0
5174 : // Cf https://github.com/OSGeo/gdal/pull/6911
5175 355 : if (m_bHasSpatialIndex)
5176 : {
5177 199 : const auto nFC = GetTotalFeatureCount();
5178 199 : if (nFC >= atoi(CPLGetConfigOption(
5179 : "OGR_GPKG_THRESHOLD_DETECT_BROKEN_RTREE", "100000")))
5180 : {
5181 2 : CPLString osSQL = "SELECT 1 FROM \"";
5182 1 : osSQL += SQLEscapeName(pszT);
5183 1 : osSQL += "\" WHERE \"";
5184 1 : osSQL += SQLEscapeName(GetFIDColumn());
5185 1 : osSQL += "\" = ";
5186 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5187 1 : osSQL += " AND \"";
5188 1 : osSQL += SQLEscapeName(pszC);
5189 1 : osSQL += "\" IS NOT NULL AND NOT ST_IsEmpty(\"";
5190 1 : osSQL += SQLEscapeName(pszC);
5191 1 : osSQL += "\")";
5192 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 1)
5193 : {
5194 1 : osSQL = "SELECT 1 FROM \"";
5195 1 : osSQL += SQLEscapeName(m_osRTreeName);
5196 1 : osSQL += "\" WHERE id = ";
5197 1 : osSQL += CPLSPrintf(CPL_FRMT_GIB, nFC);
5198 1 : if (SQLGetInteger(m_poDS->GetDB(), osSQL, nullptr) == 0)
5199 : {
5200 1 : CPLError(CE_Warning, CPLE_AppDefined,
5201 : "Spatial index (perhaps created with GDAL 3.6.0) "
5202 : "of table %s is corrupted. Disabling its use. "
5203 : "This file should be recreated or its spatial "
5204 : "index recreated",
5205 : m_pszTableName);
5206 1 : m_bHasSpatialIndex = false;
5207 : }
5208 : }
5209 : }
5210 : }
5211 :
5212 355 : return CPL_TO_BOOL(m_bHasSpatialIndex);
5213 : }
5214 :
5215 : /************************************************************************/
5216 : /* DropSpatialIndex() */
5217 : /************************************************************************/
5218 :
5219 38 : bool OGRGeoPackageTableLayer::DropSpatialIndex(bool bCalledFromSQLFunction)
5220 : {
5221 38 : if (!m_bFeatureDefnCompleted)
5222 0 : GetLayerDefn();
5223 38 : if (!CheckUpdatableTable("DropSpatialIndex"))
5224 1 : return false;
5225 :
5226 37 : if (m_bDropRTreeTable)
5227 : {
5228 0 : CPLError(CE_Failure, CPLE_AppDefined,
5229 : "Cannot run DropSpatialIndex() after non-completed deferred "
5230 : "DropSpatialIndex()");
5231 0 : return false;
5232 : }
5233 :
5234 37 : if (!HasSpatialIndex())
5235 : {
5236 1 : CPLError(CE_Failure, CPLE_AppDefined, "Spatial index not existing");
5237 1 : return false;
5238 : }
5239 :
5240 36 : const char *pszT = m_pszTableName;
5241 36 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5242 : {
5243 36 : char *pszSQL = sqlite3_mprintf(
5244 : "DELETE FROM gpkg_extensions WHERE lower(table_name)=lower('%q') "
5245 : "AND lower(column_name)=lower('%q') AND "
5246 : "extension_name='gpkg_rtree_index'",
5247 : pszT, pszC);
5248 36 : SQLCommand(m_poDS->GetDB(), pszSQL);
5249 36 : sqlite3_free(pszSQL);
5250 : }
5251 :
5252 36 : if (bCalledFromSQLFunction)
5253 : {
5254 : /* We cannot drop a table from a SQLite function call, so we just */
5255 : /* memorize that we will have to delete the table later */
5256 6 : m_bDropRTreeTable = true;
5257 : }
5258 : else
5259 : {
5260 : char *pszSQL =
5261 30 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5262 30 : SQLCommand(m_poDS->GetDB(), pszSQL);
5263 30 : sqlite3_free(pszSQL);
5264 : }
5265 :
5266 36 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5267 :
5268 36 : SQLCommand(m_poDS->GetDB(), ReturnSQLDropSpatialIndexTriggers().c_str());
5269 :
5270 36 : m_bHasSpatialIndex = false;
5271 36 : return true;
5272 : }
5273 :
5274 : /************************************************************************/
5275 : /* RunDeferredDropRTreeTableIfNecessary() */
5276 : /************************************************************************/
5277 :
5278 1563 : bool OGRGeoPackageTableLayer::RunDeferredDropRTreeTableIfNecessary()
5279 : {
5280 1563 : bool ret = true;
5281 1563 : if (m_bDropRTreeTable)
5282 : {
5283 6 : OGRGeoPackageTableLayer::ResetReading();
5284 :
5285 : char *pszSQL =
5286 6 : sqlite3_mprintf("DROP TABLE \"%w\"", m_osRTreeName.c_str());
5287 6 : ret = SQLCommand(m_poDS->GetDB(), pszSQL) == OGRERR_NONE;
5288 6 : sqlite3_free(pszSQL);
5289 6 : m_bDropRTreeTable = false;
5290 : }
5291 1563 : return ret;
5292 : }
5293 :
5294 : /************************************************************************/
5295 : /* ReturnSQLDropSpatialIndexTriggers() */
5296 : /************************************************************************/
5297 :
5298 55 : CPLString OGRGeoPackageTableLayer::ReturnSQLDropSpatialIndexTriggers()
5299 : {
5300 55 : char *pszSQL = sqlite3_mprintf(
5301 : "DROP TRIGGER \"%w_insert\";"
5302 : "DROP TRIGGER IF EXISTS \"%w_update1\";" // replaced by update6 and update7 in GPKG 1.4
5303 : "DROP TRIGGER \"%w_update2\";"
5304 : "DROP TRIGGER IF EXISTS \"%w_update3\";" // replace by update5 in GPKG 1.4
5305 : "DROP TRIGGER \"%w_update4\";"
5306 : "DROP TRIGGER IF EXISTS \"%w_update5\";" // replace update3 in GPKG 1.4
5307 : "DROP TRIGGER IF EXISTS \"%w_update6\";" // replace update1 in GPKG 1.4
5308 : "DROP TRIGGER IF EXISTS \"%w_update7\";" // replace update1 in GPKG 1.4
5309 : "DROP TRIGGER \"%w_delete\";",
5310 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5311 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str(),
5312 : m_osRTreeName.c_str(), m_osRTreeName.c_str(), m_osRTreeName.c_str());
5313 55 : CPLString osSQL(pszSQL);
5314 55 : sqlite3_free(pszSQL);
5315 :
5316 55 : return osSQL;
5317 : }
5318 :
5319 : /************************************************************************/
5320 : /* Rename() */
5321 : /************************************************************************/
5322 :
5323 12 : OGRErr OGRGeoPackageTableLayer::Rename(const char *pszDstTableName)
5324 : {
5325 12 : if (!m_bFeatureDefnCompleted)
5326 3 : GetLayerDefn();
5327 12 : if (!CheckUpdatableTable("Rename"))
5328 0 : return OGRERR_FAILURE;
5329 :
5330 12 : ResetReading();
5331 12 : SyncToDisk();
5332 :
5333 12 : char *pszSQL = sqlite3_mprintf(
5334 : "SELECT 1 FROM sqlite_master WHERE lower(name) = lower('%q') "
5335 : "AND type IN ('table', 'view')",
5336 : pszDstTableName);
5337 : const bool bAlreadyExists =
5338 12 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr) == 1;
5339 12 : sqlite3_free(pszSQL);
5340 12 : if (bAlreadyExists)
5341 : {
5342 3 : CPLError(CE_Failure, CPLE_AppDefined, "Table %s already exists",
5343 : pszDstTableName);
5344 3 : return OGRERR_FAILURE;
5345 : }
5346 :
5347 : // Temporary remove foreign key checks
5348 : const GPKGTemporaryForeignKeyCheckDisabler
5349 18 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
5350 :
5351 9 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
5352 : {
5353 0 : return OGRERR_FAILURE;
5354 : }
5355 :
5356 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5357 9 : DisableFeatureCountTriggers(false);
5358 : #endif
5359 :
5360 18 : CPLString osSQL;
5361 :
5362 9 : pszSQL = sqlite3_mprintf(
5363 : "UPDATE gpkg_geometry_columns SET table_name = '%q' WHERE "
5364 : "lower(table_name )= lower('%q');",
5365 : pszDstTableName, m_pszTableName);
5366 9 : osSQL += pszSQL;
5367 9 : sqlite3_free(pszSQL);
5368 :
5369 : // Rename the identifier if it defaulted to the table name
5370 9 : pszSQL = sqlite3_mprintf(
5371 : "UPDATE gpkg_contents SET identifier = '%q' WHERE "
5372 : "lower(table_name) = lower('%q') AND identifier = '%q';",
5373 : pszDstTableName, m_pszTableName, m_pszTableName);
5374 9 : osSQL += pszSQL;
5375 9 : sqlite3_free(pszSQL);
5376 :
5377 9 : pszSQL = sqlite3_mprintf("UPDATE gpkg_contents SET table_name = '%q' WHERE "
5378 : "lower(table_name )= lower('%q');",
5379 : pszDstTableName, m_pszTableName);
5380 9 : osSQL += pszSQL;
5381 9 : sqlite3_free(pszSQL);
5382 :
5383 9 : if (m_poDS->HasExtensionsTable())
5384 : {
5385 8 : pszSQL = sqlite3_mprintf(
5386 : "UPDATE gpkg_extensions SET table_name = '%q' WHERE "
5387 : "lower(table_name )= lower('%q');",
5388 : pszDstTableName, m_pszTableName);
5389 8 : osSQL += pszSQL;
5390 8 : sqlite3_free(pszSQL);
5391 : }
5392 :
5393 9 : if (m_poDS->HasMetadataTables())
5394 : {
5395 4 : pszSQL = sqlite3_mprintf(
5396 : "UPDATE gpkg_metadata_reference SET table_name = '%q' WHERE "
5397 : "lower(table_name )= lower('%q');",
5398 : pszDstTableName, m_pszTableName);
5399 4 : osSQL += pszSQL;
5400 4 : sqlite3_free(pszSQL);
5401 : }
5402 :
5403 9 : if (m_poDS->HasDataColumnsTable())
5404 : {
5405 1 : pszSQL = sqlite3_mprintf(
5406 : "UPDATE gpkg_data_columns SET table_name = '%q' WHERE "
5407 : "lower(table_name )= lower('%q');",
5408 : pszDstTableName, m_pszTableName);
5409 1 : osSQL += pszSQL;
5410 1 : sqlite3_free(pszSQL);
5411 : }
5412 :
5413 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5414 9 : if (m_poDS->m_bHasGPKGOGRContents)
5415 : {
5416 9 : pszSQL = sqlite3_mprintf(
5417 : "UPDATE gpkg_ogr_contents SET table_name = '%q' WHERE "
5418 : "lower(table_name )= lower('%q');",
5419 : pszDstTableName, m_pszTableName);
5420 9 : osSQL += pszSQL;
5421 9 : sqlite3_free(pszSQL);
5422 : }
5423 : #endif
5424 :
5425 9 : if (m_poDS->HasGpkgextRelationsTable())
5426 : {
5427 2 : pszSQL = sqlite3_mprintf(
5428 : "UPDATE gpkgext_relations SET base_table_name = '%q' WHERE "
5429 : "lower(base_table_name )= lower('%q');",
5430 : pszDstTableName, m_pszTableName);
5431 2 : osSQL += pszSQL;
5432 2 : sqlite3_free(pszSQL);
5433 :
5434 2 : pszSQL = sqlite3_mprintf(
5435 : "UPDATE gpkgext_relations SET related_table_name = '%q' WHERE "
5436 : "lower(related_table_name )= lower('%q');",
5437 : pszDstTableName, m_pszTableName);
5438 2 : osSQL += pszSQL;
5439 : ;
5440 2 : sqlite3_free(pszSQL);
5441 :
5442 2 : pszSQL = sqlite3_mprintf(
5443 : "UPDATE gpkgext_relations SET mapping_table_name = '%q' WHERE "
5444 : "lower(mapping_table_name )= lower('%q');",
5445 : pszDstTableName, m_pszTableName);
5446 2 : osSQL += pszSQL;
5447 2 : sqlite3_free(pszSQL);
5448 : }
5449 :
5450 9 : if (m_poDS->HasQGISLayerStyles())
5451 : {
5452 : pszSQL =
5453 1 : sqlite3_mprintf("UPDATE layer_styles SET f_table_name = '%q' WHERE "
5454 : "f_table_name = '%q';",
5455 : pszDstTableName, m_pszTableName);
5456 1 : osSQL += pszSQL;
5457 1 : sqlite3_free(pszSQL);
5458 : }
5459 :
5460 9 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5461 : m_pszTableName, pszDstTableName);
5462 9 : osSQL += pszSQL;
5463 9 : sqlite3_free(pszSQL);
5464 :
5465 9 : const bool bHasSpatialIndex = HasSpatialIndex();
5466 9 : CPLString osRTreeNameNew;
5467 9 : if (bHasSpatialIndex)
5468 : {
5469 8 : osRTreeNameNew = "rtree_";
5470 8 : osRTreeNameNew += pszDstTableName;
5471 8 : osRTreeNameNew += "_";
5472 8 : osRTreeNameNew += m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
5473 :
5474 8 : osSQL += ReturnSQLDropSpatialIndexTriggers();
5475 8 : osSQL += ';';
5476 :
5477 8 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w\" RENAME TO \"%w\";",
5478 : m_osRTreeName.c_str(), osRTreeNameNew.c_str());
5479 8 : osSQL += pszSQL;
5480 8 : sqlite3_free(pszSQL);
5481 :
5482 8 : osSQL += ReturnSQLCreateSpatialIndexTriggers(pszDstTableName, nullptr);
5483 : }
5484 :
5485 9 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL);
5486 :
5487 : // Check foreign key integrity
5488 9 : if (eErr == OGRERR_NONE)
5489 : {
5490 9 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
5491 : }
5492 :
5493 9 : if (eErr == OGRERR_NONE)
5494 : {
5495 : #ifdef ENABLE_GPKG_OGR_CONTENTS
5496 9 : CreateFeatureCountTriggers(pszDstTableName);
5497 : #endif
5498 :
5499 9 : eErr = m_poDS->SoftCommitTransaction();
5500 9 : if (eErr == OGRERR_NONE)
5501 : {
5502 9 : m_poDS->RemoveTableFromSQLiteMasterCache(m_pszTableName);
5503 :
5504 9 : CPLFree(m_pszTableName);
5505 9 : m_pszTableName = CPLStrdup(pszDstTableName);
5506 :
5507 9 : if (bHasSpatialIndex)
5508 : {
5509 8 : m_poDS->RemoveTableFromSQLiteMasterCache(m_osRTreeName);
5510 8 : m_osRTreeName = std::move(osRTreeNameNew);
5511 : }
5512 : }
5513 : }
5514 : else
5515 : {
5516 0 : m_poDS->SoftRollbackTransaction();
5517 : }
5518 :
5519 9 : if (eErr == OGRERR_NONE)
5520 : {
5521 9 : m_poDS->ClearCachedRelationships();
5522 :
5523 9 : SetDescription(pszDstTableName);
5524 9 : whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
5525 : }
5526 :
5527 9 : return eErr;
5528 : }
5529 :
5530 : /************************************************************************/
5531 : /* SetSpatialFilter() */
5532 : /************************************************************************/
5533 :
5534 23090 : void OGRGeoPackageTableLayer::SetSpatialFilter(OGRGeometry *poGeomIn)
5535 :
5536 : {
5537 23090 : if (!m_bFeatureDefnCompleted)
5538 52 : GetLayerDefn();
5539 23090 : if (InstallFilter(poGeomIn))
5540 : {
5541 23062 : BuildWhere();
5542 :
5543 23062 : ResetReading();
5544 : }
5545 23090 : }
5546 :
5547 : /************************************************************************/
5548 : /* HasFastSpatialFilter() */
5549 : /************************************************************************/
5550 :
5551 2 : bool OGRGeoPackageTableLayer::HasFastSpatialFilter(int m_iGeomColIn)
5552 : {
5553 4 : if (m_iGeomColIn < 0 ||
5554 2 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5555 0 : return false;
5556 2 : return HasSpatialIndex();
5557 : }
5558 :
5559 : /************************************************************************/
5560 : /* GetSpatialWhere() */
5561 : /************************************************************************/
5562 :
5563 23165 : CPLString OGRGeoPackageTableLayer::GetSpatialWhere(int m_iGeomColIn,
5564 : OGRGeometry *poFilterGeom)
5565 : {
5566 23165 : CPLString osSpatialWHERE;
5567 :
5568 46330 : if (m_iGeomColIn < 0 ||
5569 23165 : m_iGeomColIn >= m_poFeatureDefn->GetGeomFieldCount())
5570 2 : return osSpatialWHERE;
5571 :
5572 23163 : if (poFilterGeom != nullptr)
5573 : {
5574 23079 : OGREnvelope sEnvelope;
5575 :
5576 23079 : poFilterGeom->getEnvelope(&sEnvelope);
5577 :
5578 : const char *pszC =
5579 23079 : m_poFeatureDefn->GetGeomFieldDefn(m_iGeomColIn)->GetNameRef();
5580 :
5581 23087 : if (std::isinf(sEnvelope.MinX) && sEnvelope.MinX < 0 &&
5582 12 : std::isinf(sEnvelope.MinY) && sEnvelope.MinY < 0 &&
5583 12 : std::isinf(sEnvelope.MaxX) && sEnvelope.MaxX > 0 &&
5584 23087 : std::isinf(sEnvelope.MaxY) && sEnvelope.MaxY > 0)
5585 : {
5586 : osSpatialWHERE.Printf(
5587 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5588 4 : SQLEscapeName(pszC).c_str(), SQLEscapeName(pszC).c_str());
5589 105 : return osSpatialWHERE;
5590 : }
5591 :
5592 23075 : bool bUseSpatialIndex = true;
5593 23075 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
5594 173 : sEnvelope.MinY <= m_poExtent->MinY &&
5595 156 : sEnvelope.MaxX >= m_poExtent->MaxX &&
5596 108 : sEnvelope.MaxY >= m_poExtent->MaxY)
5597 : {
5598 : // Selecting from spatial filter on whole extent can be rather
5599 : // slow. So use function based filtering, just in case the
5600 : // advertized global extent might be wrong. Otherwise we might
5601 : // just discard completely the spatial filter.
5602 105 : bUseSpatialIndex = false;
5603 : }
5604 :
5605 23075 : if (bUseSpatialIndex && HasSpatialIndex())
5606 : {
5607 : osSpatialWHERE.Printf(
5608 : "\"%s\" IN ( SELECT id FROM \"%s\" WHERE "
5609 : "maxx >= %.12f AND minx <= %.12f AND "
5610 : "maxy >= %.12f AND miny <= %.12f)",
5611 45934 : SQLEscapeName(m_osFIDForRTree).c_str(),
5612 45934 : SQLEscapeName(m_osRTreeName).c_str(), sEnvelope.MinX - 1e-11,
5613 22967 : sEnvelope.MaxX + 1e-11, sEnvelope.MinY - 1e-11,
5614 68901 : sEnvelope.MaxY + 1e-11);
5615 : }
5616 : else
5617 : {
5618 108 : if (HasSpatialIndex())
5619 : {
5620 : // If we do have a spatial index, and our filter contains the
5621 : // bounding box of the RTree, then just filter on non-null
5622 : // non-empty geometries.
5623 : double minx, miny, maxx, maxy;
5624 102 : if (GetExtentFromRTree(m_poDS->GetDB(), m_osRTreeName, minx,
5625 102 : miny, maxx, maxy) &&
5626 102 : sEnvelope.MinX <= minx && sEnvelope.MinY <= miny &&
5627 204 : sEnvelope.MaxX >= maxx && sEnvelope.MaxY >= maxy)
5628 : {
5629 : osSpatialWHERE.Printf(
5630 : "(\"%s\" IS NOT NULL AND NOT ST_IsEmpty(\"%s\"))",
5631 202 : SQLEscapeName(pszC).c_str(),
5632 303 : SQLEscapeName(pszC).c_str());
5633 101 : return osSpatialWHERE;
5634 : }
5635 : }
5636 :
5637 : /* A bit inefficient but still faster than OGR filtering */
5638 : osSpatialWHERE.Printf(
5639 : "ST_EnvelopesIntersects(\"%s\", %.12f, %.12f, %.12f, %.12f)",
5640 7 : SQLEscapeName(pszC).c_str(), sEnvelope.MinX - 1e-11,
5641 7 : sEnvelope.MinY - 1e-11, sEnvelope.MaxX + 1e-11,
5642 7 : sEnvelope.MaxY + 1e-11);
5643 : }
5644 : }
5645 :
5646 23058 : return osSpatialWHERE;
5647 : }
5648 :
5649 : /************************************************************************/
5650 : /* BuildWhere() */
5651 : /* */
5652 : /* Build the WHERE statement appropriate to the current set of */
5653 : /* criteria (spatial and attribute queries). */
5654 : /************************************************************************/
5655 :
5656 23145 : void OGRGeoPackageTableLayer::BuildWhere()
5657 :
5658 : {
5659 23145 : m_soFilter = "";
5660 :
5661 : CPLString osSpatialWHERE =
5662 46290 : GetSpatialWhere(m_iGeomFieldFilter, m_poFilterGeom);
5663 23145 : if (!osSpatialWHERE.empty())
5664 : {
5665 23059 : m_soFilter += osSpatialWHERE;
5666 : }
5667 :
5668 23145 : if (!osQuery.empty())
5669 : {
5670 45 : if (m_soFilter.empty())
5671 : {
5672 39 : m_soFilter += osQuery;
5673 : }
5674 : else
5675 : {
5676 6 : m_soFilter += " AND (";
5677 6 : m_soFilter += osQuery;
5678 6 : m_soFilter += ")";
5679 : }
5680 : }
5681 23145 : CPLDebug("GPKG", "Filter: %s", m_soFilter.c_str());
5682 23145 : }
5683 :
5684 : /************************************************************************/
5685 : /* SetOpeningParameters() */
5686 : /************************************************************************/
5687 :
5688 2995 : void OGRGeoPackageTableLayer::SetOpeningParameters(
5689 : const char *pszTableName, const char *pszObjectType, bool bIsInGpkgContents,
5690 : bool bIsSpatial, const char *pszGeomColName, const char *pszGeomType,
5691 : bool bHasZ, bool bHasM)
5692 : {
5693 2995 : CPLFree(m_pszTableName);
5694 2995 : m_pszTableName = CPLStrdup(pszTableName);
5695 2995 : m_bIsTable = EQUAL(pszObjectType, "table");
5696 2995 : m_bIsInGpkgContents = bIsInGpkgContents;
5697 2995 : m_bIsSpatial = bIsSpatial;
5698 2995 : if (pszGeomType)
5699 : {
5700 : OGRwkbGeometryType eType =
5701 889 : GPkgGeometryTypeToWKB(pszGeomType, bHasZ, bHasM);
5702 889 : m_poFeatureDefn->SetGeomType(eType);
5703 889 : if (eType != wkbNone)
5704 : {
5705 889 : m_poFeatureDefn->GetGeomFieldDefn(0)->SetName(pszGeomColName);
5706 : }
5707 : }
5708 2995 : }
5709 :
5710 : /************************************************************************/
5711 : /* SetCreationParameters() */
5712 : /************************************************************************/
5713 :
5714 681 : void OGRGeoPackageTableLayer::SetCreationParameters(
5715 : OGRwkbGeometryType eGType, const char *pszGeomColumnName, int bGeomNullable,
5716 : const OGRSpatialReference *poSRS, const char *pszSRID,
5717 : const OGRGeomCoordinatePrecision &oCoordPrec, bool bDiscardCoordLSB,
5718 : bool bUndoDiscardCoordLSBOnReading, const char *pszFIDColumnName,
5719 : const char *pszIdentifier, const char *pszDescription)
5720 : {
5721 681 : m_bIsSpatial = eGType != wkbNone;
5722 681 : m_bIsInGpkgContents =
5723 733 : m_bIsSpatial ||
5724 52 : !m_poDS->HasNonSpatialTablesNonRegisteredInGpkgContents();
5725 681 : m_bFeatureDefnCompleted = true;
5726 681 : m_bDeferredCreation = true;
5727 681 : m_bTableCreatedInTransaction = m_poDS->IsInTransaction();
5728 681 : m_bHasTriedDetectingFID64 = true;
5729 681 : m_pszFidColumn = CPLStrdup(pszFIDColumnName);
5730 681 : m_bUndoDiscardCoordLSBOnReading = bUndoDiscardCoordLSBOnReading;
5731 :
5732 681 : if (eGType != wkbNone)
5733 : {
5734 629 : m_nZFlag = wkbHasZ(eGType) ? 1 : 0;
5735 629 : m_nMFlag = wkbHasM(eGType) ? 1 : 0;
5736 :
5737 : std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>
5738 629 : poGotSRS;
5739 1258 : OGRGeomFieldDefn oGeomFieldDefn(pszGeomColumnName, eGType);
5740 :
5741 629 : oGeomFieldDefn.SetSpatialRef(poSRS);
5742 629 : if (pszSRID)
5743 : {
5744 10 : m_iSrs = atoi(pszSRID);
5745 10 : if (m_iSrs == GDALGeoPackageDataset::FIRST_CUSTOM_SRSID - 1)
5746 : {
5747 1 : m_iSrs = m_poDS->GetSrsId(nullptr);
5748 1 : oGeomFieldDefn.SetSpatialRef(nullptr);
5749 : }
5750 : else
5751 : {
5752 : poGotSRS =
5753 18 : m_poDS->GetSpatialRef(m_iSrs, /* bFallbackToEPSG = */ false,
5754 9 : /* bEmitErrorIfNotFound = */ false);
5755 9 : if (poGotSRS)
5756 : {
5757 6 : oGeomFieldDefn.SetSpatialRef(poGotSRS.get());
5758 : }
5759 : else
5760 : {
5761 3 : bool bOK = false;
5762 3 : OGRSpatialReference *poSRSTmp = new OGRSpatialReference();
5763 3 : if (m_iSrs < 32767)
5764 : {
5765 : CPLErrorHandlerPusher oErrorHandler(
5766 4 : CPLQuietErrorHandler);
5767 4 : CPLErrorStateBackuper oBackuper;
5768 2 : if (poSRSTmp->importFromEPSG(m_iSrs) == OGRERR_NONE)
5769 : {
5770 1 : bOK = true;
5771 1 : poSRSTmp->SetAxisMappingStrategy(
5772 : OAMS_TRADITIONAL_GIS_ORDER);
5773 1 : m_iSrs = m_poDS->GetSrsId(poSRSTmp);
5774 1 : oGeomFieldDefn.SetSpatialRef(poSRSTmp);
5775 : }
5776 : }
5777 3 : if (!bOK)
5778 : {
5779 2 : CPLError(
5780 : CE_Warning, CPLE_AppDefined,
5781 : "No entry in gpkg_spatial_ref_sys matching SRID=%s",
5782 : pszSRID);
5783 : }
5784 3 : poSRSTmp->Release();
5785 : }
5786 : }
5787 : }
5788 : else
5789 : {
5790 619 : m_iSrs = m_poDS->GetSrsId(poSRS);
5791 : }
5792 629 : oGeomFieldDefn.SetNullable(bGeomNullable);
5793 629 : oGeomFieldDefn.SetCoordinatePrecision(oCoordPrec);
5794 :
5795 629 : if (bDiscardCoordLSB)
5796 2 : m_sBinaryPrecision.SetFrom(oCoordPrec);
5797 :
5798 : // Save coordinate precision in gpkg_metadata/gpkg_metadata_reference
5799 1877 : if ((oCoordPrec.dfXYResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5800 619 : oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN ||
5801 1258 : oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN) &&
5802 10 : (m_poDS->HasMetadataTables() || m_poDS->CreateMetadataTables()))
5803 : {
5804 20 : std::string osCoordPrecision = "<CoordinatePrecision ";
5805 10 : if (oCoordPrec.dfXYResolution !=
5806 : OGRGeomCoordinatePrecision::UNKNOWN)
5807 : osCoordPrecision += CPLSPrintf(" xy_resolution=\"%g\"",
5808 10 : oCoordPrec.dfXYResolution);
5809 10 : if (oCoordPrec.dfZResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5810 : osCoordPrecision += CPLSPrintf(" z_resolution=\"%g\"",
5811 7 : oCoordPrec.dfZResolution);
5812 10 : if (oCoordPrec.dfMResolution != OGRGeomCoordinatePrecision::UNKNOWN)
5813 : osCoordPrecision += CPLSPrintf(" m_resolution=\"%g\"",
5814 7 : oCoordPrec.dfMResolution);
5815 : osCoordPrecision += CPLSPrintf(" discard_coord_lsb=\"%s\"",
5816 10 : bDiscardCoordLSB ? "true" : "false");
5817 : osCoordPrecision +=
5818 : CPLSPrintf(" undo_discard_coord_lsb_on_reading=\"%s\"",
5819 10 : m_bUndoDiscardCoordLSBOnReading ? "true" : "false");
5820 10 : osCoordPrecision += " />";
5821 :
5822 10 : char *pszSQL = sqlite3_mprintf(
5823 : "INSERT INTO gpkg_metadata "
5824 : "(md_scope, md_standard_uri, mime_type, metadata) VALUES "
5825 : "('dataset','http://gdal.org','text/xml','%q')",
5826 : osCoordPrecision.c_str());
5827 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5828 10 : sqlite3_free(pszSQL);
5829 :
5830 : const sqlite_int64 nFID =
5831 10 : sqlite3_last_insert_rowid(m_poDS->GetDB());
5832 10 : pszSQL = sqlite3_mprintf(
5833 : "INSERT INTO gpkg_metadata_reference (reference_scope, "
5834 : "table_name, column_name, timestamp, md_file_id) VALUES "
5835 : "('column', '%q', '%q', %s, %d)",
5836 : m_pszTableName, pszGeomColumnName,
5837 20 : m_poDS->GetCurrentDateEscapedSQL().c_str(),
5838 : static_cast<int>(nFID));
5839 10 : CPL_IGNORE_RET_VAL(SQLCommand(m_poDS->GetDB(), pszSQL));
5840 10 : sqlite3_free(pszSQL);
5841 : }
5842 :
5843 629 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomFieldDefn);
5844 : }
5845 681 : if (pszIdentifier)
5846 : {
5847 3 : m_osIdentifierLCO = pszIdentifier;
5848 3 : OGRLayer::SetMetadataItem("IDENTIFIER", pszIdentifier);
5849 : }
5850 681 : if (pszDescription)
5851 : {
5852 1 : m_osDescriptionLCO = pszDescription;
5853 1 : OGRLayer::SetMetadataItem("DESCRIPTION", pszDescription);
5854 : }
5855 :
5856 681 : m_poFeatureDefn->Seal(/* bSealFields = */ true);
5857 681 : }
5858 :
5859 : /************************************************************************/
5860 : /* RegisterGeometryColumn() */
5861 : /************************************************************************/
5862 :
5863 632 : OGRErr OGRGeoPackageTableLayer::RegisterGeometryColumn()
5864 : {
5865 632 : OGRwkbGeometryType eGType = GetGeomType();
5866 632 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5867 : /* Requirement 27: The z value in a gpkg_geometry_columns table row */
5868 : /* SHALL be one of 0 (none), 1 (mandatory), or 2 (optional) */
5869 :
5870 : /* Update gpkg_geometry_columns with the table info */
5871 : char *pszSQL =
5872 632 : sqlite3_mprintf("INSERT INTO gpkg_geometry_columns "
5873 : "(table_name,column_name,geometry_type_name,srs_id,z,m)"
5874 : " VALUES "
5875 : "('%q','%q','%q',%d,%d,%d)",
5876 : GetName(), GetGeometryColumn(), pszGeometryType, m_iSrs,
5877 : m_nZFlag, m_nMFlag);
5878 :
5879 632 : OGRErr err = SQLCommand(m_poDS->GetDB(), pszSQL);
5880 632 : sqlite3_free(pszSQL);
5881 632 : if (err != OGRERR_NONE)
5882 0 : return OGRERR_FAILURE;
5883 :
5884 632 : if (wkbFlatten(eGType) > wkbGeometryCollection)
5885 : {
5886 6 : CreateGeometryExtensionIfNecessary(eGType);
5887 : }
5888 :
5889 632 : return OGRERR_NONE;
5890 : }
5891 :
5892 : /************************************************************************/
5893 : /* GetColumnsOfCreateTable() */
5894 : /************************************************************************/
5895 :
5896 704 : CPLString OGRGeoPackageTableLayer::GetColumnsOfCreateTable(
5897 : const std::vector<OGRFieldDefn *> &apoFields)
5898 : {
5899 704 : CPLString osSQL;
5900 :
5901 704 : char *pszSQL = nullptr;
5902 704 : bool bNeedComma = false;
5903 704 : if (m_pszFidColumn != nullptr)
5904 : {
5905 : pszSQL =
5906 704 : sqlite3_mprintf("\"%w\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL",
5907 : m_pszFidColumn);
5908 704 : osSQL += pszSQL;
5909 704 : sqlite3_free(pszSQL);
5910 704 : bNeedComma = true;
5911 : }
5912 :
5913 704 : const OGRwkbGeometryType eGType = GetGeomType();
5914 704 : if (eGType != wkbNone)
5915 : {
5916 653 : if (bNeedComma)
5917 : {
5918 653 : osSQL += ", ";
5919 : }
5920 653 : bNeedComma = true;
5921 :
5922 : /* Requirement 25: The geometry_type_name value in a
5923 : * gpkg_geometry_columns */
5924 : /* row SHALL be one of the uppercase geometry type names specified in */
5925 : /* Geometry Types (Normative). */
5926 653 : const char *pszGeometryType = m_poDS->GetGeometryTypeString(eGType);
5927 :
5928 : pszSQL =
5929 653 : sqlite3_mprintf("\"%w\" %s", GetGeometryColumn(), pszGeometryType);
5930 653 : osSQL += pszSQL;
5931 653 : sqlite3_free(pszSQL);
5932 653 : if (!m_poFeatureDefn->GetGeomFieldDefn(0)->IsNullable())
5933 : {
5934 2 : osSQL += " NOT NULL";
5935 : }
5936 : }
5937 :
5938 4480 : for (size_t i = 0; i < apoFields.size(); i++)
5939 : {
5940 3776 : OGRFieldDefn *poFieldDefn = apoFields[i];
5941 : // Eg. when a geometry type is specified + an sql statement returns no
5942 : // or NULL geometry values, the geom column is incorrectly treated as
5943 : // an attribute column as well with the same name. Not ideal, but skip
5944 : // this column here to avoid duplicate column name error. Issue: #6976.
5945 7480 : if ((eGType != wkbNone) &&
5946 3704 : (EQUAL(poFieldDefn->GetNameRef(), GetGeometryColumn())))
5947 : {
5948 0 : continue;
5949 : }
5950 3776 : if (bNeedComma)
5951 : {
5952 3776 : osSQL += ", ";
5953 : }
5954 3776 : bNeedComma = true;
5955 :
5956 3776 : pszSQL = sqlite3_mprintf("\"%w\" %s", poFieldDefn->GetNameRef(),
5957 : GPkgFieldFromOGR(poFieldDefn->GetType(),
5958 : poFieldDefn->GetSubType(),
5959 : poFieldDefn->GetWidth()));
5960 3776 : osSQL += pszSQL;
5961 3776 : sqlite3_free(pszSQL);
5962 3776 : if (!poFieldDefn->IsNullable())
5963 : {
5964 13 : osSQL += " NOT NULL";
5965 : }
5966 3776 : if (poFieldDefn->IsUnique())
5967 : {
5968 9 : osSQL += " UNIQUE";
5969 : }
5970 3776 : const char *pszDefault = poFieldDefn->GetDefault();
5971 3790 : if (pszDefault != nullptr &&
5972 14 : (!poFieldDefn->IsDefaultDriverSpecific() ||
5973 1 : (pszDefault[0] == '(' &&
5974 1 : pszDefault[strlen(pszDefault) - 1] == ')' &&
5975 1 : (STARTS_WITH_CI(pszDefault + 1, "strftime") ||
5976 0 : STARTS_WITH_CI(pszDefault + 1, " strftime")))))
5977 : {
5978 14 : osSQL += " DEFAULT ";
5979 : OGRField sField;
5980 18 : if (poFieldDefn->GetType() == OFTDateTime &&
5981 4 : OGRParseDate(pszDefault, &sField, 0))
5982 : {
5983 : char szBuffer[OGR_SIZEOF_ISO8601_DATETIME_BUFFER];
5984 0 : OGRGetISO8601DateTime(&sField, false, szBuffer);
5985 0 : osSQL += szBuffer;
5986 : }
5987 : /* Make sure CURRENT_TIMESTAMP is translated into appropriate format
5988 : * for GeoPackage */
5989 18 : else if (poFieldDefn->GetType() == OFTDateTime &&
5990 4 : EQUAL(pszDefault, "CURRENT_TIMESTAMP"))
5991 : {
5992 1 : osSQL += "(strftime('%Y-%m-%dT%H:%M:%fZ','now'))";
5993 : }
5994 : else
5995 : {
5996 13 : osSQL += poFieldDefn->GetDefault();
5997 : }
5998 : }
5999 : }
6000 :
6001 704 : return osSQL;
6002 : }
6003 :
6004 : /************************************************************************/
6005 : /* RunDeferredCreationIfNecessary() */
6006 : /************************************************************************/
6007 :
6008 4630 : OGRErr OGRGeoPackageTableLayer::RunDeferredCreationIfNecessary()
6009 : {
6010 4630 : if (!m_bDeferredCreation)
6011 3949 : return OGRERR_NONE;
6012 681 : m_bDeferredCreation = false;
6013 :
6014 681 : const char *pszLayerName = m_poFeatureDefn->GetName();
6015 :
6016 : /* Create the table! */
6017 1362 : CPLString osCommand;
6018 :
6019 681 : char *pszSQL = sqlite3_mprintf("CREATE TABLE \"%w\" ( ", pszLayerName);
6020 681 : osCommand += pszSQL;
6021 681 : sqlite3_free(pszSQL);
6022 :
6023 1362 : std::vector<OGRFieldDefn *> apoFields;
6024 4403 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); i++)
6025 : {
6026 3722 : if (i == m_iFIDAsRegularColumnIndex)
6027 4 : continue;
6028 3718 : apoFields.push_back(m_poFeatureDefn->GetFieldDefn(i));
6029 : }
6030 :
6031 681 : osCommand += GetColumnsOfCreateTable(apoFields);
6032 :
6033 681 : osCommand += ")";
6034 :
6035 : #ifdef DEBUG
6036 681 : CPLDebug("GPKG", "exec(%s)", osCommand.c_str());
6037 : #endif
6038 681 : OGRErr err = SQLCommand(m_poDS->GetDB(), osCommand.c_str());
6039 681 : if (OGRERR_NONE != err)
6040 0 : return OGRERR_FAILURE;
6041 :
6042 4399 : for (auto &poField : apoFields)
6043 : {
6044 3718 : if (!DoSpecialProcessingForColumnCreation(poField))
6045 : {
6046 0 : return OGRERR_FAILURE;
6047 : }
6048 : }
6049 :
6050 : /* Update gpkg_contents with the table info */
6051 681 : const OGRwkbGeometryType eGType = GetGeomType();
6052 681 : const bool bIsSpatial = (eGType != wkbNone);
6053 :
6054 681 : if (bIsSpatial || m_eASpatialVariant == GPKG_ATTRIBUTES)
6055 : {
6056 669 : const char *pszIdentifier = GetMetadataItem("IDENTIFIER");
6057 669 : if (pszIdentifier == nullptr)
6058 666 : pszIdentifier = pszLayerName;
6059 669 : const char *pszDescription = GetMetadataItem("DESCRIPTION");
6060 669 : if (pszDescription == nullptr)
6061 668 : pszDescription = "";
6062 :
6063 669 : pszSQL = sqlite3_mprintf(
6064 : "INSERT INTO gpkg_contents "
6065 : "(table_name,data_type,identifier,description,last_change,srs_id) "
6066 : "VALUES "
6067 : "('%q','%q','%q','%q',%s,%d)",
6068 : pszLayerName, (bIsSpatial ? "features" : "attributes"),
6069 : pszIdentifier, pszDescription,
6070 1338 : GDALGeoPackageDataset::GetCurrentDateEscapedSQL().c_str(), m_iSrs);
6071 :
6072 669 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6073 669 : sqlite3_free(pszSQL);
6074 669 : if (err != OGRERR_NONE)
6075 0 : return OGRERR_FAILURE;
6076 : }
6077 :
6078 681 : if (bIsSpatial)
6079 : {
6080 : // Insert into gpkg_geometry_columns after gpkg_contents because of
6081 : // foreign key constraints
6082 630 : err = RegisterGeometryColumn();
6083 630 : if (err != OGRERR_NONE)
6084 0 : return OGRERR_FAILURE;
6085 : }
6086 :
6087 : #ifdef ENABLE_GPKG_OGR_CONTENTS
6088 681 : if (m_poDS->m_bHasGPKGOGRContents)
6089 : {
6090 676 : pszSQL = sqlite3_mprintf("DELETE FROM gpkg_ogr_contents WHERE "
6091 : "lower(table_name) = lower('%q')",
6092 : pszLayerName);
6093 676 : SQLCommand(m_poDS->GetDB(), pszSQL);
6094 676 : sqlite3_free(pszSQL);
6095 :
6096 676 : pszSQL = sqlite3_mprintf(
6097 : "INSERT INTO gpkg_ogr_contents (table_name, feature_count) "
6098 : "VALUES ('%q', 0)",
6099 : pszLayerName);
6100 676 : err = SQLCommand(m_poDS->GetDB(), pszSQL);
6101 676 : sqlite3_free(pszSQL);
6102 676 : if (err == OGRERR_NONE)
6103 : {
6104 676 : m_nTotalFeatureCount = 0;
6105 676 : m_bAddOGRFeatureCountTriggers = true;
6106 : }
6107 : }
6108 : #endif
6109 :
6110 681 : ResetReading();
6111 :
6112 681 : return OGRERR_NONE;
6113 : }
6114 :
6115 : /************************************************************************/
6116 : /* GetMetadata() */
6117 : /************************************************************************/
6118 :
6119 10306 : char **OGRGeoPackageTableLayer::GetMetadata(const char *pszDomain)
6120 :
6121 : {
6122 10306 : if (!m_bFeatureDefnCompleted)
6123 276 : GetLayerDefn();
6124 10306 : if (!m_bHasTriedDetectingFID64 && m_pszFidColumn != nullptr)
6125 : {
6126 345 : m_bHasTriedDetectingFID64 = true;
6127 :
6128 : /* --------------------------------------------------------------------
6129 : */
6130 : /* Find if the FID holds 64bit values */
6131 : /* --------------------------------------------------------------------
6132 : */
6133 :
6134 : // Normally the fid should be AUTOINCREMENT, so check sqlite_sequence
6135 345 : OGRErr err = OGRERR_NONE;
6136 : char *pszSQL =
6137 345 : sqlite3_mprintf("SELECT seq FROM sqlite_sequence WHERE name = '%q'",
6138 : m_pszTableName);
6139 345 : CPLPushErrorHandler(CPLQuietErrorHandler);
6140 345 : GIntBig nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
6141 345 : CPLPopErrorHandler();
6142 345 : sqlite3_free(pszSQL);
6143 345 : if (err != OGRERR_NONE)
6144 : {
6145 40 : CPLErrorReset();
6146 :
6147 : // In case of error, fallback to taking the MAX of the FID
6148 40 : pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
6149 : m_pszFidColumn, m_pszTableName);
6150 :
6151 40 : nMaxId = SQLGetInteger64(m_poDS->GetDB(), pszSQL, nullptr);
6152 40 : sqlite3_free(pszSQL);
6153 : }
6154 345 : if (nMaxId > INT_MAX)
6155 1 : OGRLayer::SetMetadataItem(OLMD_FID64, "YES");
6156 : }
6157 :
6158 10306 : if (m_bHasReadMetadataFromStorage)
6159 9289 : return OGRLayer::GetMetadata(pszDomain);
6160 :
6161 1017 : m_bHasReadMetadataFromStorage = true;
6162 :
6163 1017 : if (!m_poDS->HasMetadataTables())
6164 767 : return OGRLayer::GetMetadata(pszDomain);
6165 :
6166 250 : char *pszSQL = sqlite3_mprintf(
6167 : "SELECT md.metadata, md.md_standard_uri, md.mime_type, "
6168 : "mdr.reference_scope "
6169 : "FROM gpkg_metadata md "
6170 : "JOIN gpkg_metadata_reference mdr ON (md.id = mdr.md_file_id ) "
6171 : "WHERE lower(mdr.table_name) = lower('%q') ORDER BY md.id "
6172 : "LIMIT 1000", // to avoid denial of service
6173 : m_pszTableName);
6174 :
6175 500 : auto oResult = SQLQuery(m_poDS->GetDB(), pszSQL);
6176 250 : sqlite3_free(pszSQL);
6177 250 : if (!oResult)
6178 : {
6179 0 : return OGRLayer::GetMetadata(pszDomain);
6180 : }
6181 :
6182 250 : char **papszMetadata = CSLDuplicate(OGRLayer::GetMetadata());
6183 :
6184 : /* GDAL metadata */
6185 482 : for (int i = 0; i < oResult->RowCount(); i++)
6186 : {
6187 232 : const char *pszMetadata = oResult->GetValue(0, i);
6188 232 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6189 232 : const char *pszMimeType = oResult->GetValue(2, i);
6190 232 : const char *pszReferenceScope = oResult->GetValue(3, i);
6191 232 : if (pszMetadata && pszMDStandardURI && pszMimeType &&
6192 232 : pszReferenceScope && EQUAL(pszMDStandardURI, "http://gdal.org") &&
6193 232 : EQUAL(pszMimeType, "text/xml") && EQUAL(pszReferenceScope, "table"))
6194 : {
6195 213 : CPLXMLNode *psXMLNode = CPLParseXMLString(pszMetadata);
6196 213 : if (psXMLNode)
6197 : {
6198 426 : GDALMultiDomainMetadata oLocalMDMD;
6199 213 : oLocalMDMD.XMLInit(psXMLNode, FALSE);
6200 :
6201 : papszMetadata =
6202 213 : CSLMerge(papszMetadata, oLocalMDMD.GetMetadata());
6203 213 : CSLConstList papszDomainList = oLocalMDMD.GetDomainList();
6204 213 : CSLConstList papszIter = papszDomainList;
6205 450 : while (papszIter && *papszIter)
6206 : {
6207 237 : if (!EQUAL(*papszIter, ""))
6208 25 : oMDMD.SetMetadata(oLocalMDMD.GetMetadata(*papszIter),
6209 : *papszIter);
6210 237 : papszIter++;
6211 : }
6212 :
6213 213 : CPLDestroyXMLNode(psXMLNode);
6214 : }
6215 : }
6216 : }
6217 :
6218 250 : OGRLayer::SetMetadata(papszMetadata);
6219 250 : CSLDestroy(papszMetadata);
6220 250 : papszMetadata = nullptr;
6221 :
6222 : /* Add non-GDAL metadata now */
6223 250 : int nNonGDALMDILocal = 1;
6224 482 : for (int i = 0; i < oResult->RowCount(); i++)
6225 : {
6226 232 : const char *pszMetadata = oResult->GetValue(0, i);
6227 232 : const char *pszMDStandardURI = oResult->GetValue(1, i);
6228 232 : const char *pszMimeType = oResult->GetValue(2, i);
6229 : // const char* pszReferenceScope = oResult->GetValue(3, i);
6230 232 : if (pszMetadata == nullptr || pszMDStandardURI == nullptr ||
6231 : pszMimeType == nullptr
6232 : /* || pszReferenceScope == nullptr */)
6233 : {
6234 : // should not happen as there are NOT NULL constraints
6235 : // But a database could lack such NOT NULL constraints or have
6236 : // large values that would cause a memory allocation failure.
6237 0 : continue;
6238 : }
6239 : // int bIsGPKGScope = EQUAL(pszReferenceScope, "geopackage");
6240 232 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6241 232 : EQUAL(pszMimeType, "text/xml"))
6242 232 : continue;
6243 :
6244 0 : if (EQUAL(pszMDStandardURI, "http://gdal.org") &&
6245 0 : EQUAL(pszMimeType, "text/plain"))
6246 : {
6247 0 : if (STARTS_WITH_CI(pszMetadata, "coordinate_epoch="))
6248 : {
6249 0 : continue;
6250 : }
6251 : }
6252 :
6253 : /*if( strcmp( pszMDStandardURI, "http://www.isotc211.org/2005/gmd" ) ==
6254 : 0 && strcmp( pszMimeType, "text/xml" ) == 0 )
6255 : {
6256 : char* apszMD[2];
6257 : apszMD[0] = (char*)pszMetadata;
6258 : apszMD[1] = NULL;
6259 : oMDMD.SetMetadata(apszMD, "xml:MD_Metadata");
6260 : }
6261 : else*/
6262 : {
6263 0 : oMDMD.SetMetadataItem(
6264 : CPLSPrintf("GPKG_METADATA_ITEM_%d", nNonGDALMDILocal),
6265 : pszMetadata);
6266 0 : nNonGDALMDILocal++;
6267 : }
6268 : }
6269 :
6270 250 : return OGRLayer::GetMetadata(pszDomain);
6271 : }
6272 :
6273 : /************************************************************************/
6274 : /* GetMetadataItem() */
6275 : /************************************************************************/
6276 :
6277 8943 : const char *OGRGeoPackageTableLayer::GetMetadataItem(const char *pszName,
6278 : const char *pszDomain)
6279 : {
6280 8943 : return CSLFetchNameValue(GetMetadata(pszDomain), pszName);
6281 : }
6282 :
6283 : /************************************************************************/
6284 : /* GetMetadataDomainList() */
6285 : /************************************************************************/
6286 :
6287 251 : char **OGRGeoPackageTableLayer::GetMetadataDomainList()
6288 : {
6289 251 : GetMetadata();
6290 251 : return OGRLayer::GetMetadataDomainList();
6291 : }
6292 :
6293 : /************************************************************************/
6294 : /* SetMetadata() */
6295 : /************************************************************************/
6296 :
6297 100 : CPLErr OGRGeoPackageTableLayer::SetMetadata(char **papszMetadata,
6298 : const char *pszDomain)
6299 : {
6300 100 : GetMetadata(); /* force loading from storage if needed */
6301 100 : CPLErr eErr = OGRLayer::SetMetadata(papszMetadata, pszDomain);
6302 100 : m_poDS->SetMetadataDirty();
6303 100 : if (pszDomain == nullptr || EQUAL(pszDomain, ""))
6304 : {
6305 58 : if (!m_osIdentifierLCO.empty())
6306 1 : OGRLayer::SetMetadataItem("IDENTIFIER", m_osIdentifierLCO);
6307 58 : if (!m_osDescriptionLCO.empty())
6308 1 : OGRLayer::SetMetadataItem("DESCRIPTION", m_osDescriptionLCO);
6309 : }
6310 100 : return eErr;
6311 : }
6312 :
6313 : /************************************************************************/
6314 : /* SetMetadataItem() */
6315 : /************************************************************************/
6316 :
6317 297 : CPLErr OGRGeoPackageTableLayer::SetMetadataItem(const char *pszName,
6318 : const char *pszValue,
6319 : const char *pszDomain)
6320 : {
6321 297 : GetMetadata(); /* force loading from storage if needed */
6322 298 : if (!m_osIdentifierLCO.empty() && EQUAL(pszName, "IDENTIFIER") &&
6323 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6324 1 : return CE_None;
6325 297 : if (!m_osDescriptionLCO.empty() && EQUAL(pszName, "DESCRIPTION") &&
6326 1 : (pszDomain == nullptr || EQUAL(pszDomain, "")))
6327 1 : return CE_None;
6328 295 : m_poDS->SetMetadataDirty();
6329 295 : return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
6330 : }
6331 :
6332 : /************************************************************************/
6333 : /* RecreateTable() */
6334 : /************************************************************************/
6335 :
6336 : OGRErr
6337 8 : OGRGeoPackageTableLayer::RecreateTable(const CPLString &osColumnsForCreate,
6338 : const CPLString &osFieldListForSelect)
6339 : {
6340 : /* -------------------------------------------------------------------- */
6341 : /* Save existing related triggers and index */
6342 : /* -------------------------------------------------------------------- */
6343 8 : sqlite3 *hDB = m_poDS->GetDB();
6344 :
6345 8 : char *pszSQL = sqlite3_mprintf(
6346 : "SELECT sql FROM sqlite_master WHERE type IN ('trigger','index') "
6347 : "AND lower(tbl_name)=lower('%q') LIMIT 10000",
6348 : m_pszTableName);
6349 8 : OGRErr eErr = OGRERR_NONE;
6350 8 : auto oTriggers = SQLQuery(hDB, pszSQL);
6351 8 : sqlite3_free(pszSQL);
6352 :
6353 : /* -------------------------------------------------------------------- */
6354 : /* Make a temporary table with new content. */
6355 : /* -------------------------------------------------------------------- */
6356 8 : if (oTriggers)
6357 : {
6358 8 : pszSQL = sqlite3_mprintf("CREATE TABLE \"%w_ogr_tmp\" (%s)",
6359 : m_pszTableName, osColumnsForCreate.c_str());
6360 8 : eErr = SQLCommand(hDB, pszSQL);
6361 8 : sqlite3_free(pszSQL);
6362 : }
6363 : else
6364 : {
6365 0 : eErr = OGRERR_FAILURE;
6366 : }
6367 :
6368 8 : if (eErr == OGRERR_NONE)
6369 : {
6370 8 : pszSQL = sqlite3_mprintf(
6371 : "INSERT INTO \"%w_ogr_tmp\" SELECT %s FROM \"%w\"", m_pszTableName,
6372 : osFieldListForSelect.c_str(), m_pszTableName);
6373 8 : eErr = SQLCommand(hDB, pszSQL);
6374 8 : sqlite3_free(pszSQL);
6375 : }
6376 :
6377 : /* -------------------------------------------------------------------- */
6378 : /* Drop the original table */
6379 : /* -------------------------------------------------------------------- */
6380 8 : if (eErr == OGRERR_NONE)
6381 : {
6382 6 : pszSQL = sqlite3_mprintf("DROP TABLE \"%w\"", m_pszTableName);
6383 6 : eErr = SQLCommand(hDB, pszSQL);
6384 6 : sqlite3_free(pszSQL);
6385 : }
6386 :
6387 : /* -------------------------------------------------------------------- */
6388 : /* Rename temporary table as new table */
6389 : /* -------------------------------------------------------------------- */
6390 8 : if (eErr == OGRERR_NONE)
6391 : {
6392 6 : pszSQL = sqlite3_mprintf("ALTER TABLE \"%w_ogr_tmp\" RENAME TO \"%w\"",
6393 : m_pszTableName, m_pszTableName);
6394 6 : eErr = SQLCommand(hDB, pszSQL);
6395 6 : sqlite3_free(pszSQL);
6396 : }
6397 :
6398 : /* -------------------------------------------------------------------- */
6399 : /* Recreate existing related tables, triggers and index */
6400 : /* -------------------------------------------------------------------- */
6401 45 : for (int i = 0;
6402 45 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
6403 : {
6404 37 : const char *pszSQLTriggerIdx = oTriggers->GetValue(0, i);
6405 37 : if (pszSQLTriggerIdx != nullptr && *pszSQLTriggerIdx != '\0')
6406 : {
6407 36 : eErr = SQLCommand(hDB, pszSQLTriggerIdx);
6408 : }
6409 : }
6410 :
6411 16 : return eErr;
6412 : }
6413 :
6414 : /************************************************************************/
6415 : /* BuildSelectFieldList() */
6416 : /************************************************************************/
6417 :
6418 8 : CPLString OGRGeoPackageTableLayer::BuildSelectFieldList(
6419 : const std::vector<OGRFieldDefn *> &apoFields)
6420 : {
6421 8 : CPLString osFieldListForSelect;
6422 :
6423 8 : char *pszSQL = nullptr;
6424 8 : bool bNeedComma = false;
6425 :
6426 8 : if (m_pszFidColumn != nullptr)
6427 : {
6428 8 : pszSQL = sqlite3_mprintf("\"%w\"", m_pszFidColumn);
6429 8 : osFieldListForSelect += pszSQL;
6430 8 : sqlite3_free(pszSQL);
6431 8 : bNeedComma = true;
6432 : }
6433 :
6434 8 : if (GetGeomType() != wkbNone)
6435 : {
6436 8 : if (bNeedComma)
6437 : {
6438 8 : osFieldListForSelect += ", ";
6439 : }
6440 8 : bNeedComma = true;
6441 :
6442 8 : pszSQL = sqlite3_mprintf("\"%w\"", GetGeometryColumn());
6443 8 : osFieldListForSelect += pszSQL;
6444 8 : sqlite3_free(pszSQL);
6445 : }
6446 :
6447 23 : for (size_t iField = 0; iField < apoFields.size(); iField++)
6448 : {
6449 15 : if (bNeedComma)
6450 : {
6451 15 : osFieldListForSelect += ", ";
6452 : }
6453 15 : bNeedComma = true;
6454 :
6455 15 : OGRFieldDefn *poFieldDefn = apoFields[iField];
6456 15 : pszSQL = sqlite3_mprintf("\"%w\"", poFieldDefn->GetNameRef());
6457 15 : osFieldListForSelect += pszSQL;
6458 15 : sqlite3_free(pszSQL);
6459 : }
6460 :
6461 8 : return osFieldListForSelect;
6462 : }
6463 :
6464 : /************************************************************************/
6465 : /* DeleteField() */
6466 : /************************************************************************/
6467 :
6468 13 : OGRErr OGRGeoPackageTableLayer::DeleteField(int iFieldToDelete)
6469 : {
6470 13 : if (!m_bFeatureDefnCompleted)
6471 2 : GetLayerDefn();
6472 13 : if (!CheckUpdatableTable("DeleteField"))
6473 2 : return OGRERR_FAILURE;
6474 :
6475 21 : if (iFieldToDelete < 0 ||
6476 10 : iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
6477 : {
6478 2 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6479 2 : return OGRERR_FAILURE;
6480 : }
6481 :
6482 9 : ResetReading();
6483 9 : RunDeferredCreationIfNecessary();
6484 9 : if (!RunDeferredSpatialIndexUpdate())
6485 0 : return OGRERR_FAILURE;
6486 :
6487 : const char *pszFieldName =
6488 9 : m_poFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
6489 :
6490 : /* -------------------------------------------------------------------- */
6491 : /* Drop any iterator since we change the DB structure */
6492 : /* -------------------------------------------------------------------- */
6493 9 : m_poDS->ResetReadingAllLayers();
6494 :
6495 : // Temporary remove foreign key checks
6496 : const GPKGTemporaryForeignKeyCheckDisabler
6497 18 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
6498 :
6499 9 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
6500 : {
6501 0 : return OGRERR_FAILURE;
6502 : }
6503 :
6504 : // ALTER TABLE ... DROP COLUMN ... was first implemented in 3.35.0 but
6505 : // there was bug fixes related to it until 3.35.5
6506 : #if SQLITE_VERSION_NUMBER >= 3035005L
6507 9 : OGRErr eErr = SQLCommand(
6508 18 : m_poDS->GetDB(), CPLString()
6509 : .Printf("ALTER TABLE \"%s\" DROP COLUMN \"%s\"",
6510 18 : SQLEscapeName(m_pszTableName).c_str(),
6511 27 : SQLEscapeName(pszFieldName).c_str())
6512 : .c_str());
6513 : #else
6514 : /* -------------------------------------------------------------------- */
6515 : /* Recreate table in a transaction */
6516 : /* Build list of old fields, and the list of new fields. */
6517 : /* -------------------------------------------------------------------- */
6518 : std::vector<OGRFieldDefn *> apoFields;
6519 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6520 : {
6521 : if (iField == iFieldToDelete)
6522 : continue;
6523 :
6524 : OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6525 : apoFields.push_back(poFieldDefn);
6526 : }
6527 :
6528 : CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
6529 : CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6530 :
6531 : OGRErr eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
6532 : #endif
6533 :
6534 : /* -------------------------------------------------------------------- */
6535 : /* Update gpkg_extensions if needed. */
6536 : /* -------------------------------------------------------------------- */
6537 9 : if (eErr == OGRERR_NONE && m_poDS->HasExtensionsTable())
6538 : {
6539 4 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_extensions WHERE "
6540 : "lower(table_name) = lower('%q') AND "
6541 : "lower(column_name) = lower('%q')",
6542 : m_pszTableName, pszFieldName);
6543 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6544 4 : sqlite3_free(pszSQL);
6545 : }
6546 :
6547 : /* -------------------------------------------------------------------- */
6548 : /* Update gpkg_data_columns if needed. */
6549 : /* -------------------------------------------------------------------- */
6550 9 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6551 : {
6552 4 : char *pszSQL = sqlite3_mprintf("DELETE FROM gpkg_data_columns WHERE "
6553 : "lower(table_name) = lower('%q') AND "
6554 : "lower(column_name) = lower('%q')",
6555 : m_pszTableName, pszFieldName);
6556 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6557 4 : sqlite3_free(pszSQL);
6558 : }
6559 :
6560 : /* -------------------------------------------------------------------- */
6561 : /* Update gpkg_metadata_reference if needed. */
6562 : /* -------------------------------------------------------------------- */
6563 9 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6564 : {
6565 : {
6566 : // Delete from gpkg_metadata metadata records that are only
6567 : // referenced by the column we are about to drop
6568 3 : char *pszSQL = sqlite3_mprintf(
6569 : "DELETE FROM gpkg_metadata WHERE id IN ("
6570 : "SELECT DISTINCT md_file_id FROM "
6571 : "gpkg_metadata_reference WHERE "
6572 : "lower(table_name) = lower('%q') "
6573 : "AND lower(column_name) = lower('%q') "
6574 : "AND md_parent_id is NULL) "
6575 : "AND id NOT IN ("
6576 : "SELECT DISTINCT md_file_id FROM gpkg_metadata_reference WHERE "
6577 : "md_file_id IN ("
6578 : "SELECT DISTINCT md_file_id FROM "
6579 : "gpkg_metadata_reference WHERE "
6580 : "lower(table_name) = lower('%q') "
6581 : "AND lower(column_name) = lower('%q') "
6582 : "AND md_parent_id is NULL) "
6583 : "AND ("
6584 : "lower(table_name) <> lower('%q') "
6585 : "OR column_name IS NULL "
6586 : "OR lower(column_name) <> lower('%q')))",
6587 : m_pszTableName, pszFieldName, m_pszTableName, pszFieldName,
6588 : m_pszTableName, pszFieldName);
6589 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6590 3 : sqlite3_free(pszSQL);
6591 : }
6592 :
6593 3 : if (eErr == OGRERR_NONE)
6594 : {
6595 : char *pszSQL =
6596 3 : sqlite3_mprintf("DELETE FROM gpkg_metadata_reference WHERE "
6597 : "lower(table_name) = lower('%q') AND "
6598 : "lower(column_name) = lower('%q')",
6599 : m_pszTableName, pszFieldName);
6600 3 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
6601 3 : sqlite3_free(pszSQL);
6602 : }
6603 : }
6604 :
6605 : /* -------------------------------------------------------------------- */
6606 : /* Check foreign key integrity if enforcement of foreign keys */
6607 : /* constraint is enabled. */
6608 : /* -------------------------------------------------------------------- */
6609 18 : if (eErr == OGRERR_NONE &&
6610 9 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
6611 : {
6612 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
6613 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
6614 : }
6615 :
6616 : /* -------------------------------------------------------------------- */
6617 : /* Finish */
6618 : /* -------------------------------------------------------------------- */
6619 9 : if (eErr == OGRERR_NONE)
6620 : {
6621 9 : eErr = m_poDS->SoftCommitTransaction();
6622 9 : if (eErr == OGRERR_NONE)
6623 : {
6624 :
6625 9 : if (m_poDS->IsInTransaction())
6626 : {
6627 4 : auto poFieldDefn{whileUnsealing(m_poFeatureDefn)
6628 8 : ->StealFieldDefn(iFieldToDelete)};
6629 4 : if (poFieldDefn)
6630 : {
6631 : m_apoFieldDefnChanges.emplace_back(
6632 4 : std::move(poFieldDefn), iFieldToDelete,
6633 4 : FieldChangeType::DELETE_FIELD,
6634 8 : m_abGeneratedColumns[iFieldToDelete]);
6635 : }
6636 : else
6637 : {
6638 0 : eErr = OGRERR_FAILURE;
6639 : }
6640 : }
6641 : else
6642 : {
6643 10 : eErr = whileUnsealing(m_poFeatureDefn)
6644 5 : ->DeleteFieldDefn(iFieldToDelete);
6645 : }
6646 :
6647 9 : if (eErr == OGRERR_NONE)
6648 : {
6649 : #if SQLITE_VERSION_NUMBER >= 3035005L
6650 9 : CPLAssert(iFieldToDelete <
6651 : static_cast<int>(m_abGeneratedColumns.size()));
6652 0 : m_abGeneratedColumns.erase(m_abGeneratedColumns.begin() +
6653 9 : iFieldToDelete);
6654 : #else
6655 : // We have recreated the table from scratch, and lost the
6656 : // generated column property
6657 : std::fill(m_abGeneratedColumns.begin(),
6658 : m_abGeneratedColumns.end(), false);
6659 : #endif
6660 : }
6661 :
6662 9 : ResetReading();
6663 : }
6664 : }
6665 : else
6666 : {
6667 0 : m_poDS->SoftRollbackTransaction();
6668 : }
6669 :
6670 9 : return eErr;
6671 : }
6672 :
6673 : /************************************************************************/
6674 : /* RenameFieldInAuxiliaryTables() */
6675 : /************************************************************************/
6676 :
6677 : OGRErr
6678 14 : OGRGeoPackageTableLayer::RenameFieldInAuxiliaryTables(const char *pszOldName,
6679 : const char *pszNewName)
6680 : {
6681 14 : OGRErr eErr = OGRERR_NONE;
6682 14 : sqlite3 *hDB = m_poDS->GetDB();
6683 :
6684 : /* -------------------------------------------------------------------- */
6685 : /* Update gpkg_extensions if needed. */
6686 : /* -------------------------------------------------------------------- */
6687 14 : if (/* eErr == OGRERR_NONE && */ m_poDS->HasExtensionsTable())
6688 : {
6689 12 : char *pszSQL = sqlite3_mprintf(
6690 : "UPDATE gpkg_extensions SET column_name = '%q' WHERE "
6691 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6692 : "lower('%q')",
6693 : pszNewName, m_pszTableName, pszOldName);
6694 12 : eErr = SQLCommand(hDB, pszSQL);
6695 12 : sqlite3_free(pszSQL);
6696 : }
6697 :
6698 : /* -------------------------------------------------------------------- */
6699 : /* Update gpkg_data_columns if needed. */
6700 : /* -------------------------------------------------------------------- */
6701 14 : if (eErr == OGRERR_NONE && m_poDS->HasDataColumnsTable())
6702 : {
6703 6 : char *pszSQL = sqlite3_mprintf(
6704 : "UPDATE gpkg_data_columns SET column_name = '%q' WHERE "
6705 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6706 : "lower('%q')",
6707 : pszNewName, m_pszTableName, pszOldName);
6708 6 : eErr = SQLCommand(hDB, pszSQL);
6709 6 : sqlite3_free(pszSQL);
6710 : }
6711 :
6712 : /* -------------------------------------------------------------------- */
6713 : /* Update gpkg_metadata_reference if needed. */
6714 : /* -------------------------------------------------------------------- */
6715 14 : if (eErr == OGRERR_NONE && m_poDS->HasMetadataTables())
6716 : {
6717 7 : char *pszSQL = sqlite3_mprintf(
6718 : "UPDATE gpkg_metadata_reference SET column_name = '%q' WHERE "
6719 : "lower(table_name) = lower('%q') AND lower(column_name) = "
6720 : "lower('%q')",
6721 : pszNewName, m_pszTableName, pszOldName);
6722 7 : eErr = SQLCommand(hDB, pszSQL);
6723 7 : sqlite3_free(pszSQL);
6724 : }
6725 :
6726 14 : return eErr;
6727 : }
6728 :
6729 : /************************************************************************/
6730 : /* AlterFieldDefn() */
6731 : /************************************************************************/
6732 :
6733 29 : OGRErr OGRGeoPackageTableLayer::AlterFieldDefn(int iFieldToAlter,
6734 : OGRFieldDefn *poNewFieldDefn,
6735 : int nFlagsIn)
6736 : {
6737 29 : if (!m_bFeatureDefnCompleted)
6738 1 : GetLayerDefn();
6739 29 : if (!CheckUpdatableTable("AlterFieldDefn"))
6740 2 : return OGRERR_FAILURE;
6741 :
6742 27 : if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
6743 : {
6744 1 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
6745 1 : return OGRERR_FAILURE;
6746 : }
6747 :
6748 : /* -------------------------------------------------------------------- */
6749 : /* Deferred actions, reset state. */
6750 : /* -------------------------------------------------------------------- */
6751 26 : ResetReading();
6752 26 : RunDeferredCreationIfNecessary();
6753 26 : if (m_bThreadRTreeStarted)
6754 0 : CancelAsyncRTree();
6755 26 : if (!RunDeferredSpatialIndexUpdate())
6756 0 : return OGRERR_FAILURE;
6757 :
6758 : /* -------------------------------------------------------------------- */
6759 : /* Check that the new column name is not a duplicate. */
6760 : /* -------------------------------------------------------------------- */
6761 :
6762 : OGRFieldDefn *poFieldDefnToAlter =
6763 26 : m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
6764 52 : const CPLString osOldColName(poFieldDefnToAlter->GetNameRef());
6765 26 : const CPLString osNewColName((nFlagsIn & ALTER_NAME_FLAG)
6766 : ? CPLString(poNewFieldDefn->GetNameRef())
6767 52 : : osOldColName);
6768 :
6769 : const bool bRenameCol =
6770 50 : (nFlagsIn & ALTER_NAME_FLAG) &&
6771 24 : strcmp(poNewFieldDefn->GetNameRef(), osOldColName) != 0;
6772 26 : if (bRenameCol)
6773 : {
6774 45 : if ((m_pszFidColumn &&
6775 15 : strcmp(poNewFieldDefn->GetNameRef(), m_pszFidColumn) == 0) ||
6776 14 : (GetGeomType() != wkbNone &&
6777 14 : strcmp(poNewFieldDefn->GetNameRef(),
6778 44 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()) == 0) ||
6779 13 : m_poFeatureDefn->GetFieldIndex(poNewFieldDefn->GetNameRef()) >= 0)
6780 : {
6781 4 : CPLError(CE_Failure, CPLE_AppDefined,
6782 : "Field name %s is already used for another field",
6783 : poNewFieldDefn->GetNameRef());
6784 4 : return OGRERR_FAILURE;
6785 : }
6786 : }
6787 :
6788 : /* -------------------------------------------------------------------- */
6789 : /* Build the modified field definition from the flags. */
6790 : /* -------------------------------------------------------------------- */
6791 44 : OGRFieldDefn oTmpFieldDefn(poFieldDefnToAlter);
6792 22 : bool bUseRewriteSchemaMethod = (m_poDS->nSoftTransactionLevel == 0);
6793 22 : int nActualFlags = 0;
6794 22 : if (bRenameCol)
6795 : {
6796 11 : nActualFlags |= ALTER_NAME_FLAG;
6797 11 : oTmpFieldDefn.SetName(poNewFieldDefn->GetNameRef());
6798 : }
6799 40 : if ((nFlagsIn & ALTER_TYPE_FLAG) != 0 &&
6800 18 : (poFieldDefnToAlter->GetType() != poNewFieldDefn->GetType() ||
6801 14 : poFieldDefnToAlter->GetSubType() != poNewFieldDefn->GetSubType()))
6802 : {
6803 6 : nActualFlags |= ALTER_TYPE_FLAG;
6804 6 : oTmpFieldDefn.SetSubType(OFSTNone);
6805 6 : oTmpFieldDefn.SetType(poNewFieldDefn->GetType());
6806 6 : oTmpFieldDefn.SetSubType(poNewFieldDefn->GetSubType());
6807 : }
6808 56 : if ((nFlagsIn & ALTER_WIDTH_PRECISION_FLAG) != 0 &&
6809 34 : (poFieldDefnToAlter->GetWidth() != poNewFieldDefn->GetWidth() ||
6810 16 : poFieldDefnToAlter->GetPrecision() != poNewFieldDefn->GetPrecision()))
6811 : {
6812 2 : nActualFlags |= ALTER_WIDTH_PRECISION_FLAG;
6813 2 : oTmpFieldDefn.SetWidth(poNewFieldDefn->GetWidth());
6814 2 : oTmpFieldDefn.SetPrecision(poNewFieldDefn->GetPrecision());
6815 : }
6816 40 : if ((nFlagsIn & ALTER_NULLABLE_FLAG) != 0 &&
6817 18 : poFieldDefnToAlter->IsNullable() != poNewFieldDefn->IsNullable())
6818 : {
6819 4 : nActualFlags |= ALTER_NULLABLE_FLAG;
6820 4 : bUseRewriteSchemaMethod = false;
6821 4 : oTmpFieldDefn.SetNullable(poNewFieldDefn->IsNullable());
6822 : }
6823 40 : if ((nFlagsIn & ALTER_DEFAULT_FLAG) != 0 &&
6824 18 : !((poFieldDefnToAlter->GetDefault() == nullptr &&
6825 15 : poNewFieldDefn->GetDefault() == nullptr) ||
6826 4 : (poFieldDefnToAlter->GetDefault() != nullptr &&
6827 3 : poNewFieldDefn->GetDefault() != nullptr &&
6828 2 : strcmp(poFieldDefnToAlter->GetDefault(),
6829 : poNewFieldDefn->GetDefault()) == 0)))
6830 : {
6831 2 : nActualFlags |= ALTER_DEFAULT_FLAG;
6832 2 : oTmpFieldDefn.SetDefault(poNewFieldDefn->GetDefault());
6833 : }
6834 40 : if ((nFlagsIn & ALTER_UNIQUE_FLAG) != 0 &&
6835 18 : poFieldDefnToAlter->IsUnique() != poNewFieldDefn->IsUnique())
6836 : {
6837 2 : nActualFlags |= ALTER_UNIQUE_FLAG;
6838 2 : bUseRewriteSchemaMethod = false;
6839 2 : oTmpFieldDefn.SetUnique(poNewFieldDefn->IsUnique());
6840 : }
6841 40 : if ((nFlagsIn & ALTER_DOMAIN_FLAG) != 0 &&
6842 18 : poFieldDefnToAlter->GetDomainName() != poNewFieldDefn->GetDomainName())
6843 : {
6844 3 : nActualFlags |= ALTER_DOMAIN_FLAG;
6845 3 : oTmpFieldDefn.SetDomainName(poNewFieldDefn->GetDomainName());
6846 : }
6847 41 : if ((nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG) != 0 &&
6848 19 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
6849 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
6850 : {
6851 5 : nActualFlags |= ALTER_ALTERNATIVE_NAME_FLAG;
6852 5 : oTmpFieldDefn.SetAlternativeName(
6853 : poNewFieldDefn->GetAlternativeNameRef());
6854 : }
6855 41 : if ((nFlagsIn & ALTER_COMMENT_FLAG) != 0 &&
6856 19 : poFieldDefnToAlter->GetComment() != poNewFieldDefn->GetComment())
6857 : {
6858 3 : nActualFlags |= ALTER_COMMENT_FLAG;
6859 3 : oTmpFieldDefn.SetComment(poNewFieldDefn->GetComment());
6860 : }
6861 :
6862 : /* -------------------------------------------------------------------- */
6863 : /* Build list of old fields, and the list of new fields. */
6864 : /* -------------------------------------------------------------------- */
6865 44 : std::vector<OGRFieldDefn *> apoFields;
6866 44 : std::vector<OGRFieldDefn *> apoFieldsOld;
6867 78 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
6868 : {
6869 : OGRFieldDefn *poFieldDefn;
6870 56 : if (iField == iFieldToAlter)
6871 : {
6872 22 : poFieldDefn = &oTmpFieldDefn;
6873 : }
6874 : else
6875 : {
6876 34 : poFieldDefn = m_poFeatureDefn->GetFieldDefn(iField);
6877 : }
6878 56 : apoFields.push_back(poFieldDefn);
6879 56 : apoFieldsOld.push_back(m_poFeatureDefn->GetFieldDefn(iField));
6880 : }
6881 :
6882 44 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
6883 :
6884 : /* -------------------------------------------------------------------- */
6885 : /* Drop any iterator since we change the DB structure */
6886 : /* -------------------------------------------------------------------- */
6887 22 : m_poDS->ResetReadingAllLayers();
6888 :
6889 22 : const bool bUseRenameColumn = (nActualFlags == ALTER_NAME_FLAG);
6890 22 : if (bUseRenameColumn)
6891 4 : bUseRewriteSchemaMethod = false;
6892 :
6893 22 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
6894 0 : return OGRERR_FAILURE;
6895 :
6896 22 : sqlite3 *hDB = m_poDS->GetDB();
6897 22 : OGRErr eErr = OGRERR_NONE;
6898 :
6899 : /* -------------------------------------------------------------------- */
6900 : /* Drop triggers and index that look like to be related to the */
6901 : /* column if renaming. We re-install some indexes afterwards. */
6902 : /* -------------------------------------------------------------------- */
6903 0 : std::unique_ptr<SQLResult> oTriggers;
6904 : // cppcheck-suppress knownConditionTrueFalse
6905 22 : if (bRenameCol && !bUseRenameColumn)
6906 : {
6907 7 : char *pszSQL = sqlite3_mprintf(
6908 : "SELECT name, type, sql FROM sqlite_master WHERE "
6909 : "type IN ('trigger','index') "
6910 : "AND lower(tbl_name)=lower('%q') AND sql LIKE '%%%q%%' LIMIT 10000",
6911 14 : m_pszTableName, SQLEscapeName(osOldColName).c_str());
6912 7 : oTriggers = SQLQuery(hDB, pszSQL);
6913 7 : sqlite3_free(pszSQL);
6914 :
6915 7 : if (!oTriggers)
6916 : {
6917 0 : eErr = OGRERR_FAILURE;
6918 : }
6919 :
6920 9 : for (int i = 0; oTriggers && i < oTriggers->RowCount(); i++)
6921 : {
6922 : pszSQL =
6923 2 : sqlite3_mprintf("DROP %s \"%w\"", oTriggers->GetValue(1, i),
6924 : oTriggers->GetValue(0, i));
6925 2 : eErr = SQLCommand(hDB, pszSQL);
6926 2 : sqlite3_free(pszSQL);
6927 : }
6928 : }
6929 :
6930 22 : if (bUseRenameColumn)
6931 : {
6932 4 : if (eErr == OGRERR_NONE)
6933 : {
6934 4 : CPLDebug("GPKG", "Running ALTER TABLE RENAME COLUMN");
6935 4 : eErr = SQLCommand(
6936 4 : m_poDS->GetDB(),
6937 8 : CPLString()
6938 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
6939 8 : SQLEscapeName(m_pszTableName).c_str(),
6940 8 : SQLEscapeName(osOldColName).c_str(),
6941 16 : SQLEscapeName(osNewColName).c_str())
6942 : .c_str());
6943 : }
6944 : }
6945 18 : else if (!bUseRewriteSchemaMethod)
6946 : {
6947 : /* --------------------------------------------------------------------
6948 : */
6949 : /* If we are within a transaction, we cannot use the method */
6950 : /* that consists in altering the database in a raw way. */
6951 : /* --------------------------------------------------------------------
6952 : */
6953 : const CPLString osFieldListForSelect(
6954 14 : BuildSelectFieldList(apoFieldsOld));
6955 :
6956 7 : if (eErr == OGRERR_NONE)
6957 : {
6958 7 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
6959 : }
6960 : }
6961 : else
6962 : {
6963 : /* --------------------------------------------------------------------
6964 : */
6965 : /* Rewrite schema in a transaction by altering the database */
6966 : /* schema in a rather raw way, as described at bottom of */
6967 : /* https://www.sqlite.org/lang_altertable.html */
6968 : /* --------------------------------------------------------------------
6969 : */
6970 :
6971 : /* --------------------------------------------------------------------
6972 : */
6973 : /* Collect schema version number. */
6974 : /* --------------------------------------------------------------------
6975 : */
6976 11 : int nSchemaVersion = SQLGetInteger(hDB, "PRAGMA schema_version", &eErr);
6977 :
6978 : /* --------------------------------------------------------------------
6979 : */
6980 : /* Turn on writable schema. */
6981 : /* --------------------------------------------------------------------
6982 : */
6983 11 : if (eErr == OGRERR_NONE)
6984 : {
6985 11 : eErr = m_poDS->PragmaCheck("writable_schema=ON", "", 0);
6986 : }
6987 :
6988 : /* --------------------------------------------------------------------
6989 : */
6990 : /* Rewrite CREATE TABLE statement. */
6991 : /* --------------------------------------------------------------------
6992 : */
6993 11 : if (eErr == OGRERR_NONE)
6994 : {
6995 : char *psSQLCreateTable =
6996 11 : sqlite3_mprintf("CREATE TABLE \"%w\" (%s)", m_pszTableName,
6997 : osColumnsForCreate.c_str());
6998 11 : char *pszSQL = sqlite3_mprintf("UPDATE sqlite_master SET sql='%q' "
6999 : "WHERE type='table' AND name='%q'",
7000 : psSQLCreateTable, m_pszTableName);
7001 11 : eErr = SQLCommand(hDB, pszSQL);
7002 11 : sqlite3_free(psSQLCreateTable);
7003 11 : sqlite3_free(pszSQL);
7004 : }
7005 :
7006 : /* --------------------------------------------------------------------
7007 : */
7008 : /* Increment schema number. */
7009 : /* --------------------------------------------------------------------
7010 : */
7011 11 : if (eErr == OGRERR_NONE)
7012 : {
7013 11 : char *pszSQL = sqlite3_mprintf("PRAGMA schema_version = %d",
7014 : nSchemaVersion + 1);
7015 11 : eErr = SQLCommand(hDB, pszSQL);
7016 11 : sqlite3_free(pszSQL);
7017 : }
7018 :
7019 : /* --------------------------------------------------------------------
7020 : */
7021 : /* Turn off writable schema. */
7022 : /* --------------------------------------------------------------------
7023 : */
7024 11 : if (eErr == OGRERR_NONE)
7025 : {
7026 11 : eErr = m_poDS->PragmaCheck("writable_schema=OFF", "", 0);
7027 : }
7028 : }
7029 :
7030 : /* -------------------------------------------------------------------- */
7031 : /* Update auxiliary tables */
7032 : /* -------------------------------------------------------------------- */
7033 22 : if (bRenameCol && eErr == OGRERR_NONE)
7034 : {
7035 10 : eErr = RenameFieldInAuxiliaryTables(osOldColName.c_str(),
7036 : poNewFieldDefn->GetNameRef());
7037 : }
7038 :
7039 : /* -------------------------------------------------------------------- */
7040 : /* Update gpkgext_relations if needed. */
7041 : /* -------------------------------------------------------------------- */
7042 22 : if (bRenameCol && eErr == OGRERR_NONE && m_poDS->HasGpkgextRelationsTable())
7043 : {
7044 2 : char *pszSQL = sqlite3_mprintf(
7045 : "UPDATE gpkgext_relations SET base_primary_column = '%q' WHERE "
7046 : "lower(base_table_name) = lower('%q') AND "
7047 : "lower(base_primary_column) = lower('%q')",
7048 : poNewFieldDefn->GetNameRef(), m_pszTableName, osOldColName.c_str());
7049 2 : eErr = SQLCommand(hDB, pszSQL);
7050 2 : sqlite3_free(pszSQL);
7051 :
7052 2 : if (eErr == OGRERR_NONE)
7053 : {
7054 : pszSQL =
7055 2 : sqlite3_mprintf("UPDATE gpkgext_relations SET "
7056 : "related_primary_column = '%q' WHERE "
7057 : "lower(related_table_name) = lower('%q') AND "
7058 : "lower(related_primary_column) = lower('%q')",
7059 : poNewFieldDefn->GetNameRef(), m_pszTableName,
7060 : osOldColName.c_str());
7061 2 : eErr = SQLCommand(hDB, pszSQL);
7062 2 : sqlite3_free(pszSQL);
7063 : }
7064 2 : m_poDS->ClearCachedRelationships();
7065 : }
7066 :
7067 : /* -------------------------------------------------------------------- */
7068 : /* Run integrity check only if explicitly required. */
7069 : /* -------------------------------------------------------------------- */
7070 42 : if (eErr == OGRERR_NONE &&
7071 20 : CPLTestBool(CPLGetConfigOption("OGR_GPKG_INTEGRITY_CHECK", "NO")))
7072 : {
7073 0 : CPLDebug("GPKG", "Running PRAGMA integrity_check");
7074 0 : eErr = m_poDS->PragmaCheck("integrity_check", "ok", 1);
7075 : }
7076 :
7077 : /* -------------------------------------------------------------------- */
7078 : /* Otherwise check foreign key integrity if enforcement of foreign */
7079 : /* kets constraint is enabled. */
7080 : /* -------------------------------------------------------------------- */
7081 42 : else if (eErr == OGRERR_NONE &&
7082 20 : SQLGetInteger(m_poDS->GetDB(), "PRAGMA foreign_keys", nullptr))
7083 : {
7084 0 : CPLDebug("GPKG", "Running PRAGMA foreign_key_check");
7085 0 : eErr = m_poDS->PragmaCheck("foreign_key_check", "", 0);
7086 : }
7087 :
7088 : /* -------------------------------------------------------------------- */
7089 : /* Finish */
7090 : /* -------------------------------------------------------------------- */
7091 22 : if (eErr == OGRERR_NONE)
7092 : {
7093 :
7094 20 : eErr = m_poDS->SoftCommitTransaction();
7095 :
7096 : // We need to force database reopening due to schema change
7097 31 : if (eErr == OGRERR_NONE && bUseRewriteSchemaMethod &&
7098 11 : !m_poDS->ReOpenDB())
7099 : {
7100 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen database");
7101 0 : eErr = OGRERR_FAILURE;
7102 : }
7103 20 : hDB = m_poDS->GetDB();
7104 :
7105 : /* --------------------------------------------------------------------
7106 : */
7107 : /* Recreate indices. */
7108 : /* --------------------------------------------------------------------
7109 : */
7110 22 : for (int i = 0;
7111 22 : oTriggers && i < oTriggers->RowCount() && eErr == OGRERR_NONE; i++)
7112 : {
7113 2 : if (EQUAL(oTriggers->GetValue(1, i), "index"))
7114 : {
7115 4 : CPLString osSQL(oTriggers->GetValue(2, i));
7116 : // CREATE INDEX idx_name ON table_name(column_name)
7117 2 : char **papszTokens = SQLTokenize(osSQL);
7118 2 : if (CSLCount(papszTokens) == 8 &&
7119 2 : EQUAL(papszTokens[0], "CREATE") &&
7120 2 : EQUAL(papszTokens[1], "INDEX") &&
7121 6 : EQUAL(papszTokens[3], "ON") && EQUAL(papszTokens[5], "(") &&
7122 2 : EQUAL(papszTokens[7], ")"))
7123 : {
7124 2 : osSQL = "CREATE INDEX ";
7125 2 : osSQL += papszTokens[2];
7126 2 : osSQL += " ON ";
7127 2 : osSQL += papszTokens[4];
7128 2 : osSQL += "(\"";
7129 2 : osSQL += SQLEscapeName(osNewColName);
7130 2 : osSQL += "\")";
7131 2 : eErr = SQLCommand(hDB, osSQL);
7132 : }
7133 2 : CSLDestroy(papszTokens);
7134 : }
7135 : }
7136 :
7137 20 : if (eErr == OGRERR_NONE)
7138 : {
7139 :
7140 20 : if (m_poDS->IsInTransaction())
7141 : {
7142 : m_apoFieldDefnChanges.emplace_back(
7143 4 : std::make_unique<OGRFieldDefn>(poFieldDefnToAlter),
7144 4 : iFieldToAlter, FieldChangeType::ALTER_FIELD,
7145 12 : m_abGeneratedColumns[iFieldToAlter]);
7146 : }
7147 :
7148 40 : auto oTemporaryUnsealer(poFieldDefnToAlter->GetTemporaryUnsealer());
7149 20 : bool bNeedsEntryInGpkgDataColumns = false;
7150 :
7151 : // field type
7152 20 : if (nActualFlags & ALTER_TYPE_FLAG)
7153 : {
7154 6 : poFieldDefnToAlter->SetSubType(OFSTNone);
7155 6 : poFieldDefnToAlter->SetType(poNewFieldDefn->GetType());
7156 6 : poFieldDefnToAlter->SetSubType(poNewFieldDefn->GetSubType());
7157 : }
7158 29 : if (poFieldDefnToAlter->GetType() == OFTString &&
7159 9 : poFieldDefnToAlter->GetSubType() == OFSTJSON)
7160 : {
7161 1 : bNeedsEntryInGpkgDataColumns = true;
7162 : }
7163 :
7164 : // name
7165 20 : if (nActualFlags & ALTER_NAME_FLAG)
7166 : {
7167 10 : poFieldDefnToAlter->SetName(poNewFieldDefn->GetNameRef());
7168 : }
7169 :
7170 : // width/precision
7171 20 : if (nActualFlags & ALTER_WIDTH_PRECISION_FLAG)
7172 : {
7173 2 : poFieldDefnToAlter->SetWidth(poNewFieldDefn->GetWidth());
7174 2 : poFieldDefnToAlter->SetPrecision(
7175 : poNewFieldDefn->GetPrecision());
7176 : }
7177 :
7178 : // constraints
7179 20 : if (nActualFlags & ALTER_NULLABLE_FLAG)
7180 2 : poFieldDefnToAlter->SetNullable(poNewFieldDefn->IsNullable());
7181 20 : if (nActualFlags & ALTER_DEFAULT_FLAG)
7182 2 : poFieldDefnToAlter->SetDefault(poNewFieldDefn->GetDefault());
7183 20 : if (nActualFlags & ALTER_UNIQUE_FLAG)
7184 2 : poFieldDefnToAlter->SetUnique(poNewFieldDefn->IsUnique());
7185 :
7186 : // domain
7187 23 : if ((nActualFlags & ALTER_DOMAIN_FLAG) &&
7188 3 : poFieldDefnToAlter->GetDomainName() !=
7189 3 : poNewFieldDefn->GetDomainName())
7190 : {
7191 3 : poFieldDefnToAlter->SetDomainName(
7192 : poNewFieldDefn->GetDomainName());
7193 : }
7194 20 : if (!poFieldDefnToAlter->GetDomainName().empty())
7195 : {
7196 3 : bNeedsEntryInGpkgDataColumns = true;
7197 : }
7198 :
7199 : // alternative name
7200 25 : if ((nActualFlags & ALTER_ALTERNATIVE_NAME_FLAG) &&
7201 5 : strcmp(poFieldDefnToAlter->GetAlternativeNameRef(),
7202 : poNewFieldDefn->GetAlternativeNameRef()) != 0)
7203 : {
7204 5 : poFieldDefnToAlter->SetAlternativeName(
7205 : poNewFieldDefn->GetAlternativeNameRef());
7206 : }
7207 40 : if (!std::string(poFieldDefnToAlter->GetAlternativeNameRef())
7208 20 : .empty())
7209 : {
7210 7 : bNeedsEntryInGpkgDataColumns = true;
7211 : }
7212 :
7213 : // comment
7214 23 : if ((nActualFlags & ALTER_COMMENT_FLAG) &&
7215 3 : poFieldDefnToAlter->GetComment() !=
7216 3 : poNewFieldDefn->GetComment())
7217 : {
7218 3 : poFieldDefnToAlter->SetComment(poNewFieldDefn->GetComment());
7219 : }
7220 20 : if (!poFieldDefnToAlter->GetComment().empty())
7221 : {
7222 3 : bNeedsEntryInGpkgDataColumns = true;
7223 : }
7224 :
7225 20 : if (m_poDS->HasDataColumnsTable())
7226 : {
7227 14 : char *pszSQL = sqlite3_mprintf(
7228 : "DELETE FROM gpkg_data_columns WHERE "
7229 : "lower(table_name) = lower('%q') AND "
7230 : "lower(column_name) = lower('%q')",
7231 : m_pszTableName, poFieldDefnToAlter->GetNameRef());
7232 14 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7233 14 : sqlite3_free(pszSQL);
7234 : }
7235 :
7236 20 : if (bNeedsEntryInGpkgDataColumns)
7237 : {
7238 12 : if (!DoSpecialProcessingForColumnCreation(poFieldDefnToAlter))
7239 0 : eErr = OGRERR_FAILURE;
7240 : }
7241 :
7242 20 : ResetReading();
7243 : }
7244 : }
7245 : else
7246 : {
7247 2 : m_poDS->SoftRollbackTransaction();
7248 : }
7249 :
7250 22 : return eErr;
7251 : }
7252 :
7253 : /************************************************************************/
7254 : /* AlterGeomFieldDefn() */
7255 : /************************************************************************/
7256 :
7257 8 : OGRErr OGRGeoPackageTableLayer::AlterGeomFieldDefn(
7258 : int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
7259 : int nFlagsIn)
7260 : {
7261 8 : if (!m_bFeatureDefnCompleted)
7262 1 : GetLayerDefn();
7263 8 : if (!CheckUpdatableTable("AlterGeomFieldDefn"))
7264 0 : return OGRERR_FAILURE;
7265 :
7266 16 : if (iGeomFieldToAlter < 0 ||
7267 8 : iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
7268 : {
7269 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
7270 0 : return OGRERR_FAILURE;
7271 : }
7272 :
7273 : /* -------------------------------------------------------------------- */
7274 : /* Deferred actions, reset state. */
7275 : /* -------------------------------------------------------------------- */
7276 8 : ResetReading();
7277 8 : RunDeferredCreationIfNecessary();
7278 8 : if (m_bThreadRTreeStarted)
7279 0 : CancelAsyncRTree();
7280 8 : if (!RunDeferredSpatialIndexUpdate())
7281 0 : return OGRERR_FAILURE;
7282 8 : RevertWorkaroundUpdate1TriggerIssue();
7283 :
7284 : /* -------------------------------------------------------------------- */
7285 : /* Drop any iterator since we change the DB structure */
7286 : /* -------------------------------------------------------------------- */
7287 8 : m_poDS->ResetReadingAllLayers();
7288 :
7289 8 : auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
7290 16 : auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
7291 :
7292 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG)
7293 : {
7294 : // could be potentially done. Requires rewriting the CREATE TABLE
7295 : // statement
7296 0 : if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
7297 : {
7298 0 : CPLError(CE_Failure, CPLE_NotSupported,
7299 : "Altering the geometry field type is not currently "
7300 : "supported for "
7301 : "GeoPackage");
7302 0 : return OGRERR_FAILURE;
7303 : }
7304 : }
7305 :
7306 8 : if (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG)
7307 : {
7308 : // could be potentially done. Requires rewriting the CREATE TABLE
7309 : // statement
7310 0 : if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
7311 : {
7312 0 : CPLError(CE_Failure, CPLE_NotSupported,
7313 : "Altering the nullable state of the geometry field "
7314 : "is not currently supported for GeoPackage");
7315 0 : return OGRERR_FAILURE;
7316 : }
7317 : }
7318 :
7319 12 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
7320 4 : strcmp(poGeomFieldDefn->GetNameRef(),
7321 : poNewGeomFieldDefn->GetNameRef()) != 0)
7322 : {
7323 4 : const bool bHasSpatialIndex = HasSpatialIndex();
7324 :
7325 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7326 0 : return OGRERR_FAILURE;
7327 :
7328 : // Rename geometry field
7329 4 : auto eErr = SQLCommand(
7330 4 : m_poDS->GetDB(),
7331 4 : CPLString()
7332 : .Printf("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO \"%s\"",
7333 8 : SQLEscapeName(m_pszTableName).c_str(),
7334 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7335 16 : SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str())
7336 : .c_str());
7337 4 : if (eErr != OGRERR_NONE)
7338 : {
7339 0 : m_poDS->SoftRollbackTransaction();
7340 0 : return OGRERR_FAILURE;
7341 : }
7342 :
7343 : // Update gpkg_geometry_columns
7344 4 : eErr = SQLCommand(
7345 4 : m_poDS->GetDB(),
7346 4 : CPLString()
7347 : .Printf("UPDATE gpkg_geometry_columns SET column_name = \"%s\" "
7348 : "WHERE lower(table_name) = lower(\"%s\") "
7349 : "AND lower(column_name) = lower(\"%s\")",
7350 8 : SQLEscapeName(poNewGeomFieldDefn->GetNameRef()).c_str(),
7351 8 : SQLEscapeName(m_pszTableName).c_str(),
7352 16 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str())
7353 : .c_str());
7354 4 : if (eErr != OGRERR_NONE)
7355 : {
7356 0 : m_poDS->SoftRollbackTransaction();
7357 0 : return OGRERR_FAILURE;
7358 : }
7359 :
7360 : // Update auxiliary tables
7361 4 : eErr = RenameFieldInAuxiliaryTables(poGeomFieldDefn->GetNameRef(),
7362 : poNewGeomFieldDefn->GetNameRef());
7363 4 : if (eErr != OGRERR_NONE)
7364 : {
7365 0 : m_poDS->SoftRollbackTransaction();
7366 0 : return OGRERR_FAILURE;
7367 : }
7368 :
7369 4 : std::string osNewRTreeName;
7370 4 : if (bHasSpatialIndex)
7371 : {
7372 4 : osNewRTreeName = "rtree_";
7373 4 : osNewRTreeName += m_pszTableName;
7374 4 : osNewRTreeName += "_";
7375 4 : osNewRTreeName += poNewGeomFieldDefn->GetNameRef();
7376 :
7377 : // Rename spatial index tables (not strictly needed, but for
7378 : // consistency)
7379 : eErr =
7380 4 : SQLCommand(m_poDS->GetDB(),
7381 4 : CPLString().Printf(
7382 : "ALTER TABLE \"%s\" RENAME TO \"%s\"",
7383 8 : SQLEscapeName(m_osRTreeName.c_str()).c_str(),
7384 12 : SQLEscapeName(osNewRTreeName.c_str()).c_str()));
7385 4 : if (eErr != OGRERR_NONE)
7386 : {
7387 0 : m_poDS->SoftRollbackTransaction();
7388 0 : return OGRERR_FAILURE;
7389 : }
7390 :
7391 : // Finally rename triggers (not strictly needed, but for
7392 : // consistency)
7393 4 : std::string osTriggerSQL;
7394 4 : osTriggerSQL = ReturnSQLDropSpatialIndexTriggers();
7395 4 : osTriggerSQL += ";";
7396 8 : osTriggerSQL += ReturnSQLCreateSpatialIndexTriggers(
7397 4 : nullptr, poNewGeomFieldDefn->GetNameRef());
7398 4 : eErr = SQLCommand(m_poDS->GetDB(), osTriggerSQL.c_str());
7399 4 : if (eErr != OGRERR_NONE)
7400 : {
7401 0 : m_poDS->SoftRollbackTransaction();
7402 0 : return OGRERR_FAILURE;
7403 : }
7404 : }
7405 :
7406 4 : eErr = m_poDS->SoftCommitTransaction();
7407 4 : if (eErr != OGRERR_NONE)
7408 : {
7409 0 : return OGRERR_FAILURE;
7410 : }
7411 :
7412 4 : poGeomFieldDefn->SetName(poNewGeomFieldDefn->GetNameRef());
7413 :
7414 4 : if (bHasSpatialIndex)
7415 : {
7416 4 : m_osRTreeName = osNewRTreeName;
7417 : }
7418 : }
7419 :
7420 8 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0 ||
7421 5 : (nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7422 : {
7423 4 : const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
7424 4 : const auto poNewSRSRef = poNewGeomFieldDefn->GetSpatialRef();
7425 :
7426 0 : std::unique_ptr<OGRSpatialReference> poNewSRS;
7427 4 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
7428 : {
7429 3 : if (poNewSRSRef != nullptr)
7430 : {
7431 2 : poNewSRS.reset(poNewSRSRef->Clone());
7432 2 : if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) ==
7433 : 0)
7434 : {
7435 2 : if (poOldSRS)
7436 1 : poNewSRS->SetCoordinateEpoch(
7437 : poOldSRS->GetCoordinateEpoch());
7438 : }
7439 : }
7440 : }
7441 1 : else if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_COORD_EPOCH_FLAG) != 0)
7442 : {
7443 1 : if (poOldSRS != nullptr)
7444 : {
7445 1 : poNewSRS.reset(poOldSRS->Clone());
7446 1 : if (poNewSRSRef)
7447 1 : poNewSRS->SetCoordinateEpoch(
7448 : poNewSRSRef->GetCoordinateEpoch());
7449 : }
7450 : }
7451 :
7452 4 : const char *const apszOptions[] = {
7453 : "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
7454 4 : if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
7455 10 : (poOldSRS != nullptr && poNewSRS == nullptr) ||
7456 2 : (poOldSRS != nullptr && poNewSRS != nullptr &&
7457 2 : !poOldSRS->IsSame(poNewSRS.get(), apszOptions)))
7458 : {
7459 : // Temporary remove foreign key checks
7460 : const GPKGTemporaryForeignKeyCheckDisabler
7461 4 : oGPKGTemporaryForeignKeyCheckDisabler(m_poDS);
7462 :
7463 4 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7464 0 : return OGRERR_FAILURE;
7465 :
7466 4 : const int nNewSRID = m_poDS->GetSrsId(poNewSRS.get());
7467 :
7468 : // Replace the old SRID by the new ones in geometry blobs
7469 4 : int32_t nNewSRID_LSB = nNewSRID;
7470 4 : CPL_LSBPTR32(&nNewSRID_LSB);
7471 4 : GByte abySRID_LSB[5] = {0, 0, 0, 0};
7472 4 : memcpy(abySRID_LSB, &nNewSRID_LSB, 4);
7473 4 : char *pszSRID_LSB_HEX = CPLBinaryToHex(4, abySRID_LSB);
7474 :
7475 4 : int32_t nNewSRID_MSB = nNewSRID;
7476 4 : CPL_MSBPTR32(&nNewSRID_MSB);
7477 4 : GByte abySRID_MSB[5] = {0, 0, 0, 0};
7478 4 : memcpy(abySRID_MSB, &nNewSRID_MSB, 4);
7479 4 : char *pszSRID_MSB_HEX = CPLBinaryToHex(4, abySRID_MSB);
7480 :
7481 : // Black magic below...
7482 : // the substr(hex(...) IN ('0','2',...'E') checks if bit 0 of the
7483 : // 4th byte is 0 and use that to decide how to replace the old SRID
7484 : // by the new one.
7485 4 : CPLString osSQL;
7486 : osSQL.Printf(
7487 : "UPDATE \"%s\" SET \"%s\" = "
7488 : "CAST(substr(\"%s\", 1, 4) || "
7489 : "(CASE WHEN substr(hex(substr(\"%s\", 4, 1)),2) IN "
7490 : "('0','2','4','6','8','A','C','E') "
7491 : "THEN x'%s' ELSE x'%s' END) || substr(\"%s\", 9) AS BLOB) "
7492 : "WHERE \"%s\" IS NOT NULL",
7493 8 : SQLEscapeName(m_pszTableName).c_str(),
7494 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7495 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7496 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7497 : pszSRID_MSB_HEX, pszSRID_LSB_HEX,
7498 8 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str(),
7499 28 : SQLEscapeName(poGeomFieldDefn->GetNameRef()).c_str());
7500 4 : OGRErr eErr = SQLCommand(m_poDS->GetDB(), osSQL.c_str());
7501 4 : CPLFree(pszSRID_MSB_HEX);
7502 4 : CPLFree(pszSRID_LSB_HEX);
7503 4 : if (eErr != OGRERR_NONE)
7504 : {
7505 0 : m_poDS->SoftRollbackTransaction();
7506 0 : return OGRERR_FAILURE;
7507 : }
7508 :
7509 4 : char *pszSQL = sqlite3_mprintf(
7510 : "UPDATE gpkg_contents SET srs_id = %d WHERE table_name = '%q'",
7511 : nNewSRID, m_pszTableName);
7512 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7513 4 : sqlite3_free(pszSQL);
7514 4 : if (eErr != OGRERR_NONE)
7515 : {
7516 0 : m_poDS->SoftRollbackTransaction();
7517 0 : return OGRERR_FAILURE;
7518 : }
7519 :
7520 4 : pszSQL = sqlite3_mprintf(
7521 : "UPDATE gpkg_geometry_columns SET srs_id = %d WHERE "
7522 : "table_name = '%q' AND column_name = '%q'",
7523 : nNewSRID, m_pszTableName, poGeomFieldDefn->GetNameRef());
7524 4 : eErr = SQLCommand(m_poDS->GetDB(), pszSQL);
7525 4 : sqlite3_free(pszSQL);
7526 4 : if (eErr != OGRERR_NONE)
7527 : {
7528 0 : m_poDS->SoftRollbackTransaction();
7529 0 : return OGRERR_FAILURE;
7530 : }
7531 :
7532 4 : if (m_poDS->SoftCommitTransaction() != OGRERR_NONE)
7533 : {
7534 0 : return OGRERR_FAILURE;
7535 : }
7536 :
7537 4 : m_iSrs = nNewSRID;
7538 4 : OGRSpatialReference *poSRS = poNewSRS.release();
7539 4 : poGeomFieldDefn->SetSpatialRef(poSRS);
7540 4 : if (poSRS)
7541 3 : poSRS->Release();
7542 : }
7543 : }
7544 :
7545 8 : return OGRERR_NONE;
7546 : }
7547 :
7548 : /************************************************************************/
7549 : /* ReorderFields() */
7550 : /************************************************************************/
7551 :
7552 4 : OGRErr OGRGeoPackageTableLayer::ReorderFields(int *panMap)
7553 : {
7554 4 : if (!m_bFeatureDefnCompleted)
7555 0 : GetLayerDefn();
7556 4 : if (!CheckUpdatableTable("ReorderFields"))
7557 2 : return OGRERR_FAILURE;
7558 :
7559 2 : if (m_poFeatureDefn->GetFieldCount() == 0)
7560 0 : return OGRERR_NONE;
7561 :
7562 2 : OGRErr eErr = OGRCheckPermutation(panMap, m_poFeatureDefn->GetFieldCount());
7563 2 : if (eErr != OGRERR_NONE)
7564 1 : return eErr;
7565 :
7566 : /* -------------------------------------------------------------------- */
7567 : /* Deferred actions, reset state. */
7568 : /* -------------------------------------------------------------------- */
7569 1 : ResetReading();
7570 1 : RunDeferredCreationIfNecessary();
7571 1 : if (m_bThreadRTreeStarted)
7572 0 : CancelAsyncRTree();
7573 1 : if (!RunDeferredSpatialIndexUpdate())
7574 0 : return OGRERR_FAILURE;
7575 :
7576 : /* -------------------------------------------------------------------- */
7577 : /* Drop any iterator since we change the DB structure */
7578 : /* -------------------------------------------------------------------- */
7579 1 : m_poDS->ResetReadingAllLayers();
7580 :
7581 : /* -------------------------------------------------------------------- */
7582 : /* Build list of old fields, and the list of new fields. */
7583 : /* -------------------------------------------------------------------- */
7584 2 : std::vector<OGRFieldDefn *> apoFields;
7585 3 : for (int iField = 0; iField < m_poFeatureDefn->GetFieldCount(); iField++)
7586 : {
7587 : OGRFieldDefn *poFieldDefn =
7588 2 : m_poFeatureDefn->GetFieldDefn(panMap[iField]);
7589 2 : apoFields.push_back(poFieldDefn);
7590 : }
7591 :
7592 2 : const CPLString osFieldListForSelect(BuildSelectFieldList(apoFields));
7593 2 : const CPLString osColumnsForCreate(GetColumnsOfCreateTable(apoFields));
7594 :
7595 : /* -------------------------------------------------------------------- */
7596 : /* Recreate table in a transaction */
7597 : /* -------------------------------------------------------------------- */
7598 1 : if (m_poDS->SoftStartTransaction() != OGRERR_NONE)
7599 0 : return OGRERR_FAILURE;
7600 :
7601 1 : eErr = RecreateTable(osColumnsForCreate, osFieldListForSelect);
7602 :
7603 : /* -------------------------------------------------------------------- */
7604 : /* Finish */
7605 : /* -------------------------------------------------------------------- */
7606 1 : if (eErr == OGRERR_NONE)
7607 : {
7608 1 : eErr = m_poDS->SoftCommitTransaction();
7609 :
7610 1 : if (eErr == OGRERR_NONE)
7611 : {
7612 1 : eErr = whileUnsealing(m_poFeatureDefn)->ReorderFieldDefns(panMap);
7613 : }
7614 :
7615 1 : if (eErr == OGRERR_NONE)
7616 : {
7617 : // We have recreated the table from scratch, and lost the
7618 : // generated column property
7619 1 : std::fill(m_abGeneratedColumns.begin(), m_abGeneratedColumns.end(),
7620 2 : false);
7621 : }
7622 :
7623 1 : ResetReading();
7624 : }
7625 : else
7626 : {
7627 0 : m_poDS->SoftRollbackTransaction();
7628 : }
7629 :
7630 1 : return eErr;
7631 : }
7632 :
7633 : /************************************************************************/
7634 : /* OGR_GPKG_GeometryTypeAggregate() */
7635 : /************************************************************************/
7636 :
7637 : namespace
7638 : {
7639 : struct GeometryTypeAggregateContext
7640 : {
7641 : sqlite3 *m_hDB = nullptr;
7642 : int m_nFlags = 0;
7643 : bool m_bIsGeometryTypeAggregateInterrupted = false;
7644 : std::map<OGRwkbGeometryType, int64_t> m_oMapCount{};
7645 : std::set<OGRwkbGeometryType> m_oSetNotNull{};
7646 :
7647 14 : explicit GeometryTypeAggregateContext(sqlite3 *hDB, int nFlags)
7648 14 : : m_hDB(hDB), m_nFlags(nFlags)
7649 : {
7650 14 : }
7651 :
7652 : GeometryTypeAggregateContext(const GeometryTypeAggregateContext &) = delete;
7653 : GeometryTypeAggregateContext &
7654 : operator=(const GeometryTypeAggregateContext &) = delete;
7655 :
7656 2 : void SetGeometryTypeAggregateInterrupted(bool b)
7657 : {
7658 2 : m_bIsGeometryTypeAggregateInterrupted = b;
7659 2 : if (b)
7660 2 : sqlite3_interrupt(m_hDB);
7661 2 : }
7662 : };
7663 :
7664 : } // namespace
7665 :
7666 1376 : static void OGR_GPKG_GeometryTypeAggregate_Step(sqlite3_context *pContext,
7667 : int /*argc*/,
7668 : sqlite3_value **argv)
7669 : {
7670 : const GByte *pabyBLOB =
7671 1376 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
7672 :
7673 : auto poContext = static_cast<GeometryTypeAggregateContext *>(
7674 1376 : sqlite3_user_data(pContext));
7675 :
7676 1376 : OGRwkbGeometryType eGeometryType = wkbNone;
7677 1376 : OGRErr err = OGRERR_FAILURE;
7678 1376 : if (pabyBLOB != nullptr)
7679 : {
7680 : GPkgHeader sHeader;
7681 1353 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
7682 2706 : if (GPkgHeaderFromWKB(pabyBLOB, nBLOBLen, &sHeader) == OGRERR_NONE &&
7683 1353 : static_cast<size_t>(nBLOBLen) >= sHeader.nHeaderLen + 5)
7684 : {
7685 1353 : err = OGRReadWKBGeometryType(pabyBLOB + sHeader.nHeaderLen,
7686 : wkbVariantIso, &eGeometryType);
7687 1353 : if (eGeometryType == wkbGeometryCollection25D &&
7688 4 : (poContext->m_nFlags & OGR_GGT_GEOMCOLLECTIONZ_TINZ) != 0)
7689 : {
7690 : auto poGeom = std::unique_ptr<OGRGeometry>(
7691 2 : GPkgGeometryToOGR(pabyBLOB, nBLOBLen, nullptr));
7692 1 : if (poGeom)
7693 : {
7694 1 : const auto poGC = poGeom->toGeometryCollection();
7695 1 : if (poGC->getNumGeometries() > 0)
7696 : {
7697 : auto eSubGeomType =
7698 1 : poGC->getGeometryRef(0)->getGeometryType();
7699 1 : if (eSubGeomType == wkbTINZ)
7700 1 : eGeometryType = wkbTINZ;
7701 : }
7702 : }
7703 : }
7704 : }
7705 : }
7706 : else
7707 : {
7708 : // NULL geometry
7709 23 : err = OGRERR_NONE;
7710 : }
7711 1376 : if (err == OGRERR_NONE)
7712 : {
7713 1376 : ++poContext->m_oMapCount[eGeometryType];
7714 1376 : if (eGeometryType != wkbNone &&
7715 1353 : (poContext->m_nFlags & OGR_GGT_STOP_IF_MIXED) != 0)
7716 : {
7717 4 : poContext->m_oSetNotNull.insert(eGeometryType);
7718 4 : if (poContext->m_oSetNotNull.size() == 2)
7719 : {
7720 2 : poContext->SetGeometryTypeAggregateInterrupted(true);
7721 : }
7722 : }
7723 : }
7724 1376 : }
7725 :
7726 11 : static void OGR_GPKG_GeometryTypeAggregate_Finalize(sqlite3_context *)
7727 : {
7728 11 : }
7729 :
7730 : /************************************************************************/
7731 : /* GetGeometryTypes() */
7732 : /************************************************************************/
7733 :
7734 15 : OGRGeometryTypeCounter *OGRGeoPackageTableLayer::GetGeometryTypes(
7735 : int iGeomField, int nFlagsGGT, int &nEntryCountOut,
7736 : GDALProgressFunc pfnProgress, void *pProgressData)
7737 : {
7738 15 : OGRFeatureDefn *poDefn = GetLayerDefn();
7739 :
7740 : /* -------------------------------------------------------------------- */
7741 : /* Deferred actions, reset state. */
7742 : /* -------------------------------------------------------------------- */
7743 15 : RunDeferredCreationIfNecessary();
7744 15 : if (!RunDeferredSpatialIndexUpdate())
7745 : {
7746 0 : nEntryCountOut = 0;
7747 0 : return nullptr;
7748 : }
7749 :
7750 15 : const int nGeomFieldCount = poDefn->GetGeomFieldCount();
7751 15 : if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
7752 : {
7753 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
7754 1 : nEntryCountOut = 0;
7755 1 : return nullptr;
7756 : }
7757 :
7758 : #ifdef SQLITE_HAS_PROGRESS_HANDLER
7759 : struct CancelCallback
7760 : {
7761 : sqlite3 *m_hDB = nullptr;
7762 : GDALProgressFunc m_pfnProgress = nullptr;
7763 : void *m_pProgressData = nullptr;
7764 :
7765 14 : CancelCallback(sqlite3 *hDB, GDALProgressFunc pfnProgressIn,
7766 : void *pProgressDataIn)
7767 14 : : m_hDB(hDB),
7768 14 : m_pfnProgress(pfnProgressIn != GDALDummyProgress ? pfnProgressIn
7769 : : nullptr),
7770 14 : m_pProgressData(pProgressDataIn)
7771 : {
7772 14 : if (m_pfnProgress)
7773 : {
7774 : // If changing that value, update
7775 : // ogr_gpkg.py::test_ogr_gpkg_get_geometry_types
7776 2 : constexpr int COUNT_VM_INSTRUCTIONS = 1000;
7777 2 : sqlite3_progress_handler(m_hDB, COUNT_VM_INSTRUCTIONS,
7778 : ProgressHandler, this);
7779 : }
7780 14 : }
7781 :
7782 14 : ~CancelCallback()
7783 14 : {
7784 14 : if (m_pfnProgress)
7785 : {
7786 2 : sqlite3_progress_handler(m_hDB, 0, nullptr, nullptr);
7787 : }
7788 14 : }
7789 :
7790 : CancelCallback(const CancelCallback &) = delete;
7791 : CancelCallback &operator=(const CancelCallback &) = delete;
7792 :
7793 1 : static int ProgressHandler(void *pData)
7794 : {
7795 1 : CancelCallback *psCancelCallback =
7796 : static_cast<CancelCallback *>(pData);
7797 1 : return psCancelCallback->m_pfnProgress != nullptr &&
7798 1 : psCancelCallback->m_pfnProgress(
7799 : 0.0, "", psCancelCallback->m_pProgressData)
7800 2 : ? 0
7801 1 : : 1;
7802 : }
7803 : };
7804 :
7805 28 : CancelCallback oCancelCallback(m_poDS->hDB, pfnProgress, pProgressData);
7806 : #else
7807 : CPL_IGNORE_RET_VAL(pfnProgress);
7808 : CPL_IGNORE_RET_VAL(pProgressData);
7809 : #endif
7810 :
7811 : // For internal use only
7812 :
7813 28 : GeometryTypeAggregateContext sContext(m_poDS->hDB, nFlagsGGT);
7814 :
7815 28 : CPLString osFuncName;
7816 14 : osFuncName.Printf("OGR_GPKG_GeometryTypeAggregate_INTERNAL_%p", &sContext);
7817 :
7818 14 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
7819 : &sContext, nullptr,
7820 : OGR_GPKG_GeometryTypeAggregate_Step,
7821 : OGR_GPKG_GeometryTypeAggregate_Finalize);
7822 :
7823 : // Using this aggregate function is slightly faster than using
7824 : // sqlite3_step() to loop over each geometry blob (650 ms vs 750ms on a 1.6
7825 : // GB db with 3.3 million features)
7826 28 : char *pszSQL = sqlite3_mprintf(
7827 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
7828 14 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
7829 30 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
7830 14 : char *pszErrMsg = nullptr;
7831 : const int rc =
7832 14 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
7833 :
7834 : // Delete function
7835 14 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
7836 : nullptr, nullptr, nullptr, nullptr);
7837 :
7838 14 : if (rc != SQLITE_OK && !sContext.m_bIsGeometryTypeAggregateInterrupted)
7839 : {
7840 1 : if (rc != SQLITE_INTERRUPT)
7841 : {
7842 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
7843 : pszSQL, pszErrMsg);
7844 : }
7845 1 : sqlite3_free(pszErrMsg);
7846 1 : sqlite3_free(pszSQL);
7847 1 : nEntryCountOut = 0;
7848 1 : return nullptr;
7849 : }
7850 13 : sqlite3_free(pszErrMsg);
7851 13 : sqlite3_free(pszSQL);
7852 :
7853 : // Format result
7854 13 : nEntryCountOut = static_cast<int>(sContext.m_oMapCount.size());
7855 : OGRGeometryTypeCounter *pasRet = static_cast<OGRGeometryTypeCounter *>(
7856 13 : CPLCalloc(1 + nEntryCountOut, sizeof(OGRGeometryTypeCounter)));
7857 13 : int i = 0;
7858 47 : for (const auto &sEntry : sContext.m_oMapCount)
7859 : {
7860 34 : pasRet[i].eGeomType = sEntry.first;
7861 34 : pasRet[i].nCount = sEntry.second;
7862 34 : ++i;
7863 : }
7864 13 : return pasRet;
7865 : }
7866 :
7867 : /************************************************************************/
7868 : /* OGR_GPKG_FillArrowArray_Step() */
7869 : /************************************************************************/
7870 :
7871 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7872 : sqlite3_value **argv);
7873 :
7874 64585 : void OGR_GPKG_FillArrowArray_Step(sqlite3_context *pContext, int /*argc*/,
7875 : sqlite3_value **argv)
7876 : {
7877 : auto psFillArrowArray = static_cast<OGRGPKGTableLayerFillArrowArray *>(
7878 64585 : sqlite3_user_data(pContext));
7879 :
7880 : {
7881 64585 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7882 129170 : if (psFillArrowArray->nCountRows >=
7883 64585 : psFillArrowArray->psHelper->m_nMaxBatchSize)
7884 : {
7885 6 : if (psFillArrowArray->bAsynchronousMode)
7886 : {
7887 6 : psFillArrowArray->psHelper->Shrink(
7888 : psFillArrowArray->nCountRows);
7889 6 : psFillArrowArray->oCV.notify_one();
7890 12 : while (psFillArrowArray->nCountRows > 0)
7891 : {
7892 6 : psFillArrowArray->oCV.wait(oLock);
7893 : }
7894 : // Note that psFillArrowArray->psHelper.get() will generally now be
7895 : // different from before the wait()
7896 : }
7897 : else
7898 : {
7899 : // should not happen !
7900 : psFillArrowArray->osErrorMsg = "OGR_GPKG_FillArrowArray_Step() "
7901 0 : "got more rows than expected!";
7902 0 : sqlite3_interrupt(psFillArrowArray->hDB);
7903 0 : psFillArrowArray->bErrorOccurred = true;
7904 0 : return;
7905 : }
7906 : }
7907 64585 : if (psFillArrowArray->nCountRows < 0)
7908 2 : return;
7909 : }
7910 :
7911 64583 : if (psFillArrowArray->nMemLimit == 0)
7912 128 : psFillArrowArray->nMemLimit = OGRArrowArrayHelper::GetMemLimit();
7913 64583 : const auto nMemLimit = psFillArrowArray->nMemLimit;
7914 : const int SQLITE_MAX_FUNCTION_ARG =
7915 64583 : sqlite3_limit(psFillArrowArray->hDB, SQLITE_LIMIT_FUNCTION_ARG, -1);
7916 64583 : const bool bDateTimeAsString = psFillArrowArray->bDateTimeAsString;
7917 64704 : begin:
7918 : int iFeat;
7919 : OGRArrowArrayHelper *psHelper;
7920 : {
7921 129408 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
7922 64704 : iFeat = psFillArrowArray->nCountRows;
7923 64704 : psHelper = psFillArrowArray->psHelper.get();
7924 : }
7925 64704 : int iCol = 0;
7926 64704 : const int iFieldStart = sqlite3_value_int(argv[iCol]);
7927 64704 : ++iCol;
7928 64704 : int iField = std::max(0, iFieldStart);
7929 :
7930 : GIntBig nFID;
7931 64704 : if (iFieldStart < 0)
7932 : {
7933 64698 : nFID = sqlite3_value_int64(argv[iCol]);
7934 64698 : iCol++;
7935 64698 : if (psHelper->m_panFIDValues)
7936 : {
7937 64689 : psHelper->m_panFIDValues[iFeat] = nFID;
7938 : }
7939 64698 : psFillArrowArray->nCurFID = nFID;
7940 : }
7941 : else
7942 : {
7943 6 : nFID = psFillArrowArray->nCurFID;
7944 : }
7945 :
7946 129397 : if (iFieldStart < 0 && !psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
7947 64693 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
7948 : {
7949 64690 : const int iArrowField = psHelper->m_mapOGRGeomFieldToArrowField[0];
7950 64690 : auto psArray = psHelper->m_out_array->children[iArrowField];
7951 64690 : size_t nWKBSize = 0;
7952 64690 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
7953 64690 : if (nSqlite3ColType == SQLITE_BLOB)
7954 : {
7955 : GPkgHeader oHeader;
7956 64306 : memset(&oHeader, 0, sizeof(oHeader));
7957 :
7958 64306 : const GByte *pabyWkb = nullptr;
7959 64306 : const int nBlobSize = sqlite3_value_bytes(argv[iCol]);
7960 : // coverity[tainted_data_return]
7961 : const GByte *pabyBlob =
7962 64306 : static_cast<const GByte *>(sqlite3_value_blob(argv[iCol]));
7963 64306 : std::vector<GByte> abyWkb;
7964 64306 : if (nBlobSize >= 8 && pabyBlob && pabyBlob[0] == 'G' &&
7965 64306 : pabyBlob[1] == 'P')
7966 : {
7967 64306 : if (psFillArrowArray->poLayer->m_bUndoDiscardCoordLSBOnReading)
7968 : {
7969 : OGRGeometry *poGeomPtr =
7970 1 : GPkgGeometryToOGR(pabyBlob, nBlobSize, nullptr);
7971 1 : if (poGeomPtr)
7972 : {
7973 1 : poGeomPtr->roundCoordinates(
7974 1 : psFillArrowArray->poFeatureDefn->GetGeomFieldDefn(0)
7975 : ->GetCoordinatePrecision());
7976 1 : nWKBSize = poGeomPtr->WkbSize();
7977 1 : abyWkb.resize(nWKBSize);
7978 1 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
7979 1 : wkbVariantIso) !=
7980 : OGRERR_NONE)
7981 : {
7982 0 : nWKBSize = 0;
7983 : }
7984 : else
7985 : {
7986 1 : pabyWkb = abyWkb.data();
7987 : }
7988 1 : delete poGeomPtr;
7989 : }
7990 : }
7991 : else
7992 : {
7993 : /* Read header */
7994 : OGRErr err =
7995 64305 : GPkgHeaderFromWKB(pabyBlob, nBlobSize, &oHeader);
7996 64305 : if (err == OGRERR_NONE)
7997 : {
7998 : /* WKB pointer */
7999 64305 : pabyWkb = pabyBlob + oHeader.nHeaderLen;
8000 64305 : nWKBSize = nBlobSize - oHeader.nHeaderLen;
8001 : }
8002 64306 : }
8003 : }
8004 0 : else if (nBlobSize > 0 && pabyBlob)
8005 : {
8006 : // Try also spatialite geometry blobs, although that is
8007 : // not really expected...
8008 0 : OGRGeometry *poGeomPtr = nullptr;
8009 0 : if (OGRSQLiteImportSpatiaLiteGeometry(
8010 0 : pabyBlob, nBlobSize, &poGeomPtr) != OGRERR_NONE)
8011 : {
8012 0 : CPLError(CE_Failure, CPLE_AppDefined,
8013 : "Unable to read geometry");
8014 : }
8015 : else
8016 : {
8017 0 : nWKBSize = poGeomPtr->WkbSize();
8018 0 : abyWkb.resize(nWKBSize);
8019 0 : if (poGeomPtr->exportToWkb(wkbNDR, abyWkb.data(),
8020 0 : wkbVariantIso) != OGRERR_NONE)
8021 : {
8022 0 : nWKBSize = 0;
8023 : }
8024 : else
8025 : {
8026 0 : pabyWkb = abyWkb.data();
8027 : }
8028 : }
8029 0 : delete poGeomPtr;
8030 : }
8031 :
8032 64306 : if (nWKBSize != 0)
8033 : {
8034 : // Deal with spatial filter
8035 64306 : if (psFillArrowArray->poLayerForFilterGeom)
8036 : {
8037 33 : OGREnvelope sEnvelope;
8038 33 : bool bEnvelopeAlreadySet = false;
8039 33 : if (oHeader.bEmpty)
8040 : {
8041 0 : bEnvelopeAlreadySet = true;
8042 : }
8043 33 : else if (oHeader.bExtentHasXY)
8044 : {
8045 31 : bEnvelopeAlreadySet = true;
8046 31 : sEnvelope.MinX = oHeader.MinX;
8047 31 : sEnvelope.MinY = oHeader.MinY;
8048 31 : sEnvelope.MaxX = oHeader.MaxX;
8049 31 : sEnvelope.MaxY = oHeader.MaxY;
8050 : }
8051 :
8052 66 : if (!psFillArrowArray->poLayerForFilterGeom
8053 33 : ->FilterWKBGeometry(pabyWkb, nWKBSize,
8054 : bEnvelopeAlreadySet,
8055 : sEnvelope))
8056 : {
8057 2 : return;
8058 : }
8059 : }
8060 :
8061 64304 : if (iFeat > 0)
8062 : {
8063 64158 : auto panOffsets = static_cast<int32_t *>(
8064 64158 : const_cast<void *>(psArray->buffers[1]));
8065 64158 : const uint32_t nCurLength =
8066 64158 : static_cast<uint32_t>(panOffsets[iFeat]);
8067 64158 : if (nWKBSize <= nMemLimit &&
8068 64158 : nWKBSize > nMemLimit - nCurLength)
8069 : {
8070 52 : CPLDebug("GPKG",
8071 : "OGR_GPKG_FillArrowArray_Step(): premature "
8072 : "notification of %d features to consumer due "
8073 : "to too big array",
8074 : iFeat);
8075 52 : psFillArrowArray->bMemoryLimitReached = true;
8076 52 : if (psFillArrowArray->bAsynchronousMode)
8077 : {
8078 : std::unique_lock<std::mutex> oLock(
8079 47 : psFillArrowArray->oMutex);
8080 47 : psFillArrowArray->psHelper->Shrink(
8081 : psFillArrowArray->nCountRows);
8082 47 : psFillArrowArray->oCV.notify_one();
8083 94 : while (psFillArrowArray->nCountRows > 0)
8084 : {
8085 47 : psFillArrowArray->oCV.wait(oLock);
8086 : }
8087 47 : goto begin;
8088 : }
8089 : else
8090 : {
8091 5 : sqlite3_interrupt(psFillArrowArray->hDB);
8092 5 : return;
8093 : }
8094 : }
8095 : }
8096 :
8097 64252 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8098 : iArrowField, iFeat, nWKBSize);
8099 64252 : if (outPtr == nullptr)
8100 : {
8101 0 : goto error;
8102 : }
8103 64252 : memcpy(outPtr, pabyWkb, nWKBSize);
8104 : }
8105 : else
8106 : {
8107 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8108 : }
8109 : }
8110 :
8111 64636 : if (nWKBSize == 0)
8112 : {
8113 384 : if (!psHelper->SetNull(iArrowField, iFeat))
8114 : {
8115 0 : goto error;
8116 : }
8117 : }
8118 64636 : iCol++;
8119 : }
8120 :
8121 1267150 : for (; iField < psHelper->m_nFieldCount; iField++)
8122 : {
8123 1202590 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8124 1202590 : if (iArrowField < 0)
8125 33 : continue;
8126 1202560 : if (iCol == SQLITE_MAX_FUNCTION_ARG)
8127 6 : break;
8128 :
8129 : const OGRFieldDefn *poFieldDefn =
8130 1202550 : psFillArrowArray->poFeatureDefn->GetFieldDefnUnsafe(iField);
8131 :
8132 1202550 : auto psArray = psHelper->m_out_array->children[iArrowField];
8133 :
8134 1202550 : const int nSqlite3ColType = sqlite3_value_type(argv[iCol]);
8135 1202550 : if (nSqlite3ColType == SQLITE_NULL)
8136 : {
8137 784 : if (!psHelper->SetNull(iArrowField, iFeat))
8138 : {
8139 0 : goto error;
8140 : }
8141 784 : iCol++;
8142 784 : continue;
8143 : }
8144 :
8145 1201760 : switch (poFieldDefn->GetType())
8146 : {
8147 1027 : case OFTInteger:
8148 : {
8149 1027 : const int nVal = sqlite3_value_int(argv[iCol]);
8150 1027 : if (poFieldDefn->GetSubType() == OFSTBoolean)
8151 : {
8152 122 : if (nVal != 0)
8153 : {
8154 91 : psHelper->SetBoolOn(psArray, iFeat);
8155 : }
8156 : }
8157 905 : else if (poFieldDefn->GetSubType() == OFSTInt16)
8158 : {
8159 59 : psHelper->SetInt16(psArray, iFeat,
8160 : static_cast<int16_t>(nVal));
8161 : }
8162 : else
8163 : {
8164 846 : psHelper->SetInt32(psArray, iFeat, nVal);
8165 : }
8166 1027 : break;
8167 : }
8168 :
8169 61 : case OFTInteger64:
8170 : {
8171 61 : psHelper->SetInt64(psArray, iFeat,
8172 61 : sqlite3_value_int64(argv[iCol]));
8173 61 : break;
8174 : }
8175 :
8176 152 : case OFTReal:
8177 : {
8178 152 : const double dfVal = sqlite3_value_double(argv[iCol]);
8179 152 : if (poFieldDefn->GetSubType() == OFSTFloat32)
8180 : {
8181 51 : psHelper->SetFloat(psArray, iFeat,
8182 : static_cast<float>(dfVal));
8183 : }
8184 : else
8185 : {
8186 101 : psHelper->SetDouble(psArray, iFeat, dfVal);
8187 : }
8188 152 : break;
8189 : }
8190 :
8191 176 : case OFTBinary:
8192 : {
8193 : const uint32_t nBytes =
8194 176 : static_cast<uint32_t>(sqlite3_value_bytes(argv[iCol]));
8195 : // coverity[tainted_data_return]
8196 176 : const void *pabyData = sqlite3_value_blob(argv[iCol]);
8197 176 : if (pabyData != nullptr || nBytes == 0)
8198 : {
8199 176 : if (iFeat > 0)
8200 : {
8201 119 : auto panOffsets = static_cast<int32_t *>(
8202 119 : const_cast<void *>(psArray->buffers[1]));
8203 119 : const uint32_t nCurLength =
8204 119 : static_cast<uint32_t>(panOffsets[iFeat]);
8205 119 : if (nBytes <= nMemLimit &&
8206 119 : nBytes > nMemLimit - nCurLength)
8207 : {
8208 41 : CPLDebug("GPKG",
8209 : "OGR_GPKG_FillArrowArray_Step(): "
8210 : "premature notification of %d features to "
8211 : "consumer due to too big array",
8212 : iFeat);
8213 41 : psFillArrowArray->bMemoryLimitReached = true;
8214 41 : if (psFillArrowArray->bAsynchronousMode)
8215 : {
8216 : std::unique_lock<std::mutex> oLock(
8217 37 : psFillArrowArray->oMutex);
8218 37 : psFillArrowArray->psHelper->Shrink(
8219 : psFillArrowArray->nCountRows);
8220 37 : psFillArrowArray->oCV.notify_one();
8221 74 : while (psFillArrowArray->nCountRows > 0)
8222 : {
8223 37 : psFillArrowArray->oCV.wait(oLock);
8224 : }
8225 37 : goto begin;
8226 : }
8227 : else
8228 : {
8229 4 : sqlite3_interrupt(psFillArrowArray->hDB);
8230 4 : return;
8231 : }
8232 : }
8233 : }
8234 :
8235 135 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8236 : iArrowField, iFeat, nBytes);
8237 135 : if (outPtr == nullptr)
8238 : {
8239 0 : goto error;
8240 : }
8241 135 : if (nBytes)
8242 135 : memcpy(outPtr, pabyData, nBytes);
8243 : }
8244 : else
8245 : {
8246 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8247 : }
8248 135 : break;
8249 : }
8250 :
8251 51 : case OFTDate:
8252 : {
8253 : OGRField ogrField;
8254 : const auto pszTxt = reinterpret_cast<const char *>(
8255 51 : sqlite3_value_text(argv[iCol]));
8256 102 : if (pszTxt != nullptr &&
8257 51 : psFillArrowArray->poLayer->ParseDateField(
8258 : pszTxt, &ogrField, poFieldDefn, nFID))
8259 : {
8260 51 : psHelper->SetDate(psArray, iFeat,
8261 51 : psFillArrowArray->brokenDown, ogrField);
8262 : }
8263 51 : break;
8264 : }
8265 :
8266 65 : case OFTDateTime:
8267 : {
8268 65 : if (!bDateTimeAsString)
8269 : {
8270 : OGRField ogrField;
8271 : const auto pszTxt = reinterpret_cast<const char *>(
8272 55 : sqlite3_value_text(argv[iCol]));
8273 110 : if (pszTxt != nullptr &&
8274 55 : psFillArrowArray->poLayer->ParseDateTimeField(
8275 : pszTxt, &ogrField, poFieldDefn, nFID))
8276 : {
8277 55 : psHelper->SetDateTime(
8278 55 : psArray, iFeat, psFillArrowArray->brokenDown,
8279 55 : psHelper->m_anTZFlags[iField], ogrField);
8280 : }
8281 55 : break;
8282 : }
8283 : else
8284 : {
8285 : [[fallthrough]];
8286 : }
8287 : }
8288 :
8289 : case OFTString:
8290 : {
8291 : const auto pszTxt = reinterpret_cast<const char *>(
8292 1200240 : sqlite3_value_text(argv[iCol]));
8293 1200240 : if (pszTxt != nullptr)
8294 : {
8295 1200240 : const size_t nBytes = strlen(pszTxt);
8296 1200240 : if (iFeat > 0)
8297 : {
8298 1200130 : auto panOffsets = static_cast<int32_t *>(
8299 1200130 : const_cast<void *>(psArray->buffers[1]));
8300 1200130 : const uint32_t nCurLength =
8301 1200130 : static_cast<uint32_t>(panOffsets[iFeat]);
8302 1200130 : if (nBytes <= nMemLimit &&
8303 1200130 : nBytes > nMemLimit - nCurLength)
8304 : {
8305 41 : CPLDebug("GPKG",
8306 : "OGR_GPKG_FillArrowArray_Step(): "
8307 : "premature notification of %d features to "
8308 : "consumer due to too big array",
8309 : iFeat);
8310 41 : psFillArrowArray->bMemoryLimitReached = true;
8311 41 : if (psFillArrowArray->bAsynchronousMode)
8312 : {
8313 : std::unique_lock<std::mutex> oLock(
8314 37 : psFillArrowArray->oMutex);
8315 37 : psFillArrowArray->psHelper->Shrink(
8316 : psFillArrowArray->nCountRows);
8317 37 : psFillArrowArray->oCV.notify_one();
8318 74 : while (psFillArrowArray->nCountRows > 0)
8319 : {
8320 37 : psFillArrowArray->oCV.wait(oLock);
8321 : }
8322 37 : goto begin;
8323 : }
8324 : else
8325 : {
8326 4 : sqlite3_interrupt(psFillArrowArray->hDB);
8327 4 : return;
8328 : }
8329 : }
8330 : }
8331 :
8332 1200200 : GByte *outPtr = psHelper->GetPtrForStringOrBinary(
8333 : iArrowField, iFeat, nBytes);
8334 1200200 : if (outPtr == nullptr)
8335 : {
8336 0 : goto error;
8337 : }
8338 1200200 : if (nBytes)
8339 1200200 : memcpy(outPtr, pszTxt, nBytes);
8340 : }
8341 : else
8342 : {
8343 0 : psHelper->SetEmptyStringOrBinary(psArray, iFeat);
8344 : }
8345 1200200 : break;
8346 : }
8347 :
8348 0 : default:
8349 0 : break;
8350 : }
8351 :
8352 1201680 : iCol++;
8353 : }
8354 :
8355 64568 : if (iField == psHelper->m_nFieldCount)
8356 : {
8357 64562 : std::unique_lock<std::mutex> oLock(psFillArrowArray->oMutex);
8358 64562 : psFillArrowArray->nCountRows++;
8359 : }
8360 64568 : return;
8361 :
8362 0 : error:
8363 0 : sqlite3_interrupt(psFillArrowArray->hDB);
8364 0 : psFillArrowArray->bErrorOccurred = true;
8365 : }
8366 :
8367 : /************************************************************************/
8368 : /* OGR_GPKG_FillArrowArray_Finalize() */
8369 : /************************************************************************/
8370 :
8371 137 : static void OGR_GPKG_FillArrowArray_Finalize(sqlite3_context * /*pContext*/)
8372 : {
8373 137 : }
8374 :
8375 : /************************************************************************/
8376 : /* GetNextArrowArrayAsynchronous() */
8377 : /************************************************************************/
8378 :
8379 198 : int OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronous(
8380 : struct ArrowArrayStream *stream, struct ArrowArray *out_array)
8381 : {
8382 198 : memset(out_array, 0, sizeof(*out_array));
8383 :
8384 198 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8385 :
8386 198 : if (m_poFillArrowArray)
8387 : {
8388 149 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8389 149 : if (m_poFillArrowArray->bIsFinished)
8390 : {
8391 24 : return 0;
8392 : }
8393 : }
8394 :
8395 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
8396 348 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
8397 174 : if (out_array->release == nullptr)
8398 : {
8399 0 : return ENOMEM;
8400 : }
8401 :
8402 174 : if (m_poFillArrowArray == nullptr)
8403 : {
8404 : // Check that the total number of arguments passed to
8405 : // OGR_GPKG_FillArrowArray_INTERNAL() doesn't exceed SQLITE_MAX_FUNCTION_ARG
8406 : // If it does, we cannot reliably use GetNextArrowArrayAsynchronous() in
8407 : // the situation where the ArrowArray would exceed the nMemLimit.
8408 : // So be on the safe side, and rely on the base OGRGeoPackageLayer
8409 : // implementation
8410 : const int SQLITE_MAX_FUNCTION_ARG =
8411 49 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
8412 49 : int nCountArgs = 1 // field index
8413 : + 1; // FID column
8414 95 : if (!psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
8415 46 : psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8416 : {
8417 46 : ++nCountArgs;
8418 : }
8419 466 : for (int iField = 0; iField < psHelper->m_nFieldCount; iField++)
8420 : {
8421 419 : const int iArrowField = psHelper->m_mapOGRFieldToArrowField[iField];
8422 419 : if (iArrowField >= 0)
8423 : {
8424 419 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
8425 : {
8426 2 : psHelper.reset();
8427 2 : if (out_array->release)
8428 2 : out_array->release(out_array);
8429 2 : return OGRGeoPackageLayer::GetNextArrowArray(stream,
8430 2 : out_array);
8431 : }
8432 417 : ++nCountArgs;
8433 : }
8434 : }
8435 :
8436 : m_poFillArrowArray =
8437 47 : std::make_unique<OGRGPKGTableLayerFillArrowArray>();
8438 47 : m_poFillArrowArray->psHelper = std::move(psHelper);
8439 47 : m_poFillArrowArray->nCountRows = 0;
8440 47 : m_poFillArrowArray->bErrorOccurred = false;
8441 94 : m_poFillArrowArray->bDateTimeAsString =
8442 47 : m_aosArrowArrayStreamOptions.FetchBool(GAS_OPT_DATETIME_AS_STRING,
8443 : false);
8444 47 : m_poFillArrowArray->poFeatureDefn = m_poFeatureDefn;
8445 47 : m_poFillArrowArray->poLayer = this;
8446 47 : m_poFillArrowArray->hDB = m_poDS->GetDB();
8447 47 : memset(&m_poFillArrowArray->brokenDown, 0,
8448 : sizeof(m_poFillArrowArray->brokenDown));
8449 94 : m_poFillArrowArray->nMaxBatchSize =
8450 47 : OGRArrowArrayHelper::GetMaxFeaturesInBatch(
8451 47 : m_aosArrowArrayStreamOptions);
8452 47 : m_poFillArrowArray->bAsynchronousMode = true;
8453 47 : if (m_poFilterGeom)
8454 10 : m_poFillArrowArray->poLayerForFilterGeom = this;
8455 :
8456 : try
8457 : {
8458 94 : m_oThreadNextArrowArray = std::thread(
8459 94 : [this]() { GetNextArrowArrayAsynchronousWorker(); });
8460 : }
8461 0 : catch (const std::exception &e)
8462 : {
8463 0 : m_poFillArrowArray.reset();
8464 0 : CPLError(CE_Failure, CPLE_AppDefined,
8465 0 : "Cannot start worker thread: %s", e.what());
8466 0 : out_array->release(out_array);
8467 0 : return ENOMEM;
8468 : }
8469 : }
8470 : else
8471 : {
8472 125 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8473 125 : if (m_poFillArrowArray->bErrorOccurred)
8474 : {
8475 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8476 0 : m_poFillArrowArray->osErrorMsg.c_str());
8477 0 : out_array->release(out_array);
8478 0 : return EIO;
8479 : }
8480 :
8481 : // Resume worker thread
8482 125 : m_poFillArrowArray->psHelper = std::move(psHelper);
8483 125 : m_poFillArrowArray->nCountRows = 0;
8484 125 : m_poFillArrowArray->oCV.notify_one();
8485 : }
8486 :
8487 : // Wait for GetNextArrowArrayAsynchronousWorker() /
8488 : // OGR_GPKG_FillArrowArray_Step() to have generated a result set (or an
8489 : // error)
8490 : bool bIsFinished;
8491 : {
8492 172 : std::unique_lock<std::mutex> oLock(m_poFillArrowArray->oMutex);
8493 537 : while (m_poFillArrowArray->nCountRows == 0 &&
8494 193 : !m_poFillArrowArray->bIsFinished)
8495 : {
8496 172 : m_poFillArrowArray->oCV.wait(oLock);
8497 : }
8498 172 : bIsFinished = m_poFillArrowArray->bIsFinished;
8499 : }
8500 :
8501 172 : if (m_poFillArrowArray->bErrorOccurred)
8502 : {
8503 1 : m_oThreadNextArrowArray.join();
8504 1 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8505 1 : m_poFillArrowArray->osErrorMsg.c_str());
8506 1 : m_poFillArrowArray->psHelper->ClearArray();
8507 1 : return EIO;
8508 : }
8509 171 : else if (bIsFinished)
8510 : {
8511 44 : m_oThreadNextArrowArray.join();
8512 : }
8513 :
8514 171 : return 0;
8515 : }
8516 :
8517 : /************************************************************************/
8518 : /* GetNextArrowArrayAsynchronousWorker() */
8519 : /************************************************************************/
8520 :
8521 47 : void OGRGeoPackageTableLayer::GetNextArrowArrayAsynchronousWorker()
8522 : {
8523 47 : sqlite3_create_function(
8524 47 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
8525 47 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, m_poFillArrowArray.get(), nullptr,
8526 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
8527 :
8528 94 : std::string osSQL;
8529 47 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
8530 :
8531 1698 : const auto AddFields = [this, &osSQL]()
8532 : {
8533 51 : if (m_pszFidColumn)
8534 : {
8535 48 : osSQL += "m.\"";
8536 48 : osSQL += SQLEscapeName(m_pszFidColumn);
8537 48 : osSQL += '"';
8538 : }
8539 : else
8540 : {
8541 3 : osSQL += "NULL";
8542 : }
8543 :
8544 51 : if (!m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField
8545 99 : .empty() &&
8546 48 : m_poFillArrowArray->psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
8547 : {
8548 48 : osSQL += ",m.\"";
8549 48 : osSQL += SQLEscapeName(GetGeometryColumn());
8550 48 : osSQL += '"';
8551 : }
8552 236 : for (int iField = 0;
8553 236 : iField < m_poFillArrowArray->psHelper->m_nFieldCount; iField++)
8554 : {
8555 : const int iArrowField =
8556 185 : m_poFillArrowArray->psHelper->m_mapOGRFieldToArrowField[iField];
8557 185 : if (iArrowField >= 0)
8558 : {
8559 : const OGRFieldDefn *poFieldDefn =
8560 185 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
8561 185 : osSQL += ",m.\"";
8562 185 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
8563 185 : osSQL += '"';
8564 : }
8565 : }
8566 51 : };
8567 :
8568 47 : AddFields();
8569 :
8570 47 : osSQL += ") FROM ";
8571 47 : if (m_iNextShapeId > 0)
8572 : {
8573 4 : osSQL += "(SELECT ";
8574 4 : AddFields();
8575 4 : osSQL += " FROM ";
8576 : }
8577 47 : osSQL += '\"';
8578 47 : osSQL += SQLEscapeName(m_pszTableName);
8579 47 : osSQL += "\" m";
8580 47 : if (!m_soFilter.empty())
8581 : {
8582 35 : if (m_poFilterGeom != nullptr && m_pszAttrQueryString == nullptr &&
8583 10 : HasSpatialIndex())
8584 : {
8585 10 : OGREnvelope sEnvelope;
8586 :
8587 10 : m_poFilterGeom->getEnvelope(&sEnvelope);
8588 :
8589 10 : bool bUseSpatialIndex = true;
8590 10 : if (m_poExtent && sEnvelope.MinX <= m_poExtent->MinX &&
8591 1 : sEnvelope.MinY <= m_poExtent->MinY &&
8592 1 : sEnvelope.MaxX >= m_poExtent->MaxX &&
8593 1 : sEnvelope.MaxY >= m_poExtent->MaxY)
8594 : {
8595 : // Selecting from spatial filter on whole extent can be rather
8596 : // slow. So use function based filtering, just in case the
8597 : // advertized global extent might be wrong. Otherwise we might
8598 : // just discard completely the spatial filter.
8599 1 : bUseSpatialIndex = false;
8600 : }
8601 :
8602 9 : if (bUseSpatialIndex && !std::isinf(sEnvelope.MinX) &&
8603 28 : !std::isinf(sEnvelope.MinY) && !std::isinf(sEnvelope.MaxX) &&
8604 9 : !std::isinf(sEnvelope.MaxY))
8605 : {
8606 : osSQL +=
8607 : CPLSPrintf(" JOIN \"%s\" r "
8608 : "ON m.\"%s\" = r.id WHERE "
8609 : "r.maxx >= %.12f AND r.minx <= %.12f AND "
8610 : "r.maxy >= %.12f AND r.miny <= %.12f",
8611 18 : SQLEscapeName(m_osRTreeName).c_str(),
8612 18 : SQLEscapeName(m_osFIDForRTree).c_str(),
8613 9 : sEnvelope.MinX - 1e-11, sEnvelope.MaxX + 1e-11,
8614 27 : sEnvelope.MinY - 1e-11, sEnvelope.MaxY + 1e-11);
8615 : }
8616 : }
8617 : else
8618 : {
8619 15 : osSQL += " WHERE ";
8620 15 : osSQL += m_soFilter;
8621 : }
8622 : }
8623 :
8624 47 : if (m_iNextShapeId > 0)
8625 : osSQL +=
8626 4 : CPLSPrintf(" LIMIT -1 OFFSET " CPL_FRMT_GIB ") m", m_iNextShapeId);
8627 :
8628 : // CPLDebug("GPKG", "%s", osSQL.c_str());
8629 :
8630 47 : char *pszErrMsg = nullptr;
8631 47 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
8632 47 : &pszErrMsg) != SQLITE_OK)
8633 : {
8634 1 : m_poFillArrowArray->bErrorOccurred = true;
8635 1 : m_poFillArrowArray->osErrorMsg =
8636 2 : pszErrMsg ? pszErrMsg : "unknown error";
8637 : }
8638 47 : sqlite3_free(pszErrMsg);
8639 :
8640 : // Delete function
8641 47 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
8642 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
8643 : nullptr, nullptr, nullptr);
8644 :
8645 94 : std::lock_guard oLock(m_poFillArrowArray->oMutex);
8646 47 : m_poFillArrowArray->bIsFinished = true;
8647 47 : if (m_poFillArrowArray->nCountRows >= 0)
8648 : {
8649 45 : m_poFillArrowArray->psHelper->Shrink(m_poFillArrowArray->nCountRows);
8650 45 : if (m_poFillArrowArray->nCountRows == 0)
8651 : {
8652 21 : m_poFillArrowArray->psHelper->ClearArray();
8653 : }
8654 : }
8655 47 : m_poFillArrowArray->oCV.notify_one();
8656 47 : }
8657 :
8658 : /************************************************************************/
8659 : /* GetNextArrowArray() */
8660 : /************************************************************************/
8661 :
8662 333 : int OGRGeoPackageTableLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
8663 : struct ArrowArray *out_array)
8664 : {
8665 333 : if (!m_bFeatureDefnCompleted)
8666 2 : GetLayerDefn();
8667 333 : if (m_bDeferredCreation && RunDeferredCreationIfNecessary() != OGRERR_NONE)
8668 : {
8669 0 : memset(out_array, 0, sizeof(*out_array));
8670 0 : return EIO;
8671 : }
8672 :
8673 333 : if (m_poFilterGeom != nullptr)
8674 : {
8675 : // Both are exclusive
8676 18 : CreateSpatialIndexIfNecessary();
8677 18 : if (!RunDeferredSpatialIndexUpdate())
8678 : {
8679 0 : memset(out_array, 0, sizeof(*out_array));
8680 0 : return EIO;
8681 : }
8682 : }
8683 :
8684 333 : if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
8685 : {
8686 6 : return OGRGeoPackageLayer::GetNextArrowArray(stream, out_array);
8687 : }
8688 :
8689 849 : if (m_nIsCompatOfOptimizedGetNextArrowArray == FALSE ||
8690 341 : m_pszFidColumn == nullptr || !m_soFilter.empty() ||
8691 668 : m_poFillArrowArray ||
8692 145 : (!m_bGetNextArrowArrayCalledSinceResetReading && m_iNextShapeId > 0))
8693 : {
8694 183 : return GetNextArrowArrayAsynchronous(stream, out_array);
8695 : }
8696 :
8697 : // We can use this optimized version only if there is no hole in FID
8698 : // numbering. That is min(fid) == 1 and max(fid) == m_nTotalFeatureCount
8699 144 : if (m_nIsCompatOfOptimizedGetNextArrowArray < 0)
8700 : {
8701 51 : m_nIsCompatOfOptimizedGetNextArrowArray = FALSE;
8702 51 : const auto nTotalFeatureCount = GetTotalFeatureCount();
8703 51 : if (nTotalFeatureCount < 0)
8704 3 : return GetNextArrowArrayAsynchronous(stream, out_array);
8705 : {
8706 48 : char *pszSQL = sqlite3_mprintf("SELECT MAX(\"%w\") FROM \"%w\"",
8707 : m_pszFidColumn, m_pszTableName);
8708 : OGRErr err;
8709 48 : const auto nMaxFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8710 48 : sqlite3_free(pszSQL);
8711 48 : if (nMaxFID != nTotalFeatureCount)
8712 0 : return GetNextArrowArrayAsynchronous(stream, out_array);
8713 : }
8714 : {
8715 48 : char *pszSQL = sqlite3_mprintf("SELECT MIN(\"%w\") FROM \"%w\"",
8716 : m_pszFidColumn, m_pszTableName);
8717 : OGRErr err;
8718 48 : const auto nMinFID = SQLGetInteger64(m_poDS->GetDB(), pszSQL, &err);
8719 48 : sqlite3_free(pszSQL);
8720 48 : if (nMinFID != 1)
8721 12 : return GetNextArrowArrayAsynchronous(stream, out_array);
8722 : }
8723 36 : m_nIsCompatOfOptimizedGetNextArrowArray = TRUE;
8724 : }
8725 :
8726 129 : m_bGetNextArrowArrayCalledSinceResetReading = true;
8727 :
8728 : // CPLDebug("GPKG", "m_iNextShapeId = " CPL_FRMT_GIB, m_iNextShapeId);
8729 :
8730 258 : const int nMaxBatchSize = OGRArrowArrayHelper::GetMaxFeaturesInBatch(
8731 129 : m_aosArrowArrayStreamOptions);
8732 :
8733 : // Fetch the answer from a potentially queued asynchronous task
8734 129 : if (!m_oQueueArrowArrayPrefetchTasks.empty())
8735 : {
8736 44 : const size_t nTasks = m_oQueueArrowArrayPrefetchTasks.size();
8737 44 : auto task = std::move(m_oQueueArrowArrayPrefetchTasks.front());
8738 44 : m_oQueueArrowArrayPrefetchTasks.pop();
8739 :
8740 : // Wait for thread to be ready
8741 : {
8742 44 : std::unique_lock<std::mutex> oLock(task->m_oMutex);
8743 67 : while (!task->m_bArrayReady)
8744 : {
8745 23 : task->m_oCV.wait(oLock);
8746 : }
8747 44 : task->m_bArrayReady = false;
8748 : }
8749 44 : if (!task->m_osErrorMsg.empty())
8750 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
8751 0 : task->m_osErrorMsg.c_str());
8752 :
8753 145 : const auto stopThread = [&task]()
8754 : {
8755 : {
8756 58 : std::lock_guard oLock(task->m_oMutex);
8757 29 : task->m_bStop = true;
8758 29 : task->m_oCV.notify_one();
8759 : }
8760 29 : if (task->m_oThread.joinable())
8761 29 : task->m_oThread.join();
8762 29 : };
8763 :
8764 44 : if (task->m_iStartShapeId != m_iNextShapeId)
8765 : {
8766 : // Should not normally happen, unless the user messes with
8767 : // GetNextFeature()
8768 0 : CPLError(CE_Failure, CPLE_AppDefined,
8769 : "Worker thread task has not expected m_iStartShapeId "
8770 : "value. Got " CPL_FRMT_GIB ", expected " CPL_FRMT_GIB,
8771 0 : task->m_iStartShapeId, m_iNextShapeId);
8772 0 : if (task->m_psArrowArray->release)
8773 0 : task->m_psArrowArray->release(task->m_psArrowArray.get());
8774 :
8775 0 : stopThread();
8776 : }
8777 44 : else if (task->m_psArrowArray->release)
8778 : {
8779 40 : m_iNextShapeId += task->m_psArrowArray->length;
8780 :
8781 : // Transfer the task ArrowArray to the client array
8782 40 : memcpy(out_array, task->m_psArrowArray.get(),
8783 : sizeof(struct ArrowArray));
8784 40 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8785 :
8786 80 : const bool bMemoryLimitReached = [&task]()
8787 : {
8788 40 : std::unique_lock oLock(task->m_oMutex);
8789 80 : return task->m_bMemoryLimitReached;
8790 40 : }();
8791 :
8792 40 : if (bMemoryLimitReached)
8793 : {
8794 2 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
8795 2 : stopThread();
8796 2 : CancelAsyncNextArrowArray();
8797 2 : return 0;
8798 : }
8799 : // Are the records still available for reading beyond the current
8800 : // queued tasks ? If so, recycle this task to read them
8801 38 : else if (task->m_iStartShapeId +
8802 38 : static_cast<GIntBig>(nTasks) * nMaxBatchSize <=
8803 38 : m_nTotalFeatureCount)
8804 : {
8805 15 : task->m_iStartShapeId +=
8806 15 : static_cast<GIntBig>(nTasks) * nMaxBatchSize;
8807 15 : task->m_poLayer->m_iNextShapeId = task->m_iStartShapeId;
8808 : try
8809 : {
8810 : // Wake-up thread with new task
8811 : {
8812 30 : std::lock_guard oLock(task->m_oMutex);
8813 15 : task->m_bFetchRows = true;
8814 15 : task->m_oCV.notify_one();
8815 : }
8816 15 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
8817 15 : return 0;
8818 : }
8819 0 : catch (const std::exception &e)
8820 : {
8821 0 : CPLError(CE_Failure, CPLE_AppDefined,
8822 0 : "Cannot start worker thread: %s", e.what());
8823 : }
8824 : }
8825 : else
8826 : {
8827 23 : stopThread();
8828 23 : return 0;
8829 : }
8830 : }
8831 :
8832 4 : stopThread();
8833 : }
8834 :
8835 32 : const auto GetThreadsAvailable = []()
8836 : {
8837 : const char *pszMaxThreads =
8838 32 : CPLGetConfigOption("OGR_GPKG_NUM_THREADS", nullptr);
8839 32 : if (pszMaxThreads == nullptr)
8840 32 : return std::min(4, CPLGetNumCPUs());
8841 0 : else if (EQUAL(pszMaxThreads, "ALL_CPUS"))
8842 0 : return CPLGetNumCPUs();
8843 : else
8844 0 : return atoi(pszMaxThreads);
8845 : };
8846 :
8847 : // Start asynchronous tasks to prefetch the next ArrowArray
8848 153 : if (m_poDS->GetAccess() == GA_ReadOnly &&
8849 64 : m_oQueueArrowArrayPrefetchTasks.empty() &&
8850 64 : m_iNextShapeId + 2 * static_cast<GIntBig>(nMaxBatchSize) <=
8851 64 : m_nTotalFeatureCount &&
8852 169 : sqlite3_threadsafe() != 0 && GetThreadsAvailable() >= 2 &&
8853 16 : CPLGetUsablePhysicalRAM() > 1024 * 1024 * 1024)
8854 : {
8855 16 : const int nMaxTasks = static_cast<int>(std::min<GIntBig>(
8856 16 : DIV_ROUND_UP(m_nTotalFeatureCount - nMaxBatchSize - m_iNextShapeId,
8857 : nMaxBatchSize),
8858 32 : GetThreadsAvailable()));
8859 16 : CPLDebug("GPKG", "Using %d threads", nMaxTasks);
8860 32 : GDALOpenInfo oOpenInfo(m_poDS->GetDescription(), GA_ReadOnly);
8861 16 : oOpenInfo.papszOpenOptions = m_poDS->GetOpenOptions();
8862 16 : oOpenInfo.nOpenFlags = GDAL_OF_VECTOR;
8863 59 : for (int iTask = 0; iTask < nMaxTasks; ++iTask)
8864 : {
8865 43 : auto task = std::make_unique<ArrowArrayPrefetchTask>();
8866 86 : task->m_iStartShapeId =
8867 43 : m_iNextShapeId +
8868 43 : static_cast<GIntBig>(iTask + 1) * nMaxBatchSize;
8869 43 : task->m_poDS = std::make_unique<GDALGeoPackageDataset>();
8870 43 : if (!task->m_poDS->Open(&oOpenInfo, m_poDS->m_osFilenameInZip))
8871 : {
8872 0 : break;
8873 : }
8874 0 : auto poOtherLayer = dynamic_cast<OGRGeoPackageTableLayer *>(
8875 43 : task->m_poDS->GetLayerByName(GetName()));
8876 86 : if (poOtherLayer == nullptr ||
8877 43 : poOtherLayer->GetLayerDefn()->GetFieldCount() !=
8878 43 : m_poFeatureDefn->GetFieldCount())
8879 : {
8880 0 : break;
8881 : }
8882 :
8883 : // Install query logging callback
8884 43 : if (m_poDS->pfnQueryLoggerFunc)
8885 : {
8886 0 : task->m_poDS->SetQueryLoggerFunc(m_poDS->pfnQueryLoggerFunc,
8887 0 : m_poDS->poQueryLoggerArg);
8888 : }
8889 :
8890 43 : task->m_poLayer = poOtherLayer;
8891 43 : task->m_psArrowArray = std::make_unique<struct ArrowArray>();
8892 43 : memset(task->m_psArrowArray.get(), 0, sizeof(struct ArrowArray));
8893 :
8894 43 : poOtherLayer->m_nTotalFeatureCount = m_nTotalFeatureCount;
8895 : poOtherLayer->m_aosArrowArrayStreamOptions =
8896 43 : m_aosArrowArrayStreamOptions;
8897 43 : auto poOtherFDefn = poOtherLayer->GetLayerDefn();
8898 86 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
8899 : {
8900 86 : poOtherFDefn->GetGeomFieldDefn(i)->SetIgnored(
8901 43 : m_poFeatureDefn->GetGeomFieldDefn(i)->IsIgnored());
8902 : }
8903 127 : for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
8904 : {
8905 168 : poOtherFDefn->GetFieldDefn(i)->SetIgnored(
8906 84 : m_poFeatureDefn->GetFieldDefn(i)->IsIgnored());
8907 : }
8908 :
8909 43 : poOtherLayer->m_iNextShapeId = task->m_iStartShapeId;
8910 :
8911 43 : auto taskPtr = task.get();
8912 450 : auto taskRunner = [taskPtr]()
8913 : {
8914 86 : std::unique_lock oLock(taskPtr->m_oMutex);
8915 15 : do
8916 : {
8917 58 : taskPtr->m_bFetchRows = false;
8918 58 : taskPtr->m_poLayer->GetNextArrowArrayInternal(
8919 58 : taskPtr->m_psArrowArray.get(), taskPtr->m_osErrorMsg,
8920 58 : taskPtr->m_bMemoryLimitReached);
8921 58 : taskPtr->m_bArrayReady = true;
8922 58 : taskPtr->m_oCV.notify_one();
8923 58 : if (taskPtr->m_bMemoryLimitReached)
8924 12 : break;
8925 : // cppcheck-suppress knownConditionTrueFalse
8926 : // Coverity apparently is confused by the fact that we
8927 : // use unique_lock here to guard access for m_bStop whereas
8928 : // in other places we use a lock_guard, but there's nothing
8929 : // wrong.
8930 : // coverity[missing_lock:FALSE]
8931 88 : while (!taskPtr->m_bStop && !taskPtr->m_bFetchRows)
8932 : {
8933 42 : taskPtr->m_oCV.wait(oLock);
8934 : }
8935 46 : } while (!taskPtr->m_bStop);
8936 43 : };
8937 :
8938 43 : task->m_bFetchRows = true;
8939 : try
8940 : {
8941 43 : task->m_oThread = std::thread(taskRunner);
8942 : }
8943 0 : catch (const std::exception &e)
8944 : {
8945 0 : CPLError(CE_Failure, CPLE_AppDefined,
8946 0 : "Cannot start worker thread: %s", e.what());
8947 0 : break;
8948 : }
8949 43 : m_oQueueArrowArrayPrefetchTasks.push(std::move(task));
8950 : }
8951 : }
8952 :
8953 89 : std::string osErrorMsg;
8954 89 : bool bMemoryLimitReached = false;
8955 : int ret =
8956 89 : GetNextArrowArrayInternal(out_array, osErrorMsg, bMemoryLimitReached);
8957 89 : if (!osErrorMsg.empty())
8958 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
8959 89 : if (bMemoryLimitReached)
8960 : {
8961 1 : CancelAsyncNextArrowArray();
8962 1 : m_nIsCompatOfOptimizedGetNextArrowArray = false;
8963 : }
8964 89 : return ret;
8965 : }
8966 :
8967 : /************************************************************************/
8968 : /* GetNextArrowArrayInternal() */
8969 : /************************************************************************/
8970 :
8971 147 : int OGRGeoPackageTableLayer::GetNextArrowArrayInternal(
8972 : struct ArrowArray *out_array, std::string &osErrorMsg,
8973 : bool &bMemoryLimitReached)
8974 : {
8975 147 : bMemoryLimitReached = false;
8976 147 : memset(out_array, 0, sizeof(*out_array));
8977 :
8978 147 : if (m_iNextShapeId >= m_nTotalFeatureCount)
8979 : {
8980 46 : return 0;
8981 : }
8982 :
8983 : auto psHelper = std::make_unique<OGRArrowArrayHelper>(
8984 202 : m_poDS, m_poFeatureDefn, m_aosArrowArrayStreamOptions, out_array);
8985 101 : if (out_array->release == nullptr)
8986 : {
8987 0 : return ENOMEM;
8988 : }
8989 :
8990 202 : OGRGPKGTableLayerFillArrowArray sFillArrowArray;
8991 101 : sFillArrowArray.psHelper = std::move(psHelper);
8992 101 : sFillArrowArray.nCountRows = 0;
8993 101 : sFillArrowArray.bMemoryLimitReached = false;
8994 101 : sFillArrowArray.bErrorOccurred = false;
8995 101 : sFillArrowArray.bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
8996 : GAS_OPT_DATETIME_AS_STRING, false);
8997 101 : sFillArrowArray.poFeatureDefn = m_poFeatureDefn;
8998 101 : sFillArrowArray.poLayer = this;
8999 101 : sFillArrowArray.hDB = m_poDS->GetDB();
9000 101 : memset(&sFillArrowArray.brokenDown, 0, sizeof(sFillArrowArray.brokenDown));
9001 :
9002 101 : sqlite3_create_function(
9003 101 : m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL", -1,
9004 : SQLITE_UTF8 | SQLITE_DETERMINISTIC, &sFillArrowArray, nullptr,
9005 : OGR_GPKG_FillArrowArray_Step, OGR_GPKG_FillArrowArray_Finalize);
9006 :
9007 202 : std::string osSQL;
9008 101 : osSQL = "SELECT OGR_GPKG_FillArrowArray_INTERNAL(-1,";
9009 101 : int nCountArgs = 1;
9010 :
9011 101 : osSQL += '"';
9012 101 : osSQL += SQLEscapeName(m_pszFidColumn);
9013 101 : osSQL += '"';
9014 101 : ++nCountArgs;
9015 :
9016 200 : if (!sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField.empty() &&
9017 99 : sFillArrowArray.psHelper->m_mapOGRGeomFieldToArrowField[0] >= 0)
9018 : {
9019 98 : osSQL += ',';
9020 98 : osSQL += '"';
9021 98 : osSQL += SQLEscapeName(GetGeometryColumn());
9022 98 : osSQL += '"';
9023 98 : ++nCountArgs;
9024 : }
9025 : const int SQLITE_MAX_FUNCTION_ARG =
9026 101 : sqlite3_limit(m_poDS->GetDB(), SQLITE_LIMIT_FUNCTION_ARG, -1);
9027 730 : for (int iField = 0; iField < sFillArrowArray.psHelper->m_nFieldCount;
9028 : iField++)
9029 : {
9030 : const int iArrowField =
9031 629 : sFillArrowArray.psHelper->m_mapOGRFieldToArrowField[iField];
9032 629 : if (iArrowField >= 0)
9033 : {
9034 618 : if (nCountArgs == SQLITE_MAX_FUNCTION_ARG)
9035 : {
9036 : // We cannot pass more than SQLITE_MAX_FUNCTION_ARG args
9037 : // to a function... So we have to split in several calls...
9038 3 : osSQL += "), OGR_GPKG_FillArrowArray_INTERNAL(";
9039 3 : osSQL += CPLSPrintf("%d", iField);
9040 3 : nCountArgs = 1;
9041 : }
9042 : const OGRFieldDefn *poFieldDefn =
9043 618 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
9044 618 : osSQL += ',';
9045 618 : osSQL += '"';
9046 618 : osSQL += SQLEscapeName(poFieldDefn->GetNameRef());
9047 618 : osSQL += '"';
9048 618 : ++nCountArgs;
9049 : }
9050 : }
9051 101 : osSQL += ") FROM \"";
9052 101 : osSQL += SQLEscapeName(m_pszTableName);
9053 101 : osSQL += "\" WHERE \"";
9054 101 : osSQL += SQLEscapeName(m_pszFidColumn);
9055 101 : osSQL += "\" BETWEEN ";
9056 101 : osSQL += std::to_string(m_iNextShapeId + 1);
9057 101 : osSQL += " AND ";
9058 101 : osSQL += std::to_string(m_iNextShapeId +
9059 101 : sFillArrowArray.psHelper->m_nMaxBatchSize);
9060 :
9061 : // CPLDebug("GPKG", "%s", osSQL.c_str());
9062 :
9063 101 : char *pszErrMsg = nullptr;
9064 101 : if (sqlite3_exec(m_poDS->GetDB(), osSQL.c_str(), nullptr, nullptr,
9065 101 : &pszErrMsg) != SQLITE_OK)
9066 : {
9067 13 : if (!sFillArrowArray.bErrorOccurred &&
9068 13 : !sFillArrowArray.bMemoryLimitReached)
9069 : {
9070 0 : osErrorMsg = pszErrMsg ? pszErrMsg : "unknown error";
9071 : }
9072 : }
9073 101 : sqlite3_free(pszErrMsg);
9074 :
9075 101 : bMemoryLimitReached = sFillArrowArray.bMemoryLimitReached;
9076 :
9077 : // Delete function
9078 101 : sqlite3_create_function(m_poDS->GetDB(), "OGR_GPKG_FillArrowArray_INTERNAL",
9079 : -1, SQLITE_UTF8 | SQLITE_DETERMINISTIC, nullptr,
9080 : nullptr, nullptr, nullptr);
9081 :
9082 101 : if (sFillArrowArray.bErrorOccurred)
9083 : {
9084 0 : sFillArrowArray.psHelper->ClearArray();
9085 0 : return ENOMEM;
9086 : }
9087 :
9088 101 : sFillArrowArray.psHelper->Shrink(sFillArrowArray.nCountRows);
9089 101 : if (sFillArrowArray.nCountRows == 0)
9090 : {
9091 0 : sFillArrowArray.psHelper->ClearArray();
9092 : }
9093 :
9094 101 : m_iNextShapeId += sFillArrowArray.nCountRows;
9095 :
9096 101 : return 0;
9097 : }
9098 :
9099 : /************************************************************************/
9100 : /* OGR_GPKG_GeometryExtent3DAggregate() */
9101 : /************************************************************************/
9102 :
9103 : namespace
9104 : {
9105 : struct GeometryExtent3DAggregateContext
9106 : {
9107 : sqlite3 *m_hDB = nullptr;
9108 : OGREnvelope3D m_oExtent3D;
9109 :
9110 18 : explicit GeometryExtent3DAggregateContext(sqlite3 *hDB)
9111 18 : : m_hDB(hDB), m_oExtent3D()
9112 : {
9113 18 : }
9114 :
9115 : GeometryExtent3DAggregateContext(const GeometryExtent3DAggregateContext &) =
9116 : delete;
9117 : GeometryExtent3DAggregateContext &
9118 : operator=(const GeometryExtent3DAggregateContext &) = delete;
9119 : };
9120 :
9121 : } // namespace
9122 :
9123 26 : static void OGR_GPKG_GeometryExtent3DAggregate_Step(sqlite3_context *pContext,
9124 : int /*argc*/,
9125 : sqlite3_value **argv)
9126 : {
9127 : const GByte *pabyBLOB =
9128 26 : reinterpret_cast<const GByte *>(sqlite3_value_blob(argv[0]));
9129 :
9130 : auto poContext = static_cast<GeometryExtent3DAggregateContext *>(
9131 26 : sqlite3_user_data(pContext));
9132 :
9133 26 : if (pabyBLOB != nullptr)
9134 : {
9135 : GPkgHeader sHeader;
9136 26 : if (OGRGeoPackageGetHeader(pContext, 0, argv, &sHeader, true, true))
9137 : {
9138 26 : OGREnvelope3D extent3D;
9139 26 : extent3D.MinX = sHeader.MinX;
9140 26 : extent3D.MaxX = sHeader.MaxX;
9141 26 : extent3D.MinY = sHeader.MinY;
9142 26 : extent3D.MaxY = sHeader.MaxY;
9143 26 : extent3D.MinZ = sHeader.MinZ;
9144 26 : extent3D.MaxZ = sHeader.MaxZ;
9145 26 : poContext->m_oExtent3D.Merge(extent3D);
9146 : }
9147 0 : else if (!sHeader.bEmpty)
9148 : {
9149 : // Try also spatialite geometry blobs
9150 0 : const int nBLOBLen = sqlite3_value_bytes(argv[0]);
9151 0 : OGRGeometry *poGeom = nullptr;
9152 0 : if (OGRSQLiteImportSpatiaLiteGeometry(pabyBLOB, nBLOBLen,
9153 0 : &poGeom) == OGRERR_NONE &&
9154 0 : poGeom && !poGeom->IsEmpty())
9155 : {
9156 0 : OGREnvelope3D extent3D;
9157 0 : poGeom->getEnvelope(&extent3D);
9158 0 : poContext->m_oExtent3D.Merge(extent3D);
9159 : }
9160 0 : delete poGeom;
9161 : }
9162 : }
9163 26 : }
9164 :
9165 18 : static void OGR_GPKG_GeometryExtent3DAggregate_Finalize(sqlite3_context *)
9166 : {
9167 18 : }
9168 :
9169 : /************************************************************************/
9170 : /* GetExtent3D */
9171 : /************************************************************************/
9172 20 : OGRErr OGRGeoPackageTableLayer::GetExtent3D(int iGeomField,
9173 : OGREnvelope3D *psExtent3D,
9174 : int bForce)
9175 : {
9176 :
9177 20 : OGRFeatureDefn *poDefn = GetLayerDefn();
9178 :
9179 : /* -------------------------------------------------------------------- */
9180 : /* Deferred actions, reset state. */
9181 : /* -------------------------------------------------------------------- */
9182 20 : RunDeferredCreationIfNecessary();
9183 20 : if (!RunDeferredSpatialIndexUpdate())
9184 : {
9185 0 : return OGRERR_FAILURE;
9186 : }
9187 :
9188 20 : const int nGeomFieldCount = poDefn->GetGeomFieldCount();
9189 20 : if (iGeomField < 0 || iGeomField >= nGeomFieldCount)
9190 : {
9191 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for iGeomField");
9192 0 : return OGRERR_FAILURE;
9193 : }
9194 :
9195 20 : if (m_nZFlag == 0 && m_soFilter.empty())
9196 : {
9197 : // If the layer doesn't contain any 3D geometry and no filter is set,
9198 : // we can fallback to the fast 2D GetExtent()
9199 2 : const OGRErr retVal{GetExtent(iGeomField, psExtent3D, bForce)};
9200 2 : psExtent3D->MinZ = std::numeric_limits<double>::infinity();
9201 2 : psExtent3D->MaxZ = -std::numeric_limits<double>::infinity();
9202 2 : return retVal;
9203 : }
9204 : else
9205 : {
9206 18 : *psExtent3D = OGREnvelope3D();
9207 : }
9208 :
9209 : // For internal use only
9210 :
9211 18 : GeometryExtent3DAggregateContext sContext(m_poDS->hDB);
9212 :
9213 36 : CPLString osFuncName;
9214 : osFuncName.Printf("OGR_GPKG_GeometryExtent3DAggregate_INTERNAL_%p",
9215 18 : &sContext);
9216 :
9217 18 : sqlite3_create_function(m_poDS->hDB, osFuncName.c_str(), 1, SQLITE_UTF8,
9218 : &sContext, nullptr,
9219 : OGR_GPKG_GeometryExtent3DAggregate_Step,
9220 : OGR_GPKG_GeometryExtent3DAggregate_Finalize);
9221 :
9222 36 : char *pszSQL = sqlite3_mprintf(
9223 : "SELECT %s(\"%w\") FROM \"%w\"%s", osFuncName.c_str(),
9224 18 : poDefn->GetGeomFieldDefn(iGeomField)->GetNameRef(), m_pszTableName,
9225 45 : m_soFilter.empty() ? "" : (" WHERE " + m_soFilter).c_str());
9226 18 : char *pszErrMsg = nullptr;
9227 : const int rc =
9228 18 : sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &(pszErrMsg));
9229 :
9230 : // Delete function
9231 18 : sqlite3_create_function(m_poDS->GetDB(), osFuncName.c_str(), 1, SQLITE_UTF8,
9232 : nullptr, nullptr, nullptr, nullptr);
9233 :
9234 18 : if (rc != SQLITE_OK)
9235 : {
9236 0 : if (rc != SQLITE_INTERRUPT)
9237 : {
9238 0 : CPLError(CE_Failure, CPLE_AppDefined, "sqlite3_exec(%s) failed: %s",
9239 : pszSQL, pszErrMsg);
9240 : }
9241 0 : sqlite3_free(pszErrMsg);
9242 0 : sqlite3_free(pszSQL);
9243 0 : return OGRERR_FAILURE;
9244 : }
9245 18 : sqlite3_free(pszErrMsg);
9246 18 : sqlite3_free(pszSQL);
9247 :
9248 18 : *psExtent3D = sContext.m_oExtent3D;
9249 :
9250 18 : return OGRERR_NONE;
9251 : }
9252 :
9253 : /************************************************************************/
9254 : /* Truncate() */
9255 : /************************************************************************/
9256 :
9257 : /** Implements "DELETE FROM {table_name}" in an optimzed way.
9258 : *
9259 : * Disable triggers if we detect that the only triggers on the table are ones
9260 : * under our control (i.e. the ones for the gpkg_ogr_contents table and the
9261 : * ones updating the RTree)
9262 : * And even if we cannot disable triggers, truncate the RTree before the main
9263 : * table, as this dramatically speeds up truncating the main table.
9264 : */
9265 6 : OGRErr OGRGeoPackageTableLayer::Truncate()
9266 : {
9267 6 : if (!m_poDS->GetUpdate())
9268 : {
9269 1 : CPLError(CE_Failure, CPLE_NotSupported, UNSUPPORTED_OP_READ_ONLY,
9270 : "Truncate");
9271 1 : return OGRERR_FAILURE;
9272 : }
9273 :
9274 5 : ResetReading();
9275 5 : SyncToDisk();
9276 :
9277 5 : bool bOK = (m_poDS->SoftStartTransaction() == OGRERR_NONE);
9278 :
9279 : struct ReenableTriggers
9280 : {
9281 : sqlite3 *m_hDB = nullptr;
9282 :
9283 3 : explicit ReenableTriggers(sqlite3 *hDB) : m_hDB(hDB)
9284 : {
9285 3 : }
9286 :
9287 3 : ~ReenableTriggers()
9288 3 : {
9289 3 : sqlite3_db_config(m_hDB, SQLITE_DBCONFIG_ENABLE_TRIGGER, 1,
9290 : nullptr);
9291 3 : }
9292 : CPL_DISALLOW_COPY_ASSIGN(ReenableTriggers)
9293 : };
9294 :
9295 : // to keep in top level scope!
9296 0 : std::unique_ptr<ReenableTriggers> reenableTriggers;
9297 :
9298 : // Temporarily disable triggers for greater speed if we detect that the
9299 : // only triggers on the table are the RTree ones and the ones for the
9300 : // gpkg_ogr_contents table
9301 5 : if (bOK && m_bIsTable)
9302 : {
9303 5 : char *pszSQL = sqlite3_mprintf(
9304 : "SELECT COUNT(*) FROM sqlite_master WHERE type = 'trigger' "
9305 : "AND tbl_name = '%q' "
9306 : "AND name NOT IN ('trigger_insert_feature_count_%q',"
9307 : "'trigger_delete_feature_count_%q') "
9308 : "AND name NOT LIKE 'rtree_%q_%%'",
9309 : m_pszTableName, m_pszTableName, m_pszTableName, m_pszTableName);
9310 5 : OGRErr eErr = OGRERR_NONE;
9311 8 : if (SQLGetInteger(m_poDS->GetDB(), pszSQL, &eErr) == 0 &&
9312 3 : eErr == OGRERR_NONE)
9313 : {
9314 3 : int nEnableTriggerOldVal = -1;
9315 3 : sqlite3_db_config(m_poDS->GetDB(), SQLITE_DBCONFIG_ENABLE_TRIGGER,
9316 : -1, &nEnableTriggerOldVal);
9317 3 : if (nEnableTriggerOldVal == 1)
9318 : {
9319 3 : int nNewVal = -1;
9320 3 : sqlite3_db_config(m_poDS->GetDB(),
9321 : SQLITE_DBCONFIG_ENABLE_TRIGGER, 0, &nNewVal);
9322 3 : if (nNewVal == 0)
9323 : {
9324 3 : CPLDebugOnly("GPKG",
9325 : "Disabling triggers during truncation of %s",
9326 : m_pszTableName);
9327 : reenableTriggers =
9328 3 : std::make_unique<ReenableTriggers>(m_poDS->GetDB());
9329 3 : CPL_IGNORE_RET_VAL(reenableTriggers); // to please cppcheck
9330 : }
9331 : }
9332 : }
9333 5 : sqlite3_free(pszSQL);
9334 : }
9335 :
9336 5 : char *pszErrMsg = nullptr;
9337 5 : if (bOK && m_bIsTable && HasSpatialIndex())
9338 : {
9339 : // Manually clean the 3 tables that are used by the RTree:
9340 : // - rtree_{tablename}_{geom}_node: all rows, but nodeno = 1 for which
9341 : // we reset the 'data' field to a zero blob of the same size
9342 : // - rtree_{tablename}_{geom}_parent: all rows
9343 : // - rtree_{tablename}_{geom}_rowid: all rows
9344 :
9345 1 : const char *pszT = m_pszTableName;
9346 1 : const char *pszC = m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef();
9347 :
9348 1 : m_osRTreeName = "rtree_";
9349 1 : m_osRTreeName += pszT;
9350 1 : m_osRTreeName += "_";
9351 1 : m_osRTreeName += pszC;
9352 :
9353 : {
9354 : char *pszSQL =
9355 1 : sqlite3_mprintf("DELETE FROM \"%w_node\" WHERE nodeno > 1;"
9356 : "DELETE FROM \"%w_parent\"; "
9357 : "DELETE FROM \"%w_rowid\"",
9358 : m_osRTreeName.c_str(), m_osRTreeName.c_str(),
9359 : m_osRTreeName.c_str());
9360 1 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9361 : &pszErrMsg) == SQLITE_OK;
9362 1 : sqlite3_free(pszSQL);
9363 : }
9364 :
9365 1 : if (bOK)
9366 : {
9367 1 : char *pszSQL = sqlite3_mprintf(
9368 : "SELECT length(data) FROM \"%w_node\" WHERE nodeno = 1",
9369 : m_osRTreeName.c_str());
9370 : const int nBlobSize =
9371 1 : SQLGetInteger(m_poDS->GetDB(), pszSQL, nullptr);
9372 1 : sqlite3_free(pszSQL);
9373 :
9374 1 : pszSQL = sqlite3_mprintf(
9375 : "UPDATE \"%w_node\" SET data = zeroblob(%d) WHERE nodeno = 1",
9376 : m_osRTreeName.c_str(), nBlobSize);
9377 1 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr,
9378 : &pszErrMsg) == SQLITE_OK;
9379 1 : sqlite3_free(pszSQL);
9380 : }
9381 : }
9382 :
9383 5 : if (bOK)
9384 : {
9385 : // Truncate main table
9386 5 : char *pszSQL = sqlite3_mprintf("DELETE FROM \"%w\"", m_pszTableName);
9387 5 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9388 : SQLITE_OK;
9389 5 : sqlite3_free(pszSQL);
9390 : }
9391 :
9392 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9393 : // Reset feature count
9394 5 : if (bOK && m_poDS->m_bHasGPKGOGRContents)
9395 : {
9396 : char *pszSQL =
9397 4 : sqlite3_mprintf("UPDATE gpkg_ogr_contents SET feature_count = 0 "
9398 : "WHERE lower(table_name) = lower('%q')",
9399 : m_pszTableName);
9400 4 : bOK = sqlite3_exec(m_poDS->hDB, pszSQL, nullptr, nullptr, &pszErrMsg) ==
9401 : SQLITE_OK;
9402 4 : sqlite3_free(pszSQL);
9403 : }
9404 :
9405 5 : if (bOK)
9406 : {
9407 4 : m_nTotalFeatureCount = 0;
9408 : }
9409 : #endif
9410 :
9411 5 : if (bOK)
9412 : {
9413 4 : m_poDS->SoftCommitTransaction();
9414 : }
9415 : else
9416 : {
9417 1 : m_poDS->SoftRollbackTransaction();
9418 : #ifdef ENABLE_GPKG_OGR_CONTENTS
9419 1 : DisableFeatureCount();
9420 : #endif
9421 1 : CPLError(CE_Failure, CPLE_AppDefined, "Truncate(%s) failed: %s",
9422 1 : m_pszTableName, pszErrMsg ? pszErrMsg : "(unknown reason)");
9423 : }
9424 5 : sqlite3_free(pszErrMsg);
9425 :
9426 5 : return bOK ? OGRERR_NONE : OGRERR_FAILURE;
9427 : }
|