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