Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoPackage Translator
4 : * Purpose: Implements OGRGeoPackageLayer class
5 : * Author: Paul Ramsey <pramsey@boundlessgeo.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2013, Paul Ramsey <pramsey@boundlessgeo.com>
9 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "ogr_geopackage.h"
15 : #include "ogrgeopackageutility.h"
16 : #include "ogrsqliteutility.h"
17 : #include "ogr_p.h"
18 : #include "ogr_recordbatch.h"
19 : #include "ograrrowarrayhelper.h"
20 : #include "ogrlayerarrow.h"
21 :
22 : /************************************************************************/
23 : /* OGRGeoPackageLayer() */
24 : /************************************************************************/
25 :
26 4438 : OGRGeoPackageLayer::OGRGeoPackageLayer(GDALGeoPackageDataset *poDS)
27 4438 : : m_poDS(poDS)
28 : {
29 4438 : }
30 :
31 : /************************************************************************/
32 : /* ~OGRGeoPackageLayer() */
33 : /************************************************************************/
34 :
35 4438 : OGRGeoPackageLayer::~OGRGeoPackageLayer()
36 : {
37 :
38 4438 : CPLFree(m_pszFidColumn);
39 :
40 4438 : if (m_poQueryStatement)
41 800 : sqlite3_finalize(m_poQueryStatement);
42 :
43 4438 : if (m_poFeatureDefn)
44 4438 : m_poFeatureDefn->Release();
45 4438 : }
46 :
47 : /************************************************************************/
48 : /* ResetReading() */
49 : /************************************************************************/
50 :
51 24686 : void OGRGeoPackageLayer::ResetReading()
52 :
53 : {
54 24686 : ClearStatement();
55 24686 : m_iNextShapeId = 0;
56 24686 : m_bEOF = false;
57 24686 : }
58 :
59 : /************************************************************************/
60 : /* ClearStatement() */
61 : /************************************************************************/
62 :
63 25710 : void OGRGeoPackageLayer::ClearStatement()
64 :
65 : {
66 25710 : if (m_poQueryStatement != nullptr)
67 : {
68 510 : CPLDebug("GPKG", "finalize %p", m_poQueryStatement);
69 510 : sqlite3_finalize(m_poQueryStatement);
70 510 : m_poQueryStatement = nullptr;
71 : }
72 25710 : }
73 :
74 : /************************************************************************/
75 : /* GetNextFeature() */
76 : /************************************************************************/
77 :
78 12771 : OGRFeature *OGRGeoPackageLayer::GetNextFeature()
79 :
80 : {
81 12771 : if (m_bEOF)
82 5 : return nullptr;
83 :
84 12766 : if (m_poQueryStatement == nullptr)
85 : {
86 654 : ResetStatement();
87 654 : if (m_poQueryStatement == nullptr)
88 1 : return nullptr;
89 : }
90 :
91 : for (; true;)
92 : {
93 : /* --------------------------------------------------------------------
94 : */
95 : /* Fetch a record (unless otherwise instructed) */
96 : /* --------------------------------------------------------------------
97 : */
98 12765 : if (m_bDoStep)
99 : {
100 12314 : int rc = sqlite3_step(m_poQueryStatement);
101 12314 : if (rc != SQLITE_ROW)
102 : {
103 323 : if (rc != SQLITE_DONE)
104 : {
105 0 : sqlite3_reset(m_poQueryStatement);
106 0 : CPLError(CE_Failure, CPLE_AppDefined,
107 : "In GetNextRawFeature(): sqlite3_step() : %s",
108 0 : sqlite3_errmsg(m_poDS->GetDB()));
109 : }
110 :
111 323 : ClearStatement();
112 323 : m_bEOF = true;
113 :
114 323 : return nullptr;
115 : }
116 : }
117 : else
118 : {
119 451 : m_bDoStep = true;
120 : }
121 :
122 12442 : OGRFeature *poFeature = TranslateFeature(m_poQueryStatement);
123 :
124 34753 : if ((m_poFilterGeom == nullptr ||
125 24884 : FilterGeometry(poFeature->GetGeomFieldRef(m_iGeomFieldFilter))) &&
126 12442 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
127 12442 : return poFeature;
128 :
129 0 : delete poFeature;
130 0 : }
131 : }
132 :
133 : /************************************************************************/
134 : /* ParseDateField() */
135 : /************************************************************************/
136 :
137 452 : bool OGRGeoPackageLayer::ParseDateField(sqlite3_stmt *hStmt, int iRawField,
138 : int nSqlite3ColType, OGRField *psField,
139 : const OGRFieldDefn *poFieldDefn,
140 : GIntBig nFID)
141 : {
142 452 : if (nSqlite3ColType == SQLITE_TEXT)
143 : {
144 : const char *pszTxt = reinterpret_cast<const char *>(
145 451 : sqlite3_column_text(hStmt, iRawField));
146 451 : return ParseDateField(pszTxt, psField, poFieldDefn, nFID);
147 : }
148 : else
149 : {
150 1 : constexpr int line = __LINE__;
151 1 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
152 : {
153 1 : CPLError(CE_Warning, CPLE_AppDefined,
154 : "Unexpected data type for record " CPL_FRMT_GIB
155 : " in column %s",
156 : nFID, poFieldDefn->GetNameRef());
157 1 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
158 : }
159 1 : return false;
160 : }
161 : }
162 :
163 502 : bool OGRGeoPackageLayer::ParseDateField(const char *pszTxt, OGRField *psField,
164 : const OGRFieldDefn *poFieldDefn,
165 : GIntBig nFID)
166 : {
167 502 : if (pszTxt == nullptr)
168 : {
169 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
170 0 : sqlite3_errmsg(m_poDS->GetDB()));
171 0 : return false;
172 : }
173 502 : const size_t nLen = strlen(pszTxt);
174 : // nominal format: "YYYY-MM-DD" (10 characters)
175 502 : const bool bNominalFormat =
176 501 : (nLen == 10 && pszTxt[4] == '-' && pszTxt[7] == '-' &&
177 500 : static_cast<unsigned>(pszTxt[0] - '0') <= 9 &&
178 500 : static_cast<unsigned>(pszTxt[1] - '0') <= 9 &&
179 500 : static_cast<unsigned>(pszTxt[2] - '0') <= 9 &&
180 500 : static_cast<unsigned>(pszTxt[3] - '0') <= 9 &&
181 500 : static_cast<unsigned>(pszTxt[5] - '0') <= 9 &&
182 500 : static_cast<unsigned>(pszTxt[6] - '0') <= 9 &&
183 1503 : static_cast<unsigned>(pszTxt[8] - '0') <= 9 &&
184 500 : static_cast<unsigned>(pszTxt[9] - '0') <= 9);
185 :
186 502 : bool bError = false;
187 502 : if (bNominalFormat)
188 : {
189 500 : psField->Date.Year = static_cast<GUInt16>(
190 500 : ((((pszTxt[0] - '0') * 10 + (pszTxt[1] - '0')) * 10) +
191 500 : (pszTxt[2] - '0')) *
192 500 : 10 +
193 500 : (pszTxt[3] - '0'));
194 500 : psField->Date.Month =
195 500 : static_cast<GByte>((pszTxt[5] - '0') * 10 + (pszTxt[6] - '0'));
196 500 : psField->Date.Day =
197 500 : static_cast<GByte>((pszTxt[8] - '0') * 10 + (pszTxt[9] - '0'));
198 500 : psField->Date.Hour = 0;
199 500 : psField->Date.Minute = 0;
200 500 : psField->Date.Second = 0.0f;
201 500 : psField->Date.TZFlag = 0;
202 500 : if (psField->Date.Month == 0 || psField->Date.Month > 12 ||
203 500 : psField->Date.Day == 0 || psField->Date.Day > 31)
204 : {
205 0 : bError = true;
206 : }
207 : }
208 2 : else if (OGRParseDate(pszTxt, psField, OGRPARSEDATE_OPTION_LAX))
209 : {
210 1 : constexpr int line = __LINE__;
211 1 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
212 : {
213 1 : CPLError(CE_Warning, CPLE_AppDefined,
214 : "Non-conformant content for record " CPL_FRMT_GIB
215 : " in column %s, %s, "
216 : "successfully parsed",
217 : nFID, poFieldDefn->GetNameRef(), pszTxt);
218 1 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
219 : }
220 : }
221 : else
222 : {
223 1 : bError = true;
224 : }
225 :
226 502 : if (bError)
227 : {
228 1 : OGR_RawField_SetUnset(psField);
229 1 : constexpr int line = __LINE__;
230 1 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
231 : {
232 1 : CPLError(CE_Warning, CPLE_AppDefined,
233 : "Invalid content for record " CPL_FRMT_GIB
234 : " in column %s: %s",
235 : nFID, poFieldDefn->GetNameRef(), pszTxt);
236 1 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
237 : }
238 1 : return false;
239 : }
240 :
241 501 : return true;
242 : }
243 :
244 : /************************************************************************/
245 : /* ParseDateTimeField() */
246 : /************************************************************************/
247 :
248 440 : bool OGRGeoPackageLayer::ParseDateTimeField(sqlite3_stmt *hStmt, int iRawField,
249 : int nSqlite3ColType,
250 : OGRField *psField,
251 : const OGRFieldDefn *poFieldDefn,
252 : GIntBig nFID)
253 : {
254 440 : if (nSqlite3ColType == SQLITE_TEXT)
255 : {
256 : const char *pszTxt = reinterpret_cast<const char *>(
257 439 : sqlite3_column_text(hStmt, iRawField));
258 439 : return ParseDateTimeField(pszTxt, psField, poFieldDefn, nFID);
259 : }
260 : else
261 : {
262 1 : constexpr int line = __LINE__;
263 1 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
264 : {
265 1 : CPLError(CE_Warning, CPLE_AppDefined,
266 : "Unexpected data type for record " CPL_FRMT_GIB
267 : " in column %s",
268 : nFID, poFieldDefn->GetNameRef());
269 1 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
270 : }
271 1 : return false;
272 : }
273 : }
274 :
275 494 : bool OGRGeoPackageLayer::ParseDateTimeField(const char *pszTxt,
276 : OGRField *psField,
277 : const OGRFieldDefn *poFieldDefn,
278 : GIntBig nFID)
279 : {
280 494 : if (pszTxt == nullptr)
281 : {
282 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
283 0 : sqlite3_errmsg(m_poDS->GetDB()));
284 0 : return false;
285 : }
286 :
287 494 : const size_t nLen = strlen(pszTxt);
288 :
289 494 : if (OGRParseDateTimeYYYYMMDDTHHMMSSsssZ(pszTxt, nLen, psField) ||
290 500 : OGRParseDateTimeYYYYMMDDTHHMMSSZ(pszTxt, nLen, psField) ||
291 6 : OGRParseDateTimeYYYYMMDDTHHMMZ(pszTxt, nLen, psField))
292 : {
293 : // nominal format is YYYYMMDDTHHMMSSsssZ before GeoPackage 1.4
294 : // GeoPackage 1.4 also accepts omission of seconds and milliseconds
295 : }
296 5 : else if (OGRParseDate(pszTxt, psField, OGRPARSEDATE_OPTION_LAX))
297 : {
298 4 : constexpr int line = __LINE__;
299 4 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
300 : {
301 3 : CPLError(CE_Warning, CPLE_AppDefined,
302 : "Non-conformant content for record " CPL_FRMT_GIB
303 : " in column %s, %s, "
304 : "successfully parsed",
305 : nFID, poFieldDefn->GetNameRef(), pszTxt);
306 3 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
307 : }
308 : }
309 : else
310 : {
311 1 : OGR_RawField_SetUnset(psField);
312 1 : constexpr int line = __LINE__;
313 1 : if (!m_poDS->m_oSetGPKGLayerWarnings[line])
314 : {
315 1 : CPLError(CE_Warning, CPLE_AppDefined,
316 : "Invalid content for record " CPL_FRMT_GIB
317 : " in column %s: %s",
318 : nFID, poFieldDefn->GetNameRef(), pszTxt);
319 1 : m_poDS->m_oSetGPKGLayerWarnings[line] = true;
320 : }
321 1 : return false;
322 : }
323 :
324 493 : return true;
325 : }
326 :
327 : /************************************************************************/
328 : /* TranslateFeature() */
329 : /************************************************************************/
330 :
331 13718 : OGRFeature *OGRGeoPackageLayer::TranslateFeature(sqlite3_stmt *hStmt)
332 :
333 : {
334 : /* -------------------------------------------------------------------- */
335 : /* Create a feature from the current result. */
336 : /* -------------------------------------------------------------------- */
337 13718 : OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
338 :
339 : /* -------------------------------------------------------------------- */
340 : /* Set FID if we have a column to set it from. */
341 : /* -------------------------------------------------------------------- */
342 13718 : if (m_iFIDCol >= 0)
343 : {
344 12068 : poFeature->SetFID(sqlite3_column_int64(hStmt, m_iFIDCol));
345 12068 : if (m_pszFidColumn == nullptr && poFeature->GetFID() == 0)
346 : {
347 : // Miht be the case for views with joins.
348 0 : poFeature->SetFID(m_iNextShapeId);
349 : }
350 : }
351 : else
352 1650 : poFeature->SetFID(m_iNextShapeId);
353 :
354 13718 : m_iNextShapeId++;
355 :
356 13718 : m_nFeaturesRead++;
357 :
358 : /* -------------------------------------------------------------------- */
359 : /* Process Geometry if we have a column. */
360 : /* -------------------------------------------------------------------- */
361 13718 : if (m_iGeomCol >= 0)
362 : {
363 : OGRGeomFieldDefn *poGeomFieldDefn =
364 12017 : m_poFeatureDefn->GetGeomFieldDefn(0);
365 23969 : if (sqlite3_column_type(hStmt, m_iGeomCol) != SQLITE_NULL &&
366 11952 : !poGeomFieldDefn->IsIgnored())
367 : {
368 11942 : const OGRSpatialReference *poSrs = poGeomFieldDefn->GetSpatialRef();
369 11942 : int iGpkgSize = sqlite3_column_bytes(hStmt, m_iGeomCol);
370 : // coverity[tainted_data_return]
371 : const GByte *pabyGpkg = static_cast<const GByte *>(
372 11942 : sqlite3_column_blob(hStmt, m_iGeomCol));
373 : OGRGeometry *poGeom =
374 11942 : GPkgGeometryToOGR(pabyGpkg, iGpkgSize, nullptr);
375 11942 : if (poGeom == nullptr)
376 : {
377 : // Try also spatialite geometry blobs
378 3 : if (OGRSQLiteImportSpatiaLiteGeometry(pabyGpkg, iGpkgSize,
379 3 : &poGeom) != OGRERR_NONE)
380 : {
381 0 : CPLError(CE_Failure, CPLE_AppDefined,
382 : "Unable to read geometry");
383 : }
384 : }
385 11942 : if (poGeom)
386 : {
387 11942 : if (m_bUndoDiscardCoordLSBOnReading)
388 : {
389 3 : poGeom->roundCoordinates(
390 : poGeomFieldDefn->GetCoordinatePrecision());
391 : }
392 11942 : poGeom->assignSpatialReference(poSrs);
393 : }
394 :
395 11942 : poFeature->SetGeometryDirectly(poGeom);
396 : }
397 : }
398 :
399 : /* -------------------------------------------------------------------- */
400 : /* set the fields. */
401 : /* -------------------------------------------------------------------- */
402 13718 : const int nFieldCount = m_poFeatureDefn->GetFieldCount();
403 203905 : for (int iField = 0; iField < nFieldCount; iField++)
404 : {
405 : const OGRFieldDefn *poFieldDefn =
406 190187 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
407 190187 : if (poFieldDefn->IsIgnored())
408 31 : continue;
409 :
410 190156 : const int iRawField = m_anFieldOrdinals[iField];
411 :
412 190156 : const int nSqlite3ColType = sqlite3_column_type(hStmt, iRawField);
413 190156 : if (nSqlite3ColType == SQLITE_NULL)
414 : {
415 709 : poFeature->SetFieldNull(iField);
416 709 : continue;
417 : }
418 :
419 189447 : switch (poFieldDefn->GetType())
420 : {
421 1812 : case OFTInteger:
422 1812 : poFeature->SetFieldSameTypeUnsafe(
423 : iField, sqlite3_column_int(hStmt, iRawField));
424 1812 : break;
425 :
426 661 : case OFTInteger64:
427 661 : poFeature->SetFieldSameTypeUnsafe(
428 : iField, sqlite3_column_int64(hStmt, iRawField));
429 661 : break;
430 :
431 1122 : case OFTReal:
432 1122 : poFeature->SetFieldSameTypeUnsafe(
433 : iField, sqlite3_column_double(hStmt, iRawField));
434 1122 : break;
435 :
436 2601 : case OFTBinary:
437 : {
438 2601 : const int nBytes = sqlite3_column_bytes(hStmt, iRawField);
439 : // coverity[tainted_data_return]
440 2601 : const void *pabyData = sqlite3_column_blob(hStmt, iRawField);
441 2601 : if (pabyData != nullptr || nBytes == 0)
442 : {
443 2601 : poFeature->SetField(iField, nBytes, pabyData);
444 : }
445 : else
446 : {
447 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
448 0 : sqlite3_errmsg(m_poDS->GetDB()));
449 : }
450 2601 : break;
451 : }
452 :
453 412 : case OFTDate:
454 : {
455 412 : auto psField = poFeature->GetRawFieldRef(iField);
456 412 : CPL_IGNORE_RET_VAL(
457 412 : ParseDateField(hStmt, iRawField, nSqlite3ColType, psField,
458 : poFieldDefn, poFeature->GetFID()));
459 412 : break;
460 : }
461 :
462 400 : case OFTDateTime:
463 : {
464 400 : auto psField = poFeature->GetRawFieldRef(iField);
465 400 : CPL_IGNORE_RET_VAL(ParseDateTimeField(
466 : hStmt, iRawField, nSqlite3ColType, psField, poFieldDefn,
467 : poFeature->GetFID()));
468 400 : break;
469 : }
470 :
471 182439 : case OFTString:
472 : {
473 : const char *pszTxt = reinterpret_cast<const char *>(
474 182439 : sqlite3_column_text(hStmt, iRawField));
475 182439 : if (pszTxt)
476 : {
477 182439 : char *pszTxtDup = VSI_STRDUP_VERBOSE(pszTxt);
478 182439 : if (pszTxtDup)
479 : {
480 182439 : poFeature->SetFieldSameTypeUnsafe(iField, pszTxtDup);
481 : }
482 : }
483 : else
484 : {
485 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
486 0 : sqlite3_errmsg(m_poDS->GetDB()));
487 : }
488 182439 : break;
489 : }
490 :
491 0 : default:
492 0 : break;
493 : }
494 : }
495 :
496 13718 : return poFeature;
497 : }
498 :
499 : /************************************************************************/
500 : /* GetArrowStream() */
501 : /************************************************************************/
502 :
503 113 : bool OGRGeoPackageLayer::GetArrowStream(struct ArrowArrayStream *out_stream,
504 : CSLConstList papszOptions)
505 : {
506 226 : CPLStringList aosOptions;
507 113 : aosOptions.Assign(CSLDuplicate(papszOptions), true);
508 : // GeoPackage are assumed to be in UTC. Even if another timezone is used,
509 : // we'll do the conversion to UTC
510 113 : if (aosOptions.FetchNameValue("TIMEZONE") == nullptr)
511 : {
512 112 : aosOptions.SetNameValue("TIMEZONE", "UTC");
513 : }
514 226 : return OGRLayer::GetArrowStream(out_stream, aosOptions.List());
515 : }
516 :
517 : /************************************************************************/
518 : /* GetNextArrowArray() */
519 : /************************************************************************/
520 :
521 107 : int OGRGeoPackageLayer::GetNextArrowArray(struct ArrowArrayStream *stream,
522 : struct ArrowArray *out_array)
523 : {
524 107 : if (CPLTestBool(CPLGetConfigOption("OGR_GPKG_STREAM_BASE_IMPL", "NO")))
525 : {
526 6 : return OGRLayer::GetNextArrowArray(stream, out_array);
527 : }
528 :
529 101 : int errorErrno = EIO;
530 101 : memset(out_array, 0, sizeof(*out_array));
531 :
532 101 : if (m_bEOF)
533 17 : return 0;
534 :
535 84 : if (m_poQueryStatement == nullptr)
536 : {
537 15 : GetLayerDefn();
538 15 : ResetStatement();
539 15 : if (m_poQueryStatement == nullptr)
540 0 : return 0;
541 : }
542 84 : sqlite3_stmt *hStmt = m_poQueryStatement;
543 :
544 84 : OGRArrowArrayHelper sHelper(m_poDS, m_poFeatureDefn,
545 168 : m_aosArrowArrayStreamOptions, out_array);
546 84 : if (out_array->release == nullptr)
547 : {
548 0 : return ENOMEM;
549 : }
550 :
551 : struct tm brokenDown;
552 84 : memset(&brokenDown, 0, sizeof(brokenDown));
553 :
554 84 : const bool bDateTimeAsString = m_aosArrowArrayStreamOptions.FetchBool(
555 : GAS_OPT_DATETIME_AS_STRING, false);
556 :
557 84 : const uint32_t nMemLimit = OGRArrowArrayHelper::GetMemLimit();
558 84 : int iFeat = 0;
559 294 : while (iFeat < sHelper.m_nMaxBatchSize)
560 : {
561 : /* --------------------------------------------------------------------
562 : */
563 : /* Fetch a record (unless otherwise instructed) */
564 : /* --------------------------------------------------------------------
565 : */
566 292 : if (m_bDoStep)
567 : {
568 226 : int rc = sqlite3_step(hStmt);
569 226 : if (rc != SQLITE_ROW)
570 : {
571 20 : if (rc != SQLITE_DONE)
572 : {
573 0 : sqlite3_reset(hStmt);
574 0 : CPLError(CE_Failure, CPLE_AppDefined,
575 : "In GetNextArrowArray(): sqlite3_step() : %s",
576 0 : sqlite3_errmsg(m_poDS->GetDB()));
577 : }
578 :
579 20 : ClearStatement();
580 20 : m_bEOF = true;
581 :
582 20 : break;
583 : }
584 : }
585 : else
586 : {
587 66 : m_bDoStep = true;
588 : }
589 :
590 272 : m_iNextShapeId++;
591 :
592 272 : m_nFeaturesRead++;
593 :
594 : GIntBig nFID;
595 272 : if (m_iFIDCol >= 0)
596 : {
597 272 : nFID = sqlite3_column_int64(hStmt, m_iFIDCol);
598 272 : if (m_pszFidColumn == nullptr && nFID == 0)
599 : {
600 : // Might be the case for views with joins.
601 0 : nFID = m_iNextShapeId;
602 : }
603 : }
604 : else
605 0 : nFID = m_iNextShapeId;
606 :
607 272 : if (sHelper.m_panFIDValues)
608 : {
609 272 : sHelper.m_panFIDValues[iFeat] = nFID;
610 : }
611 :
612 : /* --------------------------------------------------------------------
613 : */
614 : /* Process Geometry if we have a column. */
615 : /* --------------------------------------------------------------------
616 : */
617 272 : if (m_iGeomCol >= 0 && sHelper.m_mapOGRGeomFieldToArrowField[0] >= 0)
618 : {
619 272 : const int iArrowField = sHelper.m_mapOGRGeomFieldToArrowField[0];
620 272 : auto psArray = out_array->children[iArrowField];
621 :
622 272 : size_t nWKBSize = 0;
623 272 : if (sqlite3_column_type(hStmt, m_iGeomCol) != SQLITE_NULL)
624 : {
625 0 : std::unique_ptr<OGRGeometry> poGeom;
626 129 : const GByte *pabyWkb = nullptr;
627 129 : const int iGpkgSize = sqlite3_column_bytes(hStmt, m_iGeomCol);
628 : // coverity[tainted_data_return]
629 : const GByte *pabyGpkg = static_cast<const GByte *>(
630 129 : sqlite3_column_blob(hStmt, m_iGeomCol));
631 129 : if (m_poFilterGeom == nullptr && iGpkgSize >= 8 && pabyGpkg &&
632 107 : pabyGpkg[0] == 'G' && pabyGpkg[1] == 'P' &&
633 107 : !m_bUndoDiscardCoordLSBOnReading)
634 : {
635 : GPkgHeader oHeader;
636 :
637 : /* Read header */
638 : OGRErr err =
639 107 : GPkgHeaderFromWKB(pabyGpkg, iGpkgSize, &oHeader);
640 107 : if (err == OGRERR_NONE)
641 : {
642 : /* WKB pointer */
643 107 : pabyWkb = pabyGpkg + oHeader.nHeaderLen;
644 107 : nWKBSize = iGpkgSize - oHeader.nHeaderLen;
645 107 : }
646 : }
647 : else
648 : {
649 22 : poGeom.reset(
650 : GPkgGeometryToOGR(pabyGpkg, iGpkgSize, nullptr));
651 22 : if (poGeom == nullptr)
652 : {
653 : // Try also spatialite geometry blobs
654 0 : OGRGeometry *poGeomPtr = nullptr;
655 0 : if (OGRSQLiteImportSpatiaLiteGeometry(
656 0 : pabyGpkg, iGpkgSize, &poGeomPtr) != OGRERR_NONE)
657 : {
658 0 : CPLError(CE_Failure, CPLE_AppDefined,
659 : "Unable to read geometry");
660 : }
661 0 : poGeom.reset(poGeomPtr);
662 : }
663 22 : else if (m_bUndoDiscardCoordLSBOnReading)
664 : {
665 0 : poGeom->roundCoordinates(
666 0 : m_poFeatureDefn->GetGeomFieldDefn(0)
667 : ->GetCoordinatePrecision());
668 : }
669 22 : if (poGeom != nullptr)
670 : {
671 22 : nWKBSize = poGeom->WkbSize();
672 : }
673 44 : if (m_poFilterGeom != nullptr &&
674 22 : !FilterGeometry(poGeom.get()))
675 : {
676 2 : continue;
677 : }
678 : }
679 :
680 127 : if (nWKBSize != 0)
681 : {
682 127 : if (iFeat > 0)
683 : {
684 91 : auto panOffsets = static_cast<int32_t *>(
685 91 : const_cast<void *>(psArray->buffers[1]));
686 91 : const uint32_t nCurLength =
687 91 : static_cast<uint32_t>(panOffsets[iFeat]);
688 91 : if (nWKBSize <= nMemLimit &&
689 91 : nWKBSize > nMemLimit - nCurLength)
690 : {
691 24 : m_bDoStep = false;
692 24 : break;
693 : }
694 : }
695 :
696 103 : GByte *outPtr = sHelper.GetPtrForStringOrBinary(
697 : iArrowField, iFeat, nWKBSize);
698 103 : if (outPtr == nullptr)
699 : {
700 0 : errorErrno = ENOMEM;
701 0 : goto error;
702 : }
703 103 : if (poGeom)
704 : {
705 20 : poGeom->exportToWkb(wkbNDR, outPtr, wkbVariantIso);
706 : }
707 : else
708 : {
709 83 : memcpy(outPtr, pabyWkb, nWKBSize);
710 : }
711 : }
712 : else
713 : {
714 0 : sHelper.SetEmptyStringOrBinary(psArray, iFeat);
715 : }
716 : }
717 :
718 246 : if (nWKBSize == 0)
719 : {
720 143 : if (!sHelper.SetNull(iArrowField, iFeat))
721 : {
722 0 : errorErrno = ENOMEM;
723 0 : goto error;
724 : }
725 : }
726 : }
727 :
728 1743 : for (int iField = 0; iField < sHelper.m_nFieldCount; iField++)
729 : {
730 1535 : const int iArrowField = sHelper.m_mapOGRFieldToArrowField[iField];
731 1535 : if (iArrowField < 0)
732 0 : continue;
733 : const OGRFieldDefn *poFieldDefn =
734 1535 : m_poFeatureDefn->GetFieldDefnUnsafe(iField);
735 :
736 1535 : auto psArray = out_array->children[iArrowField];
737 1535 : const int iRawField = m_anFieldOrdinals[iField];
738 :
739 1535 : const int nSqlite3ColType = sqlite3_column_type(hStmt, iRawField);
740 1535 : if (nSqlite3ColType == SQLITE_NULL)
741 : {
742 260 : if (!sHelper.SetNull(iArrowField, iFeat))
743 : {
744 0 : errorErrno = ENOMEM;
745 0 : goto error;
746 : }
747 260 : continue;
748 : }
749 :
750 1275 : switch (poFieldDefn->GetType())
751 : {
752 876 : case OFTInteger:
753 : {
754 876 : const int nVal = sqlite3_column_int(hStmt, iRawField);
755 876 : if (poFieldDefn->GetSubType() == OFSTBoolean)
756 : {
757 41 : if (nVal != 0)
758 : {
759 40 : sHelper.SetBoolOn(psArray, iFeat);
760 : }
761 : }
762 835 : else if (poFieldDefn->GetSubType() == OFSTInt16)
763 : {
764 41 : sHelper.SetInt16(psArray, iFeat,
765 : static_cast<int16_t>(nVal));
766 : }
767 : else
768 : {
769 794 : sHelper.SetInt32(psArray, iFeat, nVal);
770 : }
771 876 : break;
772 : }
773 :
774 40 : case OFTInteger64:
775 : {
776 40 : sHelper.SetInt64(psArray, iFeat,
777 40 : sqlite3_column_int64(hStmt, iRawField));
778 40 : break;
779 : }
780 :
781 80 : case OFTReal:
782 : {
783 : const double dfVal =
784 80 : sqlite3_column_double(hStmt, iRawField);
785 80 : if (poFieldDefn->GetSubType() == OFSTFloat32)
786 : {
787 40 : sHelper.SetFloat(psArray, iFeat,
788 : static_cast<float>(dfVal));
789 : }
790 : else
791 : {
792 40 : sHelper.SetDouble(psArray, iFeat, dfVal);
793 : }
794 80 : break;
795 : }
796 :
797 98 : case OFTBinary:
798 : {
799 : const uint32_t nBytes = static_cast<uint32_t>(
800 98 : sqlite3_column_bytes(hStmt, iRawField));
801 : // coverity[tainted_data_return]
802 : const void *pabyData =
803 98 : sqlite3_column_blob(hStmt, iRawField);
804 98 : if (pabyData != nullptr || nBytes == 0)
805 : {
806 98 : if (iFeat > 0)
807 : {
808 74 : auto panOffsets = static_cast<int32_t *>(
809 74 : const_cast<void *>(psArray->buffers[1]));
810 74 : const uint32_t nCurLength =
811 74 : static_cast<uint32_t>(panOffsets[iFeat]);
812 74 : if (nBytes <= nMemLimit &&
813 74 : nBytes > nMemLimit - nCurLength)
814 : {
815 19 : m_bDoStep = false;
816 19 : m_iNextShapeId--;
817 19 : m_nFeaturesRead--;
818 19 : goto after_loop;
819 : }
820 : }
821 :
822 79 : GByte *outPtr = sHelper.GetPtrForStringOrBinary(
823 : iArrowField, iFeat, nBytes);
824 79 : if (outPtr == nullptr)
825 : {
826 0 : errorErrno = ENOMEM;
827 0 : goto error;
828 : }
829 79 : if (nBytes)
830 79 : memcpy(outPtr, pabyData, nBytes);
831 : }
832 : else
833 : {
834 0 : sHelper.SetEmptyStringOrBinary(psArray, iFeat);
835 : }
836 79 : break;
837 : }
838 :
839 40 : case OFTDate:
840 : {
841 : OGRField ogrField;
842 40 : if (ParseDateField(hStmt, iRawField, nSqlite3ColType,
843 : &ogrField, poFieldDefn, nFID))
844 : {
845 40 : sHelper.SetDate(psArray, iFeat, brokenDown, ogrField);
846 : }
847 40 : break;
848 : }
849 :
850 43 : case OFTDateTime:
851 : {
852 43 : if (!bDateTimeAsString)
853 : {
854 : OGRField ogrField;
855 40 : if (ParseDateTimeField(hStmt, iRawField,
856 : nSqlite3ColType, &ogrField,
857 : poFieldDefn, nFID))
858 : {
859 40 : sHelper.SetDateTime(psArray, iFeat, brokenDown,
860 40 : sHelper.m_anTZFlags[iField],
861 : ogrField);
862 : }
863 40 : break;
864 : }
865 : else
866 : {
867 : [[fallthrough]];
868 : }
869 : }
870 :
871 : case OFTString:
872 : {
873 : const auto pszTxt = reinterpret_cast<const char *>(
874 101 : sqlite3_column_text(hStmt, iRawField));
875 101 : if (pszTxt != nullptr)
876 : {
877 101 : const size_t nBytes = strlen(pszTxt);
878 101 : if (iFeat > 0)
879 : {
880 77 : auto panOffsets = static_cast<int32_t *>(
881 77 : const_cast<void *>(psArray->buffers[1]));
882 77 : const uint32_t nCurLength =
883 77 : static_cast<uint32_t>(panOffsets[iFeat]);
884 77 : if (nBytes <= nMemLimit &&
885 77 : nBytes > nMemLimit - nCurLength)
886 : {
887 19 : m_bDoStep = false;
888 19 : m_iNextShapeId--;
889 19 : m_nFeaturesRead--;
890 19 : goto after_loop;
891 : }
892 : }
893 :
894 82 : GByte *outPtr = sHelper.GetPtrForStringOrBinary(
895 : iArrowField, iFeat, nBytes);
896 82 : if (outPtr == nullptr)
897 : {
898 0 : errorErrno = ENOMEM;
899 0 : goto error;
900 : }
901 82 : if (nBytes)
902 82 : memcpy(outPtr, pszTxt, nBytes);
903 : }
904 : else
905 : {
906 0 : sHelper.SetEmptyStringOrBinary(psArray, iFeat);
907 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
908 0 : sqlite3_errmsg(m_poDS->GetDB()));
909 : }
910 82 : break;
911 : }
912 :
913 0 : default:
914 0 : break;
915 : }
916 : }
917 :
918 208 : ++iFeat;
919 : }
920 2 : after_loop:
921 84 : sHelper.Shrink(iFeat);
922 84 : if (iFeat == 0)
923 4 : sHelper.ClearArray();
924 :
925 84 : return 0;
926 :
927 0 : error:
928 0 : sHelper.ClearArray();
929 0 : return errorErrno;
930 : }
931 :
932 : /************************************************************************/
933 : /* GetFIDColumn() */
934 : /************************************************************************/
935 :
936 2571 : const char *OGRGeoPackageLayer::GetFIDColumn()
937 : {
938 2571 : if (!m_pszFidColumn)
939 13 : return "";
940 : else
941 2558 : return m_pszFidColumn;
942 : }
943 :
944 : /************************************************************************/
945 : /* TestCapability() */
946 : /************************************************************************/
947 :
948 75 : int OGRGeoPackageLayer::TestCapability(const char *pszCap)
949 : {
950 75 : if (EQUAL(pszCap, OLCIgnoreFields))
951 5 : return TRUE;
952 70 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
953 4 : return TRUE;
954 66 : else if (EQUAL(pszCap, OLCFastGetArrowStream))
955 41 : return TRUE;
956 25 : else if (EQUAL(pszCap, OLCZGeometries))
957 3 : return TRUE;
958 : else
959 22 : return FALSE;
960 : }
961 :
962 : /************************************************************************/
963 : /* BuildFeatureDefn() */
964 : /* */
965 : /* Build feature definition from a set of column definitions */
966 : /* set on a statement. Sift out geometry and FID fields. */
967 : /************************************************************************/
968 :
969 762 : void OGRGeoPackageLayer::BuildFeatureDefn(const char *pszLayerName,
970 : sqlite3_stmt *hStmt)
971 :
972 : {
973 762 : m_poFeatureDefn = new OGRSQLiteFeatureDefn(pszLayerName);
974 762 : SetDescription(m_poFeatureDefn->GetName());
975 762 : m_poFeatureDefn->SetGeomType(wkbNone);
976 762 : m_poFeatureDefn->Reference();
977 :
978 762 : const int nRawColumns = sqlite3_column_count(hStmt);
979 :
980 762 : m_anFieldOrdinals.resize(nRawColumns);
981 :
982 : const bool bPromoteToInteger64 =
983 762 : CPLTestBool(CPLGetConfigOption("OGR_PROMOTE_TO_INTEGER64", "FALSE"));
984 :
985 : #ifdef SQLITE_HAS_COLUMN_METADATA
986 : // Check that there are not several FID fields referenced.
987 : // This is not a sufficient condition to ensure that we can get a true FID,
988 : // but when this occurs, we are (almost) sure that this cannot be a FID.
989 762 : int nFIDCandidates = 0;
990 3012 : for (int iCol = 0; iCol < nRawColumns; iCol++)
991 : {
992 2250 : const char *pszTableName = sqlite3_column_table_name(hStmt, iCol);
993 2250 : const char *pszOriginName = sqlite3_column_origin_name(hStmt, iCol);
994 2250 : if (pszTableName != nullptr && pszOriginName != nullptr)
995 : {
996 1663 : OGRLayer *poLayer = m_poDS->GetLayerByName(pszTableName);
997 1663 : if (poLayer != nullptr)
998 : {
999 244 : if (EQUAL(pszOriginName, poLayer->GetFIDColumn()))
1000 : {
1001 46 : nFIDCandidates++;
1002 : }
1003 : }
1004 : }
1005 : }
1006 : #endif
1007 :
1008 762 : bool bGeometryColumnGuessed = false;
1009 3012 : for (int iCol = 0; iCol < nRawColumns; iCol++)
1010 : {
1011 2250 : OGRFieldDefn oField(SQLUnescape(sqlite3_column_name(hStmt, iCol)),
1012 2250 : OFTString);
1013 :
1014 : // In some cases, particularly when there is a real name for
1015 : // the primary key/_rowid_ column we will end up getting the
1016 : // primary key column appearing twice. Ignore any repeated names.
1017 2250 : if (m_poFeatureDefn->GetFieldIndex(oField.GetNameRef()) != -1)
1018 3 : continue;
1019 :
1020 2444 : if (m_pszFidColumn != nullptr &&
1021 197 : EQUAL(m_pszFidColumn, oField.GetNameRef()))
1022 3 : continue;
1023 :
1024 : // The rowid is for internal use, not a real column.
1025 2244 : if (EQUAL(oField.GetNameRef(), "_rowid_"))
1026 0 : continue;
1027 :
1028 : // this will avoid the old geom field to appear when running something
1029 : // like "select st_buffer(geom,5) as geom, * from my_layer"
1030 2401 : if (m_poFeatureDefn->GetGeomFieldCount() &&
1031 157 : EQUAL(oField.GetNameRef(),
1032 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef()))
1033 : {
1034 3 : continue;
1035 : }
1036 :
1037 : #ifdef SQLITE_HAS_COLUMN_METADATA
1038 2241 : const char *pszTableName = sqlite3_column_table_name(hStmt, iCol);
1039 2241 : const char *pszOriginName = sqlite3_column_origin_name(hStmt, iCol);
1040 2241 : if (pszTableName != nullptr && pszOriginName != nullptr)
1041 : {
1042 1657 : OGRLayer *poLayer = m_poDS->GetLayerByName(pszTableName);
1043 1657 : if (poLayer != nullptr)
1044 : {
1045 238 : if (EQUAL(pszOriginName, poLayer->GetGeometryColumn()))
1046 : {
1047 82 : if (bGeometryColumnGuessed ||
1048 41 : m_poFeatureDefn->GetGeomFieldCount() == 0)
1049 : {
1050 40 : if (bGeometryColumnGuessed)
1051 0 : m_poFeatureDefn->DeleteGeomFieldDefn(0);
1052 : OGRGeomFieldDefn oGeomField(
1053 40 : poLayer->GetLayerDefn()->GetGeomFieldDefn(0));
1054 40 : oGeomField.SetName(oField.GetNameRef());
1055 40 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1056 40 : m_iGeomCol = iCol;
1057 : }
1058 41 : continue;
1059 : }
1060 197 : else if (EQUAL(pszOriginName, poLayer->GetFIDColumn()) &&
1061 197 : m_pszFidColumn == nullptr && nFIDCandidates == 1)
1062 : {
1063 41 : m_pszFidColumn = CPLStrdup(oField.GetNameRef());
1064 41 : m_iFIDCol = iCol;
1065 41 : continue;
1066 : }
1067 : int nSrcIdx =
1068 156 : poLayer->GetLayerDefn()->GetFieldIndex(oField.GetNameRef());
1069 156 : if (nSrcIdx >= 0)
1070 : {
1071 : OGRFieldDefn *poSrcField =
1072 153 : poLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
1073 153 : oField.SetType(poSrcField->GetType());
1074 153 : oField.SetSubType(poSrcField->GetSubType());
1075 153 : oField.SetWidth(poSrcField->GetWidth());
1076 153 : oField.SetPrecision(poSrcField->GetPrecision());
1077 153 : oField.SetDomainName(poSrcField->GetDomainName());
1078 153 : m_poFeatureDefn->AddFieldDefn(&oField);
1079 153 : m_anFieldOrdinals[m_poFeatureDefn->GetFieldCount() - 1] =
1080 : iCol;
1081 153 : continue;
1082 : }
1083 : }
1084 : }
1085 : #endif
1086 :
1087 2006 : const int nColType = sqlite3_column_type(hStmt, iCol);
1088 2006 : if (m_poFeatureDefn->GetGeomFieldCount() == 0 &&
1089 2460 : m_pszFidColumn == nullptr && nColType == SQLITE_INTEGER &&
1090 454 : EQUAL(oField.GetNameRef(), "FID"))
1091 : {
1092 2 : m_pszFidColumn = CPLStrdup(oField.GetNameRef());
1093 2 : m_iFIDCol = iCol;
1094 2 : continue;
1095 : }
1096 :
1097 : // Heuristics to help for https://github.com/OSGeo/gdal/issues/8587
1098 2004 : if (nColType == SQLITE_NULL && m_iGeomCol < 0
1099 : #ifdef SQLITE_HAS_COLUMN_METADATA
1100 585 : && !pszTableName && !pszOriginName
1101 : #endif
1102 : )
1103 : {
1104 224 : bool bIsLikelyGeomColName = EQUAL(oField.GetNameRef(), "geom") ||
1105 111 : EQUAL(oField.GetNameRef(), "geometry");
1106 113 : bool bIsGeomFunction = false;
1107 113 : if (!bIsLikelyGeomColName)
1108 111 : bIsGeomFunction = OGRSQLiteIsSpatialFunctionReturningGeometry(
1109 : oField.GetNameRef());
1110 113 : if (bIsLikelyGeomColName || bIsGeomFunction)
1111 : {
1112 10 : bGeometryColumnGuessed = bIsLikelyGeomColName;
1113 20 : OGRGeomFieldDefn oGeomField(oField.GetNameRef(), wkbUnknown);
1114 10 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1115 10 : m_iGeomCol = iCol;
1116 10 : continue;
1117 : }
1118 : }
1119 :
1120 1994 : const char *pszDeclType = sqlite3_column_decltype(hStmt, iCol);
1121 :
1122 : // Recognize a geometry column from trying to build the geometry
1123 2053 : if (nColType == SQLITE_BLOB &&
1124 59 : m_poFeatureDefn->GetGeomFieldCount() == 0)
1125 : {
1126 58 : const int nBytes = sqlite3_column_bytes(hStmt, iCol);
1127 58 : if (nBytes >= 8)
1128 : {
1129 : // coverity[tainted_data_return]
1130 : const GByte *pabyGpkg = reinterpret_cast<const GByte *>(
1131 58 : sqlite3_column_blob(hStmt, iCol));
1132 : GPkgHeader oHeader;
1133 58 : OGRGeometry *poGeom = nullptr;
1134 58 : int nSRID = 0;
1135 :
1136 58 : if (GPkgHeaderFromWKB(pabyGpkg, nBytes, &oHeader) ==
1137 : OGRERR_NONE)
1138 : {
1139 7 : poGeom = GPkgGeometryToOGR(pabyGpkg, nBytes, nullptr);
1140 7 : nSRID = oHeader.iSrsId;
1141 : }
1142 : else
1143 : {
1144 : // Try also spatialite geometry blobs
1145 51 : if (OGRSQLiteImportSpatiaLiteGeometry(
1146 51 : pabyGpkg, nBytes, &poGeom, &nSRID) != OGRERR_NONE)
1147 : {
1148 49 : delete poGeom;
1149 49 : poGeom = nullptr;
1150 : }
1151 : }
1152 :
1153 58 : if (poGeom)
1154 : {
1155 : OGRGeomFieldDefn oGeomField(oField.GetNameRef(),
1156 18 : wkbUnknown);
1157 :
1158 : /* Read the SRS */
1159 18 : auto poSRS = m_poDS->GetSpatialRef(nSRID, true);
1160 9 : if (poSRS)
1161 : {
1162 7 : oGeomField.SetSpatialRef(poSRS.get());
1163 : }
1164 :
1165 9 : OGRwkbGeometryType eGeomType = poGeom->getGeometryType();
1166 9 : if (pszDeclType != nullptr)
1167 : {
1168 : OGRwkbGeometryType eDeclaredGeomType =
1169 0 : GPkgGeometryTypeToWKB(pszDeclType, false, false);
1170 0 : if (eDeclaredGeomType != wkbUnknown)
1171 : {
1172 0 : eGeomType = OGR_GT_SetModifier(
1173 : eDeclaredGeomType, OGR_GT_HasZ(eGeomType),
1174 : OGR_GT_HasM(eGeomType));
1175 : }
1176 : }
1177 9 : oGeomField.SetType(eGeomType);
1178 :
1179 9 : delete poGeom;
1180 9 : poGeom = nullptr;
1181 :
1182 9 : m_poFeatureDefn->AddGeomFieldDefn(&oGeomField);
1183 9 : m_iGeomCol = iCol;
1184 9 : continue;
1185 : }
1186 : }
1187 : }
1188 :
1189 1985 : switch (nColType)
1190 : {
1191 456 : case SQLITE_INTEGER:
1192 456 : if (bPromoteToInteger64)
1193 0 : oField.SetType(OFTInteger64);
1194 : else
1195 : {
1196 456 : GIntBig nVal = sqlite3_column_int64(hStmt, iCol);
1197 456 : if (CPL_INT64_FITS_ON_INT32(nVal))
1198 453 : oField.SetType(OFTInteger);
1199 : else
1200 3 : oField.SetType(OFTInteger64);
1201 : }
1202 456 : break;
1203 :
1204 382 : case SQLITE_FLOAT:
1205 382 : oField.SetType(OFTReal);
1206 382 : break;
1207 :
1208 50 : case SQLITE_BLOB:
1209 50 : oField.SetType(OFTBinary);
1210 50 : break;
1211 :
1212 1985 : default:
1213 : /* leave it as OFTString */;
1214 : }
1215 :
1216 1985 : if (pszDeclType != nullptr)
1217 : {
1218 : OGRFieldSubType eSubType;
1219 1373 : int nMaxWidth = 0;
1220 : const int nFieldType =
1221 1373 : GPkgFieldToOGR(pszDeclType, eSubType, nMaxWidth);
1222 1373 : if (nFieldType <= OFTMaxType)
1223 : {
1224 1373 : oField.SetType(static_cast<OGRFieldType>(nFieldType));
1225 1373 : oField.SetSubType(eSubType);
1226 1373 : oField.SetWidth(nMaxWidth);
1227 : }
1228 : }
1229 :
1230 1985 : m_poFeatureDefn->AddFieldDefn(&oField);
1231 1985 : m_anFieldOrdinals[m_poFeatureDefn->GetFieldCount() - 1] = iCol;
1232 : }
1233 762 : }
1234 :
1235 : /************************************************************************/
1236 : /* SetIgnoredFields() */
1237 : /************************************************************************/
1238 :
1239 16 : OGRErr OGRGeoPackageLayer::SetIgnoredFields(CSLConstList papszFields)
1240 : {
1241 16 : OGRErr eErr = OGRLayer::SetIgnoredFields(papszFields);
1242 16 : if (eErr == OGRERR_NONE)
1243 : {
1244 : // So that OGRGeoPackageTableLayer::BuildColumns() is called
1245 16 : ResetReading();
1246 : }
1247 16 : return eErr;
1248 : }
|