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