Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Open FileGDB OGR driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "ogr_openfilegdb.h"
15 :
16 : #include <cmath>
17 : #include <cstddef>
18 : #include <cstdio>
19 : #include <cstdlib>
20 : #include <cstring>
21 : #include <cwchar>
22 : #include <algorithm>
23 : #include <limits>
24 : #include <string>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_minixml.h"
29 : #include "cpl_quad_tree.h"
30 : #include "cpl_string.h"
31 : #include "ogr_api.h"
32 : #include "ogr_core.h"
33 : #include "ogr_feature.h"
34 : #include "ogr_geometry.h"
35 : #include "ogr_spatialref.h"
36 : #include "ogr_srs_api.h"
37 : #include "ogrsf_frmts.h"
38 : #include "filegdbtable.h"
39 : #include "ogr_swq.h"
40 : #include "filegdb_coordprec_read.h"
41 :
42 : /************************************************************************/
43 : /* OGROpenFileGDBLayer() */
44 : /************************************************************************/
45 :
46 10742 : OGROpenFileGDBLayer::OGROpenFileGDBLayer(
47 : OGROpenFileGDBDataSource *poDS, const char *pszGDBFilename,
48 : const char *pszName, const std::string &osDefinition,
49 : const std::string &osDocumentation, bool bEditable,
50 10742 : OGRwkbGeometryType eGeomType, const std::string &osParentDefinition)
51 : : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName),
52 : m_bEditable(bEditable), m_osDefinition(osDefinition),
53 10742 : m_osDocumentation(osDocumentation)
54 : {
55 : // TODO(rouault): What error on compiler versions? r33032 does not say.
56 :
57 : // We cannot initialize m_poFeatureDefn in above list. MSVC doesn't like
58 : // this to be used in initialization list.
59 10742 : m_poFeatureDefn = new OGROpenFileGDBFeatureDefn(this, pszName, false);
60 10742 : SetDescription(m_poFeatureDefn->GetName());
61 10742 : m_poFeatureDefn->SetGeomType(wkbNone);
62 10742 : m_poFeatureDefn->Reference();
63 :
64 10742 : m_eGeomType = eGeomType;
65 :
66 10742 : if (!m_osDefinition.empty())
67 : {
68 3265 : BuildGeometryColumnGDBv10(osParentDefinition);
69 : }
70 :
71 : // bSealFields = false because we do lazy resolution of fields
72 10742 : m_poFeatureDefn->Seal(/* bSealFields = */ false);
73 10742 : }
74 :
75 : /************************************************************************/
76 : /* OGROpenFileGDBLayer() */
77 : /************************************************************************/
78 :
79 270 : OGROpenFileGDBLayer::OGROpenFileGDBLayer(OGROpenFileGDBDataSource *poDS,
80 : const char *pszGDBFilename,
81 : const char *pszName,
82 : OGRwkbGeometryType eType,
83 270 : CSLConstList papszOptions)
84 : : m_poDS(poDS), m_osGDBFilename(pszGDBFilename), m_osName(pszName),
85 : m_aosCreationOptions(papszOptions), m_eGeomType(eType),
86 : m_bArcGISPro32OrLater(
87 540 : EQUAL(CSLFetchNameValueDef(papszOptions, "TARGET_ARCGIS_VERSION", ""),
88 270 : "ARCGIS_PRO_3_2_OR_LATER"))
89 : {
90 270 : }
91 :
92 : /***********************************************************************/
93 : /* ~OGROpenFileGDBLayer() */
94 : /***********************************************************************/
95 :
96 22024 : OGROpenFileGDBLayer::~OGROpenFileGDBLayer()
97 : {
98 11012 : OGROpenFileGDBLayer::SyncToDisk();
99 :
100 11012 : if (m_poFeatureDefn)
101 : {
102 11010 : m_poFeatureDefn->UnsetLayer();
103 11010 : m_poFeatureDefn->Release();
104 : }
105 :
106 11012 : delete m_poLyrTable;
107 :
108 11012 : delete m_poAttributeIterator;
109 11012 : delete m_poIterMinMax;
110 11012 : delete m_poSpatialIndexIterator;
111 11012 : delete m_poCombinedIterator;
112 11012 : if (m_pQuadTree != nullptr)
113 123 : CPLQuadTreeDestroy(m_pQuadTree);
114 11012 : CPLFree(m_pahFilteredFeatures);
115 22024 : }
116 :
117 : /************************************************************************/
118 : /* Close() */
119 : /************************************************************************/
120 :
121 6 : void OGROpenFileGDBLayer::Close()
122 : {
123 6 : delete m_poLyrTable;
124 6 : m_poLyrTable = nullptr;
125 6 : m_bValidLayerDefn = FALSE;
126 6 : }
127 :
128 : /************************************************************************/
129 : /* BuildGeometryColumnGDBv10() */
130 : /************************************************************************/
131 :
132 3265 : int OGROpenFileGDBLayer::BuildGeometryColumnGDBv10(
133 : const std::string &osParentDefinition)
134 : {
135 3265 : CPLXMLNode *psTree = CPLParseXMLString(m_osDefinition.c_str());
136 3265 : if (psTree == nullptr)
137 : {
138 0 : m_osDefinition = "";
139 0 : return FALSE;
140 : }
141 :
142 3265 : CPLStripXMLNamespace(psTree, nullptr, TRUE);
143 : /* CPLSerializeXMLTreeToFile( psTree, "/dev/stderr" ); */
144 3265 : const CPLXMLNode *psInfo = CPLSearchXMLNode(psTree, "=DEFeatureClassInfo");
145 3265 : if (psInfo == nullptr)
146 494 : psInfo = CPLSearchXMLNode(psTree, "=DETableInfo");
147 3265 : if (psInfo == nullptr)
148 : {
149 0 : m_osDefinition = "";
150 0 : CPLDestroyXMLNode(psTree);
151 0 : return FALSE;
152 : }
153 :
154 3265 : const char *pszAliasName = CPLGetXMLValue(psInfo, "AliasName", nullptr);
155 3265 : if (pszAliasName && strcmp(pszAliasName, GetDescription()) != 0)
156 : {
157 3 : SetMetadataItem("ALIAS_NAME", pszAliasName);
158 : }
159 :
160 3265 : m_bTimeInUTC = CPLTestBool(CPLGetXMLValue(psInfo, "IsTimeInUTC", "false"));
161 :
162 : /* We cannot trust the XML definition to build the field definitions. */
163 : /* It sometimes misses a few fields ! */
164 :
165 3265 : const bool bHasZ = CPLTestBool(CPLGetXMLValue(psInfo, "HasZ", "NO"));
166 3265 : const bool bHasM = CPLTestBool(CPLGetXMLValue(psInfo, "HasM", "NO"));
167 3265 : const char *pszShapeType = CPLGetXMLValue(psInfo, "ShapeType", nullptr);
168 : const char *pszShapeFieldName =
169 3265 : CPLGetXMLValue(psInfo, "ShapeFieldName", nullptr);
170 3265 : if (pszShapeType != nullptr && pszShapeFieldName != nullptr)
171 : {
172 2771 : m_eGeomType =
173 2771 : FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(pszShapeType);
174 :
175 2771 : if (EQUAL(pszShapeType, "esriGeometryMultiPatch"))
176 : {
177 74 : if (m_poLyrTable == nullptr)
178 : {
179 74 : m_poLyrTable = new FileGDBTable();
180 74 : if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
181 74 : GetDescription())))
182 : {
183 0 : Close();
184 : }
185 : }
186 74 : if (m_poLyrTable != nullptr)
187 : {
188 74 : m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
189 74 : if (m_iGeomFieldIdx >= 0)
190 : {
191 : FileGDBGeomField *poGDBGeomField =
192 : reinterpret_cast<FileGDBGeomField *>(
193 74 : m_poLyrTable->GetField(m_iGeomFieldIdx));
194 74 : m_poGeomConverter.reset(
195 : FileGDBOGRGeometryConverter::BuildConverter(
196 : poGDBGeomField));
197 74 : TryToDetectMultiPatchKind();
198 : }
199 : }
200 : }
201 :
202 2771 : if (bHasZ)
203 1088 : m_eGeomType = wkbSetZ(m_eGeomType);
204 2771 : if (bHasM)
205 974 : m_eGeomType = wkbSetM(m_eGeomType);
206 :
207 : auto poGeomFieldDefn = std::make_unique<OGROpenFileGDBGeomFieldDefn>(
208 2771 : nullptr, pszShapeFieldName, m_eGeomType);
209 :
210 : const CPLXMLNode *psGPFieldInfoExs =
211 2771 : CPLGetXMLNode(psInfo, "GPFieldInfoExs");
212 2771 : if (psGPFieldInfoExs)
213 : {
214 5600 : for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild; psChild;
215 2829 : psChild = psChild->psNext)
216 : {
217 5600 : if (psChild->eType != CXT_Element)
218 2771 : continue;
219 5658 : if (EQUAL(psChild->pszValue, "GPFieldInfoEx") &&
220 2829 : EQUAL(CPLGetXMLValue(psChild, "Name", ""),
221 : pszShapeFieldName))
222 : {
223 2771 : poGeomFieldDefn->SetNullable(CPLTestBool(
224 : CPLGetXMLValue(psChild, "IsNullable", "TRUE")));
225 2771 : break;
226 : }
227 : }
228 : }
229 :
230 : const CPLXMLNode *psSpatialReference =
231 2771 : CPLGetXMLNode(psInfo, "SpatialReference");
232 2771 : if (psSpatialReference)
233 : {
234 5542 : poGeomFieldDefn->SetCoordinatePrecision(
235 5542 : GDBGridSettingsToOGR(psSpatialReference));
236 : }
237 :
238 2771 : OGRSpatialReference *poParentSRS = nullptr;
239 2771 : if (!osParentDefinition.empty())
240 : {
241 : CPLXMLNode *psParentTree =
242 19 : CPLParseXMLString(osParentDefinition.c_str());
243 19 : if (psParentTree != nullptr)
244 : {
245 19 : CPLStripXMLNamespace(psParentTree, nullptr, TRUE);
246 : CPLXMLNode *psParentInfo =
247 19 : CPLSearchXMLNode(psParentTree, "=DEFeatureDataset");
248 19 : if (psParentInfo != nullptr)
249 : {
250 19 : poParentSRS = m_poDS->BuildSRS(psParentInfo);
251 : }
252 19 : CPLDestroyXMLNode(psParentTree);
253 : }
254 19 : if (poParentSRS == nullptr)
255 : {
256 4 : CPLDebug("OpenFileGDB", "Cannot get SRS from feature dataset");
257 : }
258 : }
259 :
260 2771 : auto poSRS = m_poDS->BuildSRS(psInfo);
261 2771 : if (poParentSRS)
262 : {
263 15 : if (poSRS)
264 : {
265 15 : if (!poSRS->IsSame(poParentSRS))
266 : {
267 : // Not sure this situation is really valid (seems more a
268 : // bug of the editing software), but happens with
269 : // https://github.com/OSGeo/gdal/issues/5747
270 : // In the situation of
271 : // https://github.com/OSGeo/gdal/issues/5747, the SRS inside
272 : // the .gdbtable is consistent with the XML definition of
273 : // the feature dataset, so it seems that the XML
274 : // definition of the feature table lacked an update.
275 4 : CPLDebug(
276 : "OpenFileGDB",
277 : "Table %s declare a CRS '%s' in its XML definition, "
278 : "but its feature dataset declares '%s'. "
279 : "Using the later",
280 2 : GetDescription(), poSRS->GetName(),
281 : poParentSRS->GetName());
282 : }
283 15 : poSRS->Release();
284 : }
285 : // Always use the SRS of the feature dataset
286 15 : poSRS = poParentSRS;
287 : }
288 2771 : if (poSRS != nullptr)
289 : {
290 2367 : poGeomFieldDefn->SetSpatialRef(poSRS);
291 2367 : poSRS->Dereference();
292 : }
293 2771 : m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
294 : }
295 : else
296 : {
297 494 : m_eGeomType = wkbNone;
298 : }
299 3265 : CPLDestroyXMLNode(psTree);
300 :
301 3265 : return TRUE;
302 : }
303 :
304 : /************************************************************************/
305 : /* TryToDetectMultiPatchKind() */
306 : /************************************************************************/
307 :
308 : // If the first and last feature have the same geometry type, then use
309 : // it for the whole layer.
310 78 : void OGROpenFileGDBLayer::TryToDetectMultiPatchKind()
311 : {
312 78 : CPLAssert(m_poLyrTable != nullptr);
313 78 : CPLAssert(m_iGeomFieldIdx >= 0);
314 :
315 78 : if (m_poLyrTable->GetTotalRecordCount() == 0)
316 1 : return;
317 77 : const int64_t nFirstIdx = m_poLyrTable->GetAndSelectNextNonEmptyRow(0);
318 77 : if (nFirstIdx < 0)
319 0 : return;
320 :
321 77 : const OGRField *psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
322 77 : if (psField == nullptr)
323 0 : return;
324 77 : OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField);
325 77 : if (poGeom == nullptr)
326 0 : return;
327 77 : const OGRwkbGeometryType eType = poGeom->getGeometryType();
328 77 : delete poGeom;
329 :
330 77 : int64_t nLastIdx = m_poLyrTable->GetTotalRecordCount() - 1;
331 77 : const GUInt32 nErrorCount = CPLGetErrorCounter();
332 76 : while (nLastIdx > nFirstIdx &&
333 77 : m_poLyrTable->GetOffsetInTableForRow(nLastIdx) == 0 &&
334 0 : nErrorCount == CPLGetErrorCounter())
335 : {
336 0 : nLastIdx--;
337 : }
338 77 : if (nLastIdx > nFirstIdx && m_poLyrTable->SelectRow(nLastIdx))
339 : {
340 76 : psField = m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
341 76 : if (psField == nullptr)
342 : {
343 0 : m_eGeomType = eType;
344 0 : return;
345 : }
346 76 : poGeom = m_poGeomConverter->GetAsGeometry(psField);
347 76 : if (poGeom == nullptr)
348 : {
349 0 : m_eGeomType = eType;
350 0 : return;
351 : }
352 76 : if (eType == poGeom->getGeometryType())
353 76 : m_eGeomType = eType;
354 76 : delete poGeom;
355 : }
356 : }
357 :
358 : /************************************************************************/
359 : /* BuildLayerDefinition() */
360 : /************************************************************************/
361 :
362 276445 : int OGROpenFileGDBLayer::BuildLayerDefinition()
363 : {
364 276445 : if (m_bValidLayerDefn >= 0)
365 275631 : return m_bValidLayerDefn;
366 :
367 814 : if (m_poLyrTable == nullptr)
368 : {
369 809 : m_poLyrTable = new FileGDBTable();
370 809 : if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
371 809 : GetDescription())))
372 : {
373 2 : if (m_bEditable)
374 : {
375 : // Retry in read-only mode
376 2 : m_bEditable = false;
377 2 : delete m_poLyrTable;
378 2 : m_poLyrTable = new FileGDBTable();
379 2 : if (!(m_poLyrTable->Open(m_osGDBFilename, m_bEditable,
380 2 : GetDescription())))
381 : {
382 0 : Close();
383 0 : return FALSE;
384 : }
385 : else
386 : {
387 2 : CPLError(
388 : CE_Failure, CPLE_FileIO,
389 : "Cannot open %s in update mode, but only in read-only",
390 2 : GetDescription());
391 : }
392 : }
393 : else
394 : {
395 0 : Close();
396 0 : return FALSE;
397 : }
398 : }
399 : }
400 :
401 814 : m_bValidLayerDefn = TRUE;
402 1628 : auto oTemporaryUnsealer(m_poFeatureDefn->GetTemporaryUnsealer());
403 :
404 814 : m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
405 814 : if (m_iGeomFieldIdx >= 0)
406 : {
407 542 : FileGDBGeomField *poGDBGeomField = cpl::down_cast<FileGDBGeomField *>(
408 542 : m_poLyrTable->GetField(m_iGeomFieldIdx));
409 542 : m_poGeomConverter.reset(
410 : FileGDBOGRGeometryConverter::BuildConverter(poGDBGeomField));
411 :
412 : #ifdef DEBUG
413 542 : const auto poSRS = GetSpatialRef();
414 758 : if (poSRS != nullptr && !poGDBGeomField->GetWKT().empty() &&
415 216 : poGDBGeomField->GetWKT()[0] != '{')
416 : {
417 : auto poSRSFromGDBTable =
418 216 : m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str());
419 216 : if (poSRSFromGDBTable)
420 : {
421 216 : if (!poSRS->IsSame(poSRSFromGDBTable))
422 : {
423 0 : CPLDebug("OpenFileGDB",
424 : "Table %s declare a CRS '%s' in its XML "
425 : "definition (or in its parent's one), "
426 : "but its .gdbtable declares '%s'. "
427 : "Using the former",
428 0 : GetDescription(), poSRS->GetName(),
429 : poSRSFromGDBTable->GetName());
430 : }
431 216 : poSRSFromGDBTable->Release();
432 : }
433 : }
434 : #endif
435 :
436 542 : if (!(m_poLyrTable->CanUseIndices() &&
437 534 : m_poLyrTable->HasSpatialIndex() &&
438 427 : CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX",
439 1084 : "YES"))) &&
440 123 : CPLTestBool(CPLGetConfigOption("OPENFILEGDB_IN_MEMORY_SPI", "YES")))
441 : {
442 : CPLRectObj sGlobalBounds;
443 123 : sGlobalBounds.minx = poGDBGeomField->GetXMin();
444 123 : sGlobalBounds.miny = poGDBGeomField->GetYMin();
445 123 : sGlobalBounds.maxx = poGDBGeomField->GetXMax();
446 123 : sGlobalBounds.maxy = poGDBGeomField->GetYMax();
447 123 : m_pQuadTree = CPLQuadTreeCreate(&sGlobalBounds, nullptr);
448 123 : CPLQuadTreeSetMaxDepth(
449 : m_pQuadTree,
450 : CPLQuadTreeGetAdvisedMaxDepth(
451 123 : static_cast<int>(std::min<int64_t>(
452 246 : INT_MAX, m_poLyrTable->GetValidRecordCount()))));
453 : }
454 : else
455 : {
456 419 : m_eSpatialIndexState = SPI_INVALID;
457 : }
458 : }
459 :
460 1754 : if (m_iGeomFieldIdx >= 0 &&
461 542 : (m_osDefinition.empty() ||
462 398 : m_poFeatureDefn->OGRFeatureDefn::GetGeomFieldCount() == 0))
463 : {
464 : /* FileGDB v9 case */
465 : FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
466 144 : m_poLyrTable->GetField(m_iGeomFieldIdx));
467 144 : const char *pszName = poGDBGeomField->GetName().c_str();
468 : const FileGDBTableGeometryType eGDBGeomType =
469 144 : m_poLyrTable->GetGeometryType();
470 :
471 144 : OGRwkbGeometryType eGeomType = wkbUnknown;
472 144 : switch (eGDBGeomType)
473 : {
474 0 : case FGTGT_NONE: /* doesn't make sense ! */
475 0 : break;
476 12 : case FGTGT_POINT:
477 12 : eGeomType = wkbPoint;
478 12 : break;
479 12 : case FGTGT_MULTIPOINT:
480 12 : eGeomType = wkbMultiPoint;
481 12 : break;
482 16 : case FGTGT_LINE:
483 16 : eGeomType = wkbMultiLineString;
484 16 : break;
485 100 : case FGTGT_POLYGON:
486 100 : eGeomType = wkbMultiPolygon;
487 100 : break;
488 4 : case FGTGT_MULTIPATCH:
489 4 : eGeomType = wkbUnknown;
490 4 : break;
491 : }
492 :
493 210 : if (m_eGeomType != wkbUnknown &&
494 66 : wkbFlatten(eGeomType) != wkbFlatten(m_eGeomType))
495 : {
496 0 : CPLError(CE_Warning, CPLE_AppDefined,
497 : "Inconsistency for layer geometry type");
498 : }
499 :
500 144 : m_eGeomType = eGeomType;
501 :
502 144 : if (eGDBGeomType == FGTGT_MULTIPATCH)
503 : {
504 4 : TryToDetectMultiPatchKind();
505 : }
506 :
507 144 : if (m_poLyrTable->GetGeomTypeHasZ())
508 28 : m_eGeomType = wkbSetZ(m_eGeomType);
509 :
510 144 : if (m_poLyrTable->GetGeomTypeHasM())
511 0 : m_eGeomType = wkbSetM(m_eGeomType);
512 :
513 : {
514 : auto poGeomFieldDefn =
515 0 : std::make_unique<OGROpenFileGDBGeomFieldDefn>(nullptr, pszName,
516 144 : m_eGeomType);
517 144 : poGeomFieldDefn->SetNullable(poGDBGeomField->IsNullable());
518 :
519 144 : m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
520 : }
521 144 : auto poGeomFieldDefn = m_poFeatureDefn->GetGeomFieldDefn(0);
522 :
523 144 : OGRSpatialReference *poSRS = nullptr;
524 288 : if (!poGDBGeomField->GetWKT().empty() &&
525 144 : poGDBGeomField->GetWKT()[0] != '{')
526 : {
527 138 : poSRS = m_poDS->BuildSRS(poGDBGeomField->GetWKT().c_str());
528 : }
529 144 : if (poSRS != nullptr)
530 : {
531 138 : poGeomFieldDefn->SetSpatialRef(poSRS);
532 138 : poSRS->Dereference();
533 : }
534 : }
535 670 : else if (m_osDefinition.empty() && m_iGeomFieldIdx < 0)
536 : {
537 168 : m_eGeomType = wkbNone;
538 : }
539 :
540 1628 : CPLXMLTreeCloser oTree(nullptr);
541 814 : const CPLXMLNode *psGPFieldInfoExs = nullptr;
542 :
543 1628 : std::string osAreaFieldName;
544 814 : std::string osLengthFieldName;
545 814 : if (!m_osDefinition.empty())
546 : {
547 502 : oTree.reset(CPLParseXMLString(m_osDefinition.c_str()));
548 502 : if (oTree != nullptr)
549 : {
550 502 : CPLStripXMLNamespace(oTree.get(), nullptr, TRUE);
551 : CPLXMLNode *psInfo =
552 502 : CPLSearchXMLNode(oTree.get(), "=DEFeatureClassInfo");
553 502 : if (psInfo == nullptr)
554 104 : psInfo = CPLSearchXMLNode(oTree.get(), "=DETableInfo");
555 502 : if (psInfo != nullptr)
556 : {
557 502 : psGPFieldInfoExs = CPLGetXMLNode(psInfo, "GPFieldInfoExs");
558 502 : osAreaFieldName = CPLGetXMLValue(psInfo, "AreaFieldName", "");
559 : osLengthFieldName =
560 502 : CPLGetXMLValue(psInfo, "LengthFieldName", "");
561 502 : m_osPath = CPLGetXMLValue(psInfo, "CatalogPath", "");
562 : }
563 : }
564 : }
565 :
566 6605 : for (int i = 0; i < m_poLyrTable->GetFieldCount(); i++)
567 : {
568 5791 : if (i == m_iGeomFieldIdx)
569 1356 : continue;
570 5249 : if (i == m_poLyrTable->GetObjectIdFieldIdx())
571 814 : continue;
572 :
573 4435 : FileGDBField *poGDBField = m_poLyrTable->GetField(i);
574 4435 : OGRFieldType eType = OFTString;
575 4435 : OGRFieldSubType eSubType = OFSTNone;
576 4435 : int nWidth = poGDBField->GetMaxWidth();
577 4435 : switch (poGDBField->GetType())
578 : {
579 199 : case FGFT_INT16:
580 199 : eType = OFTInteger;
581 199 : eSubType = OFSTInt16;
582 199 : break;
583 1263 : case FGFT_INT32:
584 1263 : eType = OFTInteger;
585 1263 : break;
586 191 : case FGFT_FLOAT32:
587 191 : eType = OFTReal;
588 191 : eSubType = OFSTFloat32;
589 191 : break;
590 332 : case FGFT_FLOAT64:
591 332 : eType = OFTReal;
592 332 : break;
593 911 : case FGFT_STRING:
594 : /* nWidth = poGDBField->GetMaxWidth(); */
595 911 : eType = OFTString;
596 911 : break;
597 715 : case FGFT_GUID:
598 : case FGFT_GLOBALID:
599 : case FGFT_XML:
600 715 : eType = OFTString;
601 715 : break;
602 205 : case FGFT_DATETIME:
603 205 : eType = OFTDateTime;
604 205 : break;
605 0 : case FGFT_UNDEFINED:
606 : case FGFT_OBJECTID:
607 : case FGFT_GEOMETRY:
608 0 : CPLAssert(false);
609 : break;
610 595 : case FGFT_BINARY:
611 : {
612 : /* Special case for v9 GDB_UserMetadata table */
613 595 : if (m_iFieldToReadAsBinary < 0 &&
614 1190 : poGDBField->GetName() == "Xml" &&
615 0 : poGDBField->GetType() == FGFT_BINARY)
616 : {
617 0 : m_iFieldToReadAsBinary = i;
618 0 : eType = OFTString;
619 : }
620 : else
621 : {
622 595 : eType = OFTBinary;
623 : }
624 595 : break;
625 : }
626 1 : case FGFT_RASTER:
627 : {
628 : const FileGDBRasterField *rasterField =
629 1 : cpl::down_cast<const FileGDBRasterField *>(poGDBField);
630 1 : if (rasterField->GetRasterType() ==
631 : FileGDBRasterField::Type::MANAGED)
632 1 : eType = OFTInteger;
633 0 : else if (rasterField->GetRasterType() ==
634 : FileGDBRasterField::Type::EXTERNAL)
635 0 : eType = OFTString;
636 : else
637 0 : eType = OFTBinary;
638 1 : break;
639 : }
640 4 : case FGFT_INT64:
641 4 : m_bArcGISPro32OrLater = true;
642 4 : eType = OFTInteger64;
643 4 : break;
644 6 : case FGFT_DATE:
645 6 : m_bArcGISPro32OrLater = true;
646 6 : eType = OFTDate;
647 6 : break;
648 6 : case FGFT_TIME:
649 6 : m_bArcGISPro32OrLater = true;
650 6 : eType = OFTTime;
651 6 : break;
652 7 : case FGFT_DATETIME_WITH_OFFSET:
653 7 : m_bArcGISPro32OrLater = true;
654 7 : eType = OFTDateTime;
655 7 : break;
656 : }
657 8870 : OGRFieldDefn oFieldDefn(poGDBField->GetName().c_str(), eType);
658 4435 : oFieldDefn.SetAlternativeName(poGDBField->GetAlias().c_str());
659 4435 : oFieldDefn.SetSubType(eSubType);
660 : // On creation in the FileGDB driver (GDBFieldTypeToLengthInBytes) if
661 : // string width is 0, we pick up DEFAULT_STRING_WIDTH=65536 by default
662 : // to mean unlimited string length, but we do not want to advertise
663 : // such a big number.
664 4702 : if (eType == OFTString &&
665 267 : (nWidth < DEFAULT_STRING_WIDTH ||
666 267 : CPLTestBool(CPLGetConfigOption(
667 : "OPENFILEGDB_REPORT_GENUINE_FIELD_WIDTH", "NO"))))
668 : {
669 1361 : oFieldDefn.SetWidth(nWidth);
670 : }
671 4435 : oFieldDefn.SetNullable(poGDBField->IsNullable());
672 :
673 4435 : const CPLXMLNode *psFieldDef = nullptr;
674 4435 : if (psGPFieldInfoExs != nullptr)
675 : {
676 1716 : for (const CPLXMLNode *psChild = psGPFieldInfoExs->psChild;
677 14361 : psChild != nullptr; psChild = psChild->psNext)
678 : {
679 14352 : if (psChild->eType != CXT_Element)
680 1716 : continue;
681 25272 : if (EQUAL(psChild->pszValue, "GPFieldInfoEx") &&
682 12636 : EQUAL(CPLGetXMLValue(psChild, "Name", ""),
683 : poGDBField->GetName().c_str()))
684 : {
685 1707 : psFieldDef = psChild;
686 1707 : break;
687 : }
688 : }
689 : }
690 :
691 4435 : if (psFieldDef && poGDBField->GetType() == FGFT_DATETIME)
692 : {
693 130 : if (EQUAL(CPLGetXMLValue(psFieldDef, "HighPrecision", ""), "true"))
694 : {
695 2 : poGDBField->SetHighPrecision();
696 : }
697 : }
698 :
699 4435 : const OGRField *psDefault = poGDBField->GetDefault();
700 4435 : if (!OGR_RawField_IsUnset(psDefault) && !OGR_RawField_IsNull(psDefault))
701 : {
702 95 : if (eType == OFTString)
703 : {
704 30 : CPLString osDefault("'");
705 : char *pszTmp =
706 15 : CPLEscapeString(psDefault->String, -1, CPLES_SQL);
707 15 : osDefault += pszTmp;
708 15 : CPLFree(pszTmp);
709 15 : osDefault += "'";
710 15 : oFieldDefn.SetDefault(osDefault);
711 : }
712 80 : else if (eType == OFTInteger || eType == OFTReal ||
713 : eType == OFTInteger64)
714 : {
715 : // GDBs and the FileGDB SDK are not always reliable for
716 : // numeric values It often occurs that the XML definition in
717 : // a00000004.gdbtable does not match the default values (in
718 : // binary) found in the field definition section of the
719 : // .gdbtable of the layers themselves So check consistency.
720 :
721 49 : const char *pszDefaultValue = nullptr;
722 49 : if (psFieldDef)
723 : {
724 : // From ArcGIS this is called DefaultValueNumeric
725 : // for integer and real.
726 : // From FileGDB API this is
727 : // called DefaultValue xsi:type=xs:int for integer
728 : // and DefaultValueNumeric for real ...
729 49 : pszDefaultValue = CPLGetXMLValue(
730 : psFieldDef, "DefaultValueNumeric", nullptr);
731 49 : if (pszDefaultValue == nullptr)
732 : pszDefaultValue =
733 29 : CPLGetXMLValue(psFieldDef, "DefaultValue", nullptr);
734 : // For ArcGIS Pro 3.2 and esriFieldTypeBigInteger, this is
735 : // DefaultValueInteger
736 49 : if (pszDefaultValue == nullptr)
737 16 : pszDefaultValue = CPLGetXMLValue(
738 : psFieldDef, "DefaultValueInteger", nullptr);
739 : }
740 49 : if (pszDefaultValue != nullptr)
741 : {
742 37 : if (eType == OFTInteger)
743 : {
744 20 : if (atoi(pszDefaultValue) != psDefault->Integer)
745 : {
746 1 : CPLDebug(
747 : "OpenFileGDB",
748 : "For field %s, XML definition mentions %s "
749 : "as default value whereas .gdbtable header "
750 : "mentions %d. Using %s",
751 1 : poGDBField->GetName().c_str(), pszDefaultValue,
752 1 : psDefault->Integer, pszDefaultValue);
753 : }
754 20 : oFieldDefn.SetDefault(pszDefaultValue);
755 : }
756 17 : else if (eType == OFTReal)
757 : {
758 13 : if (fabs(CPLAtof(pszDefaultValue) - psDefault->Real) >
759 : 1e-15)
760 : {
761 1 : CPLDebug(
762 : "OpenFileGDB",
763 : "For field %s, XML definition "
764 : "mentions %s as default value whereas "
765 : ".gdbtable header mentions %.17g. Using %s",
766 1 : poGDBField->GetName().c_str(), pszDefaultValue,
767 1 : psDefault->Real, pszDefaultValue);
768 : }
769 13 : oFieldDefn.SetDefault(pszDefaultValue);
770 : }
771 4 : else if (eType == OFTInteger64)
772 : {
773 4 : if (CPLAtoGIntBig(pszDefaultValue) !=
774 4 : psDefault->Integer64)
775 : {
776 0 : CPLDebug(
777 : "OpenFileGDB",
778 : "For field %s, XML definition mentions %s "
779 : "as default value whereas .gdbtable header "
780 : "mentions " CPL_FRMT_GIB ". Using %s",
781 0 : poGDBField->GetName().c_str(), pszDefaultValue,
782 0 : psDefault->Integer64, pszDefaultValue);
783 : }
784 4 : oFieldDefn.SetDefault(pszDefaultValue);
785 : }
786 49 : }
787 : }
788 31 : else if (eType == OFTDateTime)
789 : {
790 19 : if (poGDBField->GetType() == FGFT_DATETIME_WITH_OFFSET)
791 : {
792 14 : oFieldDefn.SetDefault(CPLSPrintf(
793 : "'%04d/%02d/%02d %02d:%02d:%06.03f%c%02d:%02d'",
794 7 : psDefault->Date.Year, psDefault->Date.Month,
795 7 : psDefault->Date.Day, psDefault->Date.Hour,
796 7 : psDefault->Date.Minute, psDefault->Date.Second,
797 7 : psDefault->Date.TZFlag >= 100 ? '+' : '-',
798 7 : std::abs(psDefault->Date.TZFlag - 100) / 4,
799 7 : (std::abs(psDefault->Date.TZFlag - 100) % 4) * 15));
800 : }
801 : else
802 : {
803 12 : oFieldDefn.SetDefault(CPLSPrintf(
804 12 : "'%04d/%02d/%02d %02d:%02d:%02d'", psDefault->Date.Year,
805 12 : psDefault->Date.Month, psDefault->Date.Day,
806 12 : psDefault->Date.Hour, psDefault->Date.Minute,
807 12 : static_cast<int>(psDefault->Date.Second)));
808 : }
809 : }
810 12 : else if (eType == OFTDate)
811 6 : oFieldDefn.SetDefault(
812 6 : CPLSPrintf("'%04d/%02d/%02d'", psDefault->Date.Year,
813 6 : psDefault->Date.Month, psDefault->Date.Day));
814 6 : else if (eType == OFTTime)
815 6 : oFieldDefn.SetDefault(
816 6 : CPLSPrintf("'%02d:%02d:%02d'", psDefault->Date.Hour,
817 6 : psDefault->Date.Minute,
818 6 : static_cast<int>(psDefault->Date.Second)));
819 : }
820 :
821 4435 : if (psFieldDef)
822 : {
823 : const char *pszDomainName =
824 1707 : CPLGetXMLValue(psFieldDef, "DomainName", nullptr);
825 1707 : if (pszDomainName)
826 15 : oFieldDefn.SetDomainName(pszDomainName);
827 : }
828 :
829 4454 : if (osAreaFieldName == poGDBField->GetName() &&
830 19 : oFieldDefn.GetType() == OFTReal)
831 : {
832 19 : m_iAreaField = m_poFeatureDefn->GetFieldCount();
833 19 : oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_AREA");
834 : }
835 4444 : else if (osLengthFieldName == poGDBField->GetName() &&
836 28 : oFieldDefn.GetType() == OFTReal)
837 : {
838 28 : m_iLengthField = m_poFeatureDefn->GetFieldCount();
839 28 : oFieldDefn.SetDefault("FILEGEODATABASE_SHAPE_LENGTH");
840 : }
841 :
842 4435 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
843 : }
844 :
845 814 : if (m_poLyrTable->HasDeletedFeaturesListed())
846 : {
847 0 : OGRFieldDefn oFieldDefn("_deleted_", OFTInteger);
848 0 : m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
849 : }
850 :
851 814 : return TRUE;
852 : }
853 :
854 : /************************************************************************/
855 : /* GetGeomType() */
856 : /************************************************************************/
857 :
858 554 : OGRwkbGeometryType OGROpenFileGDBLayer::GetGeomType()
859 : {
860 1104 : if (m_eGeomType == wkbUnknown ||
861 550 : m_osDefinition.empty() /* FileGDB v9 case */)
862 : {
863 82 : (void)BuildLayerDefinition();
864 : }
865 :
866 554 : return m_eGeomType;
867 : }
868 :
869 : /***********************************************************************/
870 : /* GetLayerDefn() */
871 : /***********************************************************************/
872 :
873 41624 : OGRFeatureDefn *OGROpenFileGDBLayer::GetLayerDefn()
874 : {
875 41624 : return m_poFeatureDefn;
876 : }
877 :
878 : /***********************************************************************/
879 : /* GetFIDColumn() */
880 : /***********************************************************************/
881 :
882 4039 : const char *OGROpenFileGDBLayer::GetFIDColumn()
883 : {
884 4039 : if (!BuildLayerDefinition())
885 0 : return "";
886 4039 : int iIdx = m_poLyrTable->GetObjectIdFieldIdx();
887 4039 : if (iIdx < 0)
888 0 : return "";
889 4039 : return m_poLyrTable->GetField(iIdx)->GetName().c_str();
890 : }
891 :
892 : /***********************************************************************/
893 : /* ResetReading() */
894 : /***********************************************************************/
895 :
896 9497 : void OGROpenFileGDBLayer::ResetReading()
897 : {
898 9497 : if (m_iCurFeat != 0)
899 : {
900 2803 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
901 7 : m_eSpatialIndexState = SPI_INVALID;
902 : }
903 9497 : m_bEOF = FALSE;
904 9497 : m_iCurFeat = 0;
905 9497 : if (m_poAttributeIterator)
906 63 : m_poAttributeIterator->Reset();
907 9497 : if (m_poSpatialIndexIterator)
908 2590 : m_poSpatialIndexIterator->Reset();
909 9497 : if (m_poCombinedIterator)
910 7 : m_poCombinedIterator->Reset();
911 9497 : }
912 :
913 : /***********************************************************************/
914 : /* SetSpatialFilter() */
915 : /***********************************************************************/
916 :
917 3180 : void OGROpenFileGDBLayer::SetSpatialFilter(OGRGeometry *poGeom)
918 : {
919 3180 : if (!BuildLayerDefinition())
920 0 : return;
921 :
922 3180 : OGRLayer::SetSpatialFilter(poGeom);
923 :
924 3180 : if (m_bFilterIsEnvelope)
925 : {
926 1760 : OGREnvelope sLayerEnvelope;
927 1760 : if (GetExtent(&sLayerEnvelope, FALSE) == OGRERR_NONE)
928 : {
929 1728 : if (m_sFilterEnvelope.MinX <= sLayerEnvelope.MinX &&
930 489 : m_sFilterEnvelope.MinY <= sLayerEnvelope.MinY &&
931 479 : m_sFilterEnvelope.MaxX >= sLayerEnvelope.MaxX &&
932 303 : m_sFilterEnvelope.MaxY >= sLayerEnvelope.MaxY)
933 : {
934 : #ifdef DEBUG
935 295 : CPLDebug("OpenFileGDB", "Disabling spatial filter since it "
936 : "contains the layer spatial extent");
937 : #endif
938 295 : poGeom = nullptr;
939 295 : OGRLayer::SetSpatialFilter(poGeom);
940 : }
941 : }
942 : }
943 :
944 3180 : if (poGeom != nullptr)
945 : {
946 399 : if (m_poSpatialIndexIterator == nullptr &&
947 2228 : m_poLyrTable->CanUseIndices() && m_poLyrTable->HasSpatialIndex() &&
948 364 : CPLTestBool(
949 : CPLGetConfigOption("OPENFILEGDB_USE_SPATIAL_INDEX", "YES")))
950 : {
951 358 : m_poSpatialIndexIterator = FileGDBSpatialIndexIterator::Build(
952 358 : m_poLyrTable, m_sFilterEnvelope);
953 : }
954 1107 : else if (m_poSpatialIndexIterator != nullptr)
955 : {
956 1066 : if (!m_poSpatialIndexIterator->SetEnvelope(m_sFilterEnvelope))
957 : {
958 0 : delete m_poSpatialIndexIterator;
959 0 : m_poSpatialIndexIterator = nullptr;
960 : }
961 : }
962 41 : else if (m_eSpatialIndexState == SPI_COMPLETED)
963 : {
964 : CPLRectObj aoi;
965 1 : aoi.minx = m_sFilterEnvelope.MinX;
966 1 : aoi.miny = m_sFilterEnvelope.MinY;
967 1 : aoi.maxx = m_sFilterEnvelope.MaxX;
968 1 : aoi.maxy = m_sFilterEnvelope.MaxY;
969 1 : CPLFree(m_pahFilteredFeatures);
970 1 : m_nFilteredFeatureCount = -1;
971 1 : m_pahFilteredFeatures =
972 1 : CPLQuadTreeSearch(m_pQuadTree, &aoi, &m_nFilteredFeatureCount);
973 1 : if (m_nFilteredFeatureCount >= 0)
974 : {
975 1 : size_t *panStart =
976 : reinterpret_cast<size_t *>(m_pahFilteredFeatures);
977 1 : std::sort(panStart, panStart + m_nFilteredFeatureCount);
978 : }
979 : }
980 :
981 1465 : m_poLyrTable->InstallFilterEnvelope(&m_sFilterEnvelope);
982 : }
983 : else
984 : {
985 1715 : delete m_poSpatialIndexIterator;
986 1715 : m_poSpatialIndexIterator = nullptr;
987 1715 : CPLFree(m_pahFilteredFeatures);
988 1715 : m_pahFilteredFeatures = nullptr;
989 1715 : m_nFilteredFeatureCount = -1;
990 1715 : m_poLyrTable->InstallFilterEnvelope(nullptr);
991 : }
992 :
993 3180 : BuildCombinedIterator();
994 : }
995 :
996 : /***********************************************************************/
997 : /* CompValues() */
998 : /***********************************************************************/
999 :
1000 18 : static int CompValues(OGRFieldDefn *poFieldDefn, const swq_expr_node *poValue1,
1001 : const swq_expr_node *poValue2)
1002 : {
1003 18 : int ret = 0;
1004 18 : switch (poFieldDefn->GetType())
1005 : {
1006 13 : case OFTInteger:
1007 : {
1008 : int n1, n2;
1009 13 : if (poValue1->field_type == SWQ_FLOAT)
1010 1 : n1 = static_cast<int>(poValue1->float_value);
1011 : else
1012 12 : n1 = static_cast<int>(poValue1->int_value);
1013 13 : if (poValue2->field_type == SWQ_FLOAT)
1014 0 : n2 = static_cast<int>(poValue2->float_value);
1015 : else
1016 13 : n2 = static_cast<int>(poValue2->int_value);
1017 13 : if (n1 < n2)
1018 5 : ret = -1;
1019 8 : else if (n1 == n2)
1020 5 : ret = 0;
1021 : else
1022 3 : ret = 1;
1023 13 : break;
1024 : }
1025 :
1026 4 : case OFTReal:
1027 4 : if (poValue1->float_value < poValue2->float_value)
1028 2 : ret = -1;
1029 2 : else if (poValue1->float_value == poValue2->float_value)
1030 2 : ret = 0;
1031 : else
1032 0 : ret = 1;
1033 4 : break;
1034 :
1035 0 : case OFTString:
1036 0 : ret = strcmp(poValue1->string_value, poValue2->string_value);
1037 0 : break;
1038 :
1039 1 : case OFTDate:
1040 : case OFTTime:
1041 : case OFTDateTime:
1042 : {
1043 1 : if ((poValue1->field_type == SWQ_TIMESTAMP ||
1044 0 : poValue1->field_type == SWQ_DATE ||
1045 0 : poValue1->field_type == SWQ_TIME) &&
1046 1 : (poValue2->field_type == SWQ_TIMESTAMP ||
1047 0 : poValue2->field_type == SWQ_DATE ||
1048 0 : poValue2->field_type == SWQ_TIME))
1049 : {
1050 1 : ret = strcmp(poValue1->string_value, poValue2->string_value);
1051 : }
1052 1 : break;
1053 : }
1054 :
1055 0 : default:
1056 0 : break;
1057 : }
1058 18 : return ret;
1059 : }
1060 :
1061 : /***********************************************************************/
1062 : /* OGROpenFileGDBIsComparisonOp() */
1063 : /***********************************************************************/
1064 :
1065 1217 : int OGROpenFileGDBIsComparisonOp(int op)
1066 : {
1067 259 : return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
1068 1476 : op == SWQ_GT || op == SWQ_GE);
1069 : }
1070 :
1071 : /***********************************************************************/
1072 : /* AreExprExclusive() */
1073 : /***********************************************************************/
1074 :
1075 : static const struct
1076 : {
1077 : swq_op op1;
1078 : swq_op op2;
1079 : int expected_comp_1;
1080 : int expected_comp_2;
1081 : } asPairsOfComparisons[] = {
1082 : {SWQ_EQ, SWQ_EQ, -1, 1}, {SWQ_LT, SWQ_GT, -1, 0},
1083 : {SWQ_GT, SWQ_LT, 0, 1}, {SWQ_LT, SWQ_GE, -1, 999},
1084 : {SWQ_LE, SWQ_GE, -1, 999}, {SWQ_LE, SWQ_GT, -1, 999},
1085 : {SWQ_GE, SWQ_LE, 1, 999}, {SWQ_GE, SWQ_LT, 1, 999},
1086 : {SWQ_GT, SWQ_LE, 1, 999}};
1087 :
1088 22 : static int AreExprExclusive(OGRFeatureDefn *poFeatureDefn,
1089 : const swq_expr_node *poNode1,
1090 : const swq_expr_node *poNode2)
1091 : {
1092 22 : if (poNode1->eNodeType != SNT_OPERATION)
1093 0 : return FALSE;
1094 22 : if (poNode2->eNodeType != SNT_OPERATION)
1095 0 : return FALSE;
1096 :
1097 22 : const size_t nPairs =
1098 : sizeof(asPairsOfComparisons) / sizeof(asPairsOfComparisons[0]);
1099 94 : for (size_t i = 0; i < nPairs; i++)
1100 : {
1101 90 : if (poNode1->nOperation == asPairsOfComparisons[i].op1 &&
1102 20 : poNode2->nOperation == asPairsOfComparisons[i].op2 &&
1103 18 : poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 2)
1104 : {
1105 18 : swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1106 18 : swq_expr_node *poValue1 = poNode1->papoSubExpr[1];
1107 18 : swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1108 18 : swq_expr_node *poValue2 = poNode2->papoSubExpr[1];
1109 54 : if (poColumn1->eNodeType == SNT_COLUMN &&
1110 18 : poValue1->eNodeType == SNT_CONSTANT &&
1111 18 : poColumn2->eNodeType == SNT_COLUMN &&
1112 18 : poValue2->eNodeType == SNT_CONSTANT &&
1113 54 : poColumn1->field_index == poColumn2->field_index &&
1114 18 : poColumn1->field_index < poFeatureDefn->GetFieldCount())
1115 : {
1116 : OGRFieldDefn *poFieldDefn =
1117 18 : poFeatureDefn->GetFieldDefn(poColumn1->field_index);
1118 :
1119 18 : const int nComp = CompValues(poFieldDefn, poValue1, poValue2);
1120 26 : return nComp == asPairsOfComparisons[i].expected_comp_1 ||
1121 26 : nComp == asPairsOfComparisons[i].expected_comp_2;
1122 : }
1123 0 : return FALSE;
1124 : }
1125 : }
1126 :
1127 1 : if ((poNode2->nOperation == SWQ_ISNULL &&
1128 1 : OGROpenFileGDBIsComparisonOp(poNode1->nOperation) &&
1129 8 : poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 1) ||
1130 6 : (poNode1->nOperation == SWQ_ISNULL &&
1131 3 : OGROpenFileGDBIsComparisonOp(poNode2->nOperation) &&
1132 1 : poNode2->nSubExprCount == 2 && poNode1->nSubExprCount == 1))
1133 : {
1134 2 : swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1135 2 : swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1136 6 : if (poColumn1->eNodeType == SNT_COLUMN &&
1137 2 : poColumn2->eNodeType == SNT_COLUMN &&
1138 6 : poColumn1->field_index == poColumn2->field_index &&
1139 2 : poColumn1->field_index < poFeatureDefn->GetFieldCount())
1140 : {
1141 2 : return TRUE;
1142 : }
1143 : }
1144 :
1145 : /* In doubt: return FALSE */
1146 2 : return FALSE;
1147 : }
1148 :
1149 : /***********************************************************************/
1150 : /* FillTargetValueFromSrcExpr() */
1151 : /***********************************************************************/
1152 :
1153 337 : static int FillTargetValueFromSrcExpr(OGRFieldDefn *poFieldDefn,
1154 : OGRField *poTargetValue,
1155 : const swq_expr_node *poSrcValue)
1156 : {
1157 337 : switch (poFieldDefn->GetType())
1158 : {
1159 102 : case OFTInteger:
1160 102 : if (poSrcValue->field_type == SWQ_FLOAT)
1161 1 : poTargetValue->Integer =
1162 1 : static_cast<int>(poSrcValue->float_value);
1163 : else
1164 101 : poTargetValue->Integer =
1165 101 : static_cast<int>(poSrcValue->int_value);
1166 102 : break;
1167 :
1168 4 : case OFTInteger64:
1169 4 : if (poSrcValue->field_type == SWQ_FLOAT)
1170 0 : poTargetValue->Integer64 =
1171 0 : static_cast<GIntBig>(poSrcValue->float_value);
1172 : else
1173 4 : poTargetValue->Integer64 = poSrcValue->int_value;
1174 4 : break;
1175 :
1176 38 : case OFTReal:
1177 38 : poTargetValue->Real = poSrcValue->float_value;
1178 38 : break;
1179 :
1180 185 : case OFTString:
1181 185 : poTargetValue->String = poSrcValue->string_value;
1182 185 : break;
1183 :
1184 8 : case OFTDate:
1185 : case OFTTime:
1186 : case OFTDateTime:
1187 8 : if (poSrcValue->field_type == SWQ_TIMESTAMP ||
1188 0 : poSrcValue->field_type == SWQ_DATE ||
1189 0 : poSrcValue->field_type == SWQ_TIME)
1190 : {
1191 8 : int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
1192 8 : nSec = 0;
1193 16 : if (sscanf(poSrcValue->string_value,
1194 : "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
1195 1 : &nDay, &nHour, &nMin, &nSec) == 6 ||
1196 1 : sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
1197 9 : &nMonth, &nDay) == 3 ||
1198 0 : sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
1199 : &nMin, &nSec) == 3)
1200 : {
1201 8 : poTargetValue->Date.Year = static_cast<GInt16>(nYear);
1202 8 : poTargetValue->Date.Month = static_cast<GByte>(nMonth);
1203 8 : poTargetValue->Date.Day = static_cast<GByte>(nDay);
1204 8 : poTargetValue->Date.Hour = static_cast<GByte>(nHour);
1205 8 : poTargetValue->Date.Minute = static_cast<GByte>(nMin);
1206 8 : poTargetValue->Date.Second = static_cast<GByte>(nSec);
1207 8 : poTargetValue->Date.TZFlag = 0;
1208 8 : poTargetValue->Date.Reserved = 0;
1209 : }
1210 : else
1211 8 : return FALSE;
1212 : }
1213 : else
1214 0 : return FALSE;
1215 8 : break;
1216 :
1217 0 : default:
1218 0 : return FALSE;
1219 : }
1220 337 : return TRUE;
1221 : }
1222 :
1223 : /***********************************************************************/
1224 : /* GetColumnSubNode() */
1225 : /***********************************************************************/
1226 :
1227 1184 : static swq_expr_node *GetColumnSubNode(swq_expr_node *poNode)
1228 : {
1229 1184 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1230 : {
1231 1184 : if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
1232 614 : return poNode->papoSubExpr[0];
1233 570 : if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
1234 5 : return poNode->papoSubExpr[1];
1235 : }
1236 565 : return nullptr;
1237 : }
1238 :
1239 : /***********************************************************************/
1240 : /* GetConstantSubNode() */
1241 : /***********************************************************************/
1242 :
1243 1184 : static swq_expr_node *GetConstantSubNode(swq_expr_node *poNode)
1244 : {
1245 1184 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1246 : {
1247 1184 : if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
1248 1177 : return poNode->papoSubExpr[1];
1249 7 : if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
1250 5 : return poNode->papoSubExpr[0];
1251 : }
1252 2 : return nullptr;
1253 : }
1254 :
1255 : /***********************************************************************/
1256 : /* BuildIteratorFromExprNode() */
1257 : /***********************************************************************/
1258 :
1259 : FileGDBIterator *
1260 1246 : OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node *poNode)
1261 : {
1262 1246 : if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1263 0 : return nullptr;
1264 :
1265 1246 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
1266 13 : poNode->nSubExprCount == 2)
1267 : {
1268 : // Even if there is only one branch of the 2 that results to an
1269 : // iterator, it is useful. Of course, the iterator will not be
1270 : // sufficient to evaluatethe filter, but it will be a super-set of the
1271 : // features
1272 : FileGDBIterator *poIter1 =
1273 13 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1274 :
1275 : /* In case the first branch didn't result to an iterator, temporarily */
1276 : /* restore the flag */
1277 : const bool bSaveIteratorSufficientToEvaluateFilter =
1278 13 : CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter);
1279 13 : m_bIteratorSufficientToEvaluateFilter = -1;
1280 : FileGDBIterator *poIter2 =
1281 13 : BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1282 13 : m_bIteratorSufficientToEvaluateFilter =
1283 13 : bSaveIteratorSufficientToEvaluateFilter;
1284 :
1285 13 : if (poIter1 != nullptr && poIter2 != nullptr)
1286 9 : return FileGDBIterator::BuildAnd(poIter1, poIter2, true);
1287 4 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1288 4 : if (poIter1 != nullptr)
1289 2 : return poIter1;
1290 2 : if (poIter2 != nullptr)
1291 2 : return poIter2;
1292 : }
1293 :
1294 1233 : else if (poNode->eNodeType == SNT_OPERATION &&
1295 1233 : poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
1296 : {
1297 : /* For a OR, we need an iterator for the 2 branches */
1298 : FileGDBIterator *poIter1 =
1299 24 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1300 24 : if (poIter1 != nullptr)
1301 : {
1302 : FileGDBIterator *poIter2 =
1303 23 : BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1304 23 : if (poIter2 == nullptr)
1305 : {
1306 1 : delete poIter1;
1307 : }
1308 : else
1309 : {
1310 22 : return FileGDBIterator::BuildOr(
1311 : poIter1, poIter2,
1312 22 : AreExprExclusive(GetLayerDefn(), poNode->papoSubExpr[0],
1313 44 : poNode->papoSubExpr[1]));
1314 : }
1315 2 : }
1316 : }
1317 :
1318 1209 : else if (poNode->eNodeType == SNT_OPERATION &&
1319 1209 : (OGROpenFileGDBIsComparisonOp(poNode->nOperation) ||
1320 2423 : poNode->nOperation == SWQ_ILIKE) &&
1321 1184 : poNode->nSubExprCount == 2)
1322 : {
1323 1184 : swq_expr_node *poColumn = GetColumnSubNode(poNode);
1324 1184 : swq_expr_node *poValue = GetConstantSubNode(poNode);
1325 1801 : if (poColumn != nullptr && poValue != nullptr &&
1326 617 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1327 : {
1328 : OGRFieldDefn *poFieldDefn =
1329 612 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1330 :
1331 : int nTableColIdx =
1332 612 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1333 1224 : if (nTableColIdx >= 0 &&
1334 612 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1335 : {
1336 : OGRField sValue;
1337 :
1338 321 : if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
1339 : {
1340 321 : FileGDBSQLOp eOp = FGSO_EQ;
1341 321 : CPL_IGNORE_RET_VAL(eOp);
1342 321 : if (poColumn == poNode->papoSubExpr[0])
1343 : {
1344 316 : switch (poNode->nOperation)
1345 : {
1346 21 : case SWQ_LE:
1347 21 : eOp = FGSO_LE;
1348 21 : break;
1349 22 : case SWQ_LT:
1350 22 : eOp = FGSO_LT;
1351 22 : break;
1352 10 : case SWQ_NE:
1353 10 : eOp = FGSO_EQ; /* yes : EQ */
1354 10 : break;
1355 215 : case SWQ_EQ:
1356 215 : eOp = FGSO_EQ;
1357 215 : break;
1358 22 : case SWQ_GE:
1359 22 : eOp = FGSO_GE;
1360 22 : break;
1361 21 : case SWQ_GT:
1362 21 : eOp = FGSO_GT;
1363 21 : break;
1364 5 : case SWQ_ILIKE:
1365 5 : eOp = FGSO_ILIKE;
1366 5 : break;
1367 0 : default:
1368 0 : CPLAssert(false);
1369 : break;
1370 : }
1371 : }
1372 : else
1373 : {
1374 : /* If "constant op column", then we must reverse */
1375 : /* the operator */
1376 5 : switch (poNode->nOperation)
1377 : {
1378 1 : case SWQ_LE:
1379 1 : eOp = FGSO_GE;
1380 1 : break;
1381 1 : case SWQ_LT:
1382 1 : eOp = FGSO_GT;
1383 1 : break;
1384 0 : case SWQ_NE:
1385 0 : eOp = FGSO_EQ; /* yes : EQ */
1386 0 : break;
1387 1 : case SWQ_EQ:
1388 1 : eOp = FGSO_EQ;
1389 1 : break;
1390 1 : case SWQ_GE:
1391 1 : eOp = FGSO_LE;
1392 1 : break;
1393 1 : case SWQ_GT:
1394 1 : eOp = FGSO_LT;
1395 1 : break;
1396 0 : case SWQ_ILIKE:
1397 0 : eOp = FGSO_ILIKE;
1398 0 : break;
1399 0 : default:
1400 0 : CPLAssert(false);
1401 : break;
1402 : }
1403 : }
1404 :
1405 321 : bool bIteratorSufficient = true;
1406 321 : auto poField = m_poLyrTable->GetField(nTableColIdx);
1407 642 : std::string osTruncatedStr; // keep it in this scope !
1408 491 : if (poField->GetType() == FGFT_STRING &&
1409 170 : poFieldDefn->GetType() == OFTString)
1410 : {
1411 : // If we have an equality comparison, but the index
1412 : // uses LOWER(), transform it to a ILIKE comparison
1413 323 : if (eOp == FGSO_EQ && poField->HasIndex() &&
1414 153 : STARTS_WITH_CI(
1415 : poField->GetIndex()->GetExpression().c_str(),
1416 : "LOWER("))
1417 : {
1418 : // Note: FileGDBIndexIterator::SetConstraint()
1419 : // checks that the string to compare with has no
1420 : // wildcard
1421 4 : eOp = FGSO_ILIKE;
1422 :
1423 : // In theory, a ILIKE is not sufficient as it is
1424 : // case insensitive, whereas one could expect
1425 : // equality testing to be case sensitive... but
1426 : // it is not in OGR SQL...
1427 : // So we can comment the below line
1428 : // bIteratorSufficient = false;
1429 : }
1430 :
1431 : // As the index use ' ' as padding value, we cannot
1432 : // fully trust the index.
1433 166 : else if ((eOp == FGSO_EQ &&
1434 149 : poNode->nOperation != SWQ_NE) ||
1435 20 : eOp == FGSO_GE)
1436 149 : bIteratorSufficient = false;
1437 : else
1438 17 : return nullptr;
1439 :
1440 : const int nMaxWidthIndexedStr =
1441 153 : poField->GetIndex()->GetMaxWidthInBytes(
1442 153 : m_poLyrTable);
1443 153 : if (nMaxWidthIndexedStr > 0)
1444 : {
1445 304 : wchar_t *pWide = CPLRecodeToWChar(
1446 152 : sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1447 152 : if (pWide)
1448 : {
1449 152 : const size_t nUCS2Len = wcslen(pWide);
1450 152 : if (nUCS2Len * sizeof(uint16_t) >
1451 152 : static_cast<size_t>(nMaxWidthIndexedStr))
1452 : {
1453 2 : pWide[nMaxWidthIndexedStr /
1454 2 : sizeof(uint16_t)] = 0;
1455 2 : char *pszTruncated = CPLRecodeFromWChar(
1456 : pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1457 2 : if (pszTruncated)
1458 : {
1459 2 : osTruncatedStr = pszTruncated;
1460 2 : sValue.String = &osTruncatedStr[0];
1461 2 : CPLFree(pszTruncated);
1462 : }
1463 : }
1464 152 : CPLFree(pWide);
1465 : }
1466 : }
1467 : }
1468 151 : else if (eOp == FGSO_ILIKE)
1469 0 : return nullptr;
1470 :
1471 304 : FileGDBIterator *poIter = FileGDBIterator::Build(
1472 : m_poLyrTable, nTableColIdx, TRUE, eOp,
1473 : poFieldDefn->GetType(), &sValue);
1474 304 : if (poIter != nullptr)
1475 299 : m_bIteratorSufficientToEvaluateFilter =
1476 : bIteratorSufficient;
1477 304 : if (poIter && poNode->nOperation == SWQ_NE)
1478 7 : return FileGDBIterator::BuildNot(poIter);
1479 : else
1480 297 : return poIter;
1481 : }
1482 : }
1483 : }
1484 : }
1485 25 : else if (poNode->eNodeType == SNT_OPERATION &&
1486 25 : poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
1487 : {
1488 8 : swq_expr_node *poColumn = poNode->papoSubExpr[0];
1489 16 : if (poColumn->eNodeType == SNT_COLUMN &&
1490 8 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1491 : {
1492 : OGRFieldDefn *poFieldDefn =
1493 7 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1494 :
1495 : int nTableColIdx =
1496 7 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1497 14 : if (nTableColIdx >= 0 &&
1498 7 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1499 : {
1500 7 : FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1501 : m_poLyrTable, nTableColIdx, TRUE);
1502 7 : if (poIter)
1503 : {
1504 7 : m_bIteratorSufficientToEvaluateFilter = TRUE;
1505 7 : poIter = FileGDBIterator::BuildNot(poIter);
1506 : }
1507 7 : return poIter;
1508 : }
1509 1 : }
1510 : }
1511 17 : else if (poNode->eNodeType == SNT_OPERATION &&
1512 17 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
1513 8 : poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
1514 8 : poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
1515 6 : poNode->papoSubExpr[0]->nSubExprCount == 1)
1516 : {
1517 6 : swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
1518 12 : if (poColumn->eNodeType == SNT_COLUMN &&
1519 6 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1520 : {
1521 : OGRFieldDefn *poFieldDefn =
1522 5 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1523 :
1524 : int nTableColIdx =
1525 5 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1526 10 : if (nTableColIdx >= 0 &&
1527 5 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1528 : {
1529 5 : FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1530 : m_poLyrTable, nTableColIdx, TRUE);
1531 5 : if (poIter)
1532 5 : m_bIteratorSufficientToEvaluateFilter = TRUE;
1533 5 : return poIter;
1534 : }
1535 1 : }
1536 : }
1537 11 : else if (poNode->eNodeType == SNT_OPERATION &&
1538 11 : poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2)
1539 : {
1540 9 : swq_expr_node *poColumn = poNode->papoSubExpr[0];
1541 18 : if (poColumn->eNodeType == SNT_COLUMN &&
1542 9 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1543 : {
1544 8 : bool bAllConstants = true;
1545 22 : for (int i = 1; i < poNode->nSubExprCount; i++)
1546 : {
1547 14 : if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
1548 0 : bAllConstants = false;
1549 : }
1550 : OGRFieldDefn *poFieldDefn =
1551 8 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1552 :
1553 : int nTableColIdx =
1554 8 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1555 16 : if (bAllConstants && nTableColIdx >= 0 &&
1556 8 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1557 : {
1558 8 : FileGDBIterator *poRet = nullptr;
1559 :
1560 8 : bool bIteratorSufficient = true;
1561 8 : auto poField = m_poLyrTable->GetField(nTableColIdx);
1562 :
1563 22 : for (int i = 1; i < poNode->nSubExprCount; i++)
1564 : {
1565 : OGRField sValue;
1566 14 : if (!FillTargetValueFromSrcExpr(poFieldDefn, &sValue,
1567 14 : poNode->papoSubExpr[i]))
1568 : {
1569 0 : delete poRet;
1570 0 : poRet = nullptr;
1571 0 : break;
1572 : }
1573 :
1574 14 : std::string osTruncatedStr; // keep it in this scope !
1575 22 : if (poField->GetType() == FGFT_STRING &&
1576 8 : poFieldDefn->GetType() == OFTString)
1577 : {
1578 : const int nMaxWidthIndexedStr =
1579 8 : poField->GetIndex()->GetMaxWidthInBytes(
1580 8 : m_poLyrTable);
1581 8 : if (nMaxWidthIndexedStr > 0)
1582 : {
1583 16 : wchar_t *pWide = CPLRecodeToWChar(
1584 8 : sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1585 8 : if (pWide)
1586 : {
1587 8 : const size_t nUCS2Len = wcslen(pWide);
1588 8 : if (nUCS2Len * sizeof(uint16_t) >
1589 8 : static_cast<size_t>(nMaxWidthIndexedStr))
1590 : {
1591 1 : pWide[nMaxWidthIndexedStr /
1592 1 : sizeof(uint16_t)] = 0;
1593 1 : char *pszTruncated = CPLRecodeFromWChar(
1594 : pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1595 1 : if (pszTruncated)
1596 : {
1597 1 : osTruncatedStr = pszTruncated;
1598 1 : sValue.String = &osTruncatedStr[0];
1599 1 : CPLFree(pszTruncated);
1600 : }
1601 : }
1602 8 : CPLFree(pWide);
1603 : }
1604 : }
1605 :
1606 : // As the index use ' ' as padding value, we cannot
1607 : // fully trust the index.
1608 8 : bIteratorSufficient = false;
1609 : }
1610 :
1611 14 : FileGDBIterator *poIter = FileGDBIterator::Build(
1612 : m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ,
1613 : poFieldDefn->GetType(), &sValue);
1614 14 : if (poIter == nullptr)
1615 : {
1616 0 : delete poRet;
1617 0 : poRet = nullptr;
1618 0 : break;
1619 : }
1620 14 : if (poRet == nullptr)
1621 8 : poRet = poIter;
1622 : else
1623 6 : poRet = FileGDBIterator::BuildOr(poRet, poIter);
1624 : }
1625 8 : if (poRet != nullptr)
1626 : {
1627 8 : m_bIteratorSufficientToEvaluateFilter = bIteratorSufficient;
1628 8 : return poRet;
1629 : }
1630 : }
1631 1 : }
1632 : }
1633 2 : else if (poNode->eNodeType == SNT_OPERATION &&
1634 2 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
1635 : {
1636 : FileGDBIterator *poIter =
1637 2 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1638 : /* If we have an iterator that is only partial w.r.t the full clause */
1639 : /* then we cannot do anything with it unfortunately */
1640 2 : if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1641 : {
1642 1 : if (poIter != nullptr)
1643 1 : CPLDebug("OpenFileGDB", "Disabling use of indexes");
1644 1 : delete poIter;
1645 : }
1646 1 : else if (poIter != nullptr)
1647 : {
1648 1 : return FileGDBIterator::BuildNot(poIter);
1649 : }
1650 : }
1651 :
1652 870 : if (m_bIteratorSufficientToEvaluateFilter == TRUE)
1653 1 : CPLDebug("OpenFileGDB", "Disabling use of indexes");
1654 870 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1655 870 : return nullptr;
1656 : }
1657 :
1658 : /***********************************************************************/
1659 : /* SetAttributeFilter() */
1660 : /***********************************************************************/
1661 :
1662 2843 : OGRErr OGROpenFileGDBLayer::SetAttributeFilter(const char *pszFilter)
1663 : {
1664 2843 : if (!BuildLayerDefinition())
1665 0 : return OGRERR_FAILURE;
1666 :
1667 2843 : delete m_poAttributeIterator;
1668 2843 : m_poAttributeIterator = nullptr;
1669 2843 : delete m_poCombinedIterator;
1670 2843 : m_poCombinedIterator = nullptr;
1671 2843 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1672 :
1673 2843 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1674 5686 : if (eErr != OGRERR_NONE ||
1675 2843 : !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
1676 0 : return eErr;
1677 :
1678 2843 : if (m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0)
1679 : {
1680 : swq_expr_node *poNode =
1681 1171 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
1682 1171 : poNode->ReplaceBetweenByGEAndLERecurse();
1683 1171 : m_bIteratorSufficientToEvaluateFilter = -1;
1684 1171 : m_poAttributeIterator = BuildIteratorFromExprNode(poNode);
1685 1171 : if (m_poAttributeIterator != nullptr &&
1686 286 : m_eSpatialIndexState == SPI_IN_BUILDING)
1687 48 : m_eSpatialIndexState = SPI_INVALID;
1688 1171 : if (m_bIteratorSufficientToEvaluateFilter < 0)
1689 21 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1690 : }
1691 :
1692 2843 : BuildCombinedIterator();
1693 :
1694 2843 : return eErr;
1695 : }
1696 :
1697 : /***********************************************************************/
1698 : /* BuildCombinedIterator() */
1699 : /***********************************************************************/
1700 :
1701 6023 : void OGROpenFileGDBLayer::BuildCombinedIterator()
1702 : {
1703 6023 : delete m_poCombinedIterator;
1704 6023 : if (m_poAttributeIterator && m_poSpatialIndexIterator)
1705 : {
1706 2 : m_poCombinedIterator = FileGDBIterator::BuildAnd(
1707 2 : m_poAttributeIterator, m_poSpatialIndexIterator, false);
1708 : }
1709 : else
1710 : {
1711 6021 : m_poCombinedIterator = nullptr;
1712 : }
1713 6023 : }
1714 :
1715 : /***********************************************************************/
1716 : /* GetCurrentFeature() */
1717 : /***********************************************************************/
1718 :
1719 38461 : OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature()
1720 : {
1721 38461 : OGRFeature *poFeature = nullptr;
1722 38461 : int iOGRIdx = 0;
1723 38461 : int64_t iRow = m_poLyrTable->GetCurRow();
1724 213458 : for (int iGDBIdx = 0; iGDBIdx < m_poLyrTable->GetFieldCount(); iGDBIdx++)
1725 : {
1726 185998 : if (iOGRIdx == m_iFIDAsRegularColumnIndex)
1727 6 : iOGRIdx++;
1728 :
1729 185998 : if (iGDBIdx == m_iGeomFieldIdx)
1730 : {
1731 22684 : if (m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
1732 : {
1733 275 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
1734 0 : m_eSpatialIndexState = SPI_INVALID;
1735 275 : continue;
1736 : }
1737 :
1738 22409 : const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1739 22409 : if (psField != nullptr)
1740 : {
1741 19701 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
1742 : {
1743 67 : OGREnvelope sFeatureEnvelope;
1744 67 : if (m_poLyrTable->GetFeatureExtent(psField,
1745 67 : &sFeatureEnvelope))
1746 : {
1747 : #if SIZEOF_VOIDP < 8
1748 : if (iRow > INT32_MAX)
1749 : {
1750 : // m_pQuadTree stores iRow values as void*
1751 : // This would overflow here.
1752 : m_eSpatialIndexState = SPI_INVALID;
1753 : }
1754 : else
1755 : #endif
1756 : {
1757 : CPLRectObj sBounds;
1758 67 : sBounds.minx = sFeatureEnvelope.MinX;
1759 67 : sBounds.miny = sFeatureEnvelope.MinY;
1760 67 : sBounds.maxx = sFeatureEnvelope.MaxX;
1761 67 : sBounds.maxy = sFeatureEnvelope.MaxY;
1762 67 : CPLQuadTreeInsertWithBounds(
1763 : m_pQuadTree,
1764 : reinterpret_cast<void *>(
1765 : static_cast<uintptr_t>(iRow)),
1766 : &sBounds);
1767 : }
1768 : }
1769 : }
1770 :
1771 51753 : if (m_poFilterGeom != nullptr &&
1772 32041 : m_eSpatialIndexState != SPI_COMPLETED &&
1773 12340 : !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
1774 : psField))
1775 : {
1776 11001 : delete poFeature;
1777 11001 : return nullptr;
1778 : }
1779 :
1780 8700 : OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField);
1781 8700 : if (poGeom != nullptr)
1782 : {
1783 : OGRwkbGeometryType eFlattenType =
1784 8700 : wkbFlatten(poGeom->getGeometryType());
1785 8700 : if (eFlattenType == wkbPolygon)
1786 : poGeom =
1787 1916 : OGRGeometryFactory::forceToMultiPolygon(poGeom);
1788 6784 : else if (eFlattenType == wkbCurvePolygon)
1789 : {
1790 29 : OGRMultiSurface *poMS = new OGRMultiSurface();
1791 29 : poMS->addGeometryDirectly(poGeom);
1792 29 : poGeom = poMS;
1793 : }
1794 6755 : else if (eFlattenType == wkbLineString)
1795 : poGeom =
1796 1681 : OGRGeometryFactory::forceToMultiLineString(poGeom);
1797 5074 : else if (eFlattenType == wkbCompoundCurve)
1798 : {
1799 17 : OGRMultiCurve *poMC = new OGRMultiCurve();
1800 17 : poMC->addGeometryDirectly(poGeom);
1801 17 : poGeom = poMC;
1802 : }
1803 :
1804 8700 : poGeom->assignSpatialReference(
1805 8700 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
1806 :
1807 8700 : if (poFeature == nullptr)
1808 8696 : poFeature = new OGRFeature(m_poFeatureDefn);
1809 8700 : poFeature->SetGeometryDirectly(poGeom);
1810 : }
1811 : }
1812 : }
1813 163314 : else if (iGDBIdx != m_poLyrTable->GetObjectIdFieldIdx())
1814 : {
1815 : const OGRFieldDefn *poFieldDefn =
1816 135850 : m_poFeatureDefn->GetFieldDefn(iOGRIdx);
1817 135850 : if (!poFieldDefn->IsIgnored())
1818 : {
1819 134401 : const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1820 134401 : if (poFeature == nullptr)
1821 17504 : poFeature = new OGRFeature(m_poFeatureDefn);
1822 134401 : if (psField == nullptr)
1823 : {
1824 19670 : poFeature->SetFieldNull(iOGRIdx);
1825 : }
1826 : else
1827 : {
1828 :
1829 114731 : if (iGDBIdx == m_iFieldToReadAsBinary)
1830 0 : poFeature->SetField(iOGRIdx,
1831 : reinterpret_cast<const char *>(
1832 0 : psField->Binary.paData));
1833 114731 : else if (poFieldDefn->GetType() == OFTDateTime)
1834 : {
1835 7623 : OGRField sField = *psField;
1836 7623 : if (m_poLyrTable->GetField(iGDBIdx)->GetType() ==
1837 : FGFT_DATETIME)
1838 : {
1839 7599 : sField.Date.TZFlag = m_bTimeInUTC ? 100 : 0;
1840 : }
1841 7623 : poFeature->SetField(iOGRIdx, &sField);
1842 : }
1843 : else
1844 107108 : poFeature->SetField(iOGRIdx, psField);
1845 : }
1846 : }
1847 135850 : iOGRIdx++;
1848 : }
1849 : }
1850 :
1851 27460 : if (poFeature == nullptr)
1852 1260 : poFeature = new OGRFeature(m_poFeatureDefn);
1853 :
1854 27460 : if (m_poLyrTable->HasDeletedFeaturesListed())
1855 : {
1856 0 : poFeature->SetField(poFeature->GetFieldCount() - 1,
1857 0 : m_poLyrTable->IsCurRowDeleted());
1858 : }
1859 :
1860 27460 : poFeature->SetFID(iRow + 1);
1861 :
1862 27460 : if (m_iFIDAsRegularColumnIndex >= 0)
1863 6 : poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
1864 :
1865 27460 : return poFeature;
1866 : }
1867 :
1868 : /***********************************************************************/
1869 : /* GetNextFeature() */
1870 : /***********************************************************************/
1871 :
1872 22091 : OGRFeature *OGROpenFileGDBLayer::GetNextFeature()
1873 : {
1874 22091 : if (!BuildLayerDefinition() || m_bEOF)
1875 4 : return nullptr;
1876 :
1877 44168 : FileGDBIterator *poIterator = m_poCombinedIterator ? m_poCombinedIterator
1878 22081 : : m_poSpatialIndexIterator
1879 22081 : ? m_poSpatialIndexIterator
1880 : : m_poAttributeIterator;
1881 :
1882 : while (true)
1883 : {
1884 28556 : OGRFeature *poFeature = nullptr;
1885 :
1886 28556 : if (m_nFilteredFeatureCount >= 0)
1887 : {
1888 : while (true)
1889 : {
1890 3 : if (m_iCurFeat >= m_nFilteredFeatureCount)
1891 : {
1892 1 : return nullptr;
1893 : }
1894 : const auto iRow =
1895 : static_cast<int64_t>(reinterpret_cast<GUIntptr_t>(
1896 2 : m_pahFilteredFeatures[m_iCurFeat++]));
1897 2 : if (m_poLyrTable->SelectRow(iRow))
1898 : {
1899 2 : poFeature = GetCurrentFeature();
1900 2 : if (poFeature)
1901 2 : break;
1902 : }
1903 0 : else if (m_poLyrTable->HasGotError())
1904 : {
1905 0 : m_bEOF = TRUE;
1906 0 : return nullptr;
1907 : }
1908 0 : }
1909 : }
1910 28553 : else if (poIterator != nullptr)
1911 : {
1912 : while (true)
1913 : {
1914 12600 : const auto iRow = poIterator->GetNextRowSortedByFID();
1915 12600 : if (iRow < 0)
1916 308 : return nullptr;
1917 12292 : if (m_poLyrTable->SelectRow(iRow))
1918 : {
1919 12292 : poFeature = GetCurrentFeature();
1920 12292 : if (poFeature)
1921 3410 : break;
1922 : }
1923 0 : else if (m_poLyrTable->HasGotError())
1924 : {
1925 0 : m_bEOF = TRUE;
1926 0 : return nullptr;
1927 : }
1928 8882 : }
1929 : }
1930 : else
1931 : {
1932 : while (true)
1933 : {
1934 26954 : if (m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1935 : {
1936 1755 : return nullptr;
1937 : }
1938 25199 : m_iCurFeat =
1939 25199 : m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat);
1940 25199 : if (m_iCurFeat < 0)
1941 : {
1942 75 : m_bEOF = TRUE;
1943 75 : return nullptr;
1944 : }
1945 : else
1946 : {
1947 25124 : m_iCurFeat++;
1948 25124 : poFeature = GetCurrentFeature();
1949 26810 : if (m_eSpatialIndexState == SPI_IN_BUILDING &&
1950 1686 : m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1951 : {
1952 165 : CPLDebug("OpenFileGDB", "SPI_COMPLETED");
1953 165 : m_eSpatialIndexState = SPI_COMPLETED;
1954 : }
1955 25124 : if (poFeature)
1956 23005 : break;
1957 : }
1958 : }
1959 : }
1960 :
1961 54266 : if ((m_poFilterGeom == nullptr ||
1962 52734 : FilterGeometry(poFeature->GetGeometryRef())) &&
1963 26317 : (m_poAttrQuery == nullptr ||
1964 13974 : (m_poAttributeIterator != nullptr &&
1965 2303 : m_bIteratorSufficientToEvaluateFilter) ||
1966 11826 : m_poAttrQuery->Evaluate(poFeature)))
1967 : {
1968 19948 : return poFeature;
1969 : }
1970 :
1971 6469 : delete poFeature;
1972 6469 : }
1973 : }
1974 :
1975 : /***********************************************************************/
1976 : /* GetFeature() */
1977 : /***********************************************************************/
1978 :
1979 1324 : OGRFeature *OGROpenFileGDBLayer::GetFeature(GIntBig nFeatureId)
1980 : {
1981 1324 : if (!BuildLayerDefinition())
1982 0 : return nullptr;
1983 :
1984 1324 : if (nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount())
1985 279 : return nullptr;
1986 1045 : if (!m_poLyrTable->SelectRow(nFeatureId - 1))
1987 2 : return nullptr;
1988 :
1989 : /* Temporarily disable spatial filter */
1990 1043 : OGRGeometry *poOldSpatialFilter = m_poFilterGeom;
1991 1043 : m_poFilterGeom = nullptr;
1992 : /* and also spatial index state to avoid features to be inserted */
1993 : /* multiple times in spatial index */
1994 1043 : SPIState eOldState = m_eSpatialIndexState;
1995 1043 : m_eSpatialIndexState = SPI_INVALID;
1996 :
1997 1043 : OGRFeature *poFeature = GetCurrentFeature();
1998 :
1999 : /* Set it back */
2000 1043 : m_poFilterGeom = poOldSpatialFilter;
2001 1043 : m_eSpatialIndexState = eOldState;
2002 :
2003 1043 : return poFeature;
2004 : }
2005 :
2006 : /***********************************************************************/
2007 : /* SetNextByIndex() */
2008 : /***********************************************************************/
2009 :
2010 314 : OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex)
2011 : {
2012 314 : if (m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr)
2013 0 : return OGRLayer::SetNextByIndex(nIndex);
2014 :
2015 314 : if (!BuildLayerDefinition())
2016 0 : return OGRERR_FAILURE;
2017 :
2018 314 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2019 1 : m_eSpatialIndexState = SPI_INVALID;
2020 :
2021 314 : if (m_nFilteredFeatureCount >= 0)
2022 : {
2023 3 : if (nIndex < 0 || nIndex >= m_nFilteredFeatureCount)
2024 2 : return OGRERR_FAILURE;
2025 1 : m_iCurFeat = nIndex;
2026 1 : return OGRERR_NONE;
2027 : }
2028 622 : else if (m_poLyrTable->GetValidRecordCount() ==
2029 311 : m_poLyrTable->GetTotalRecordCount())
2030 : {
2031 283 : if (nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount())
2032 158 : return OGRERR_FAILURE;
2033 125 : m_iCurFeat = nIndex;
2034 125 : return OGRERR_NONE;
2035 : }
2036 : else
2037 28 : return OGRLayer::SetNextByIndex(nIndex);
2038 : }
2039 :
2040 : /***********************************************************************/
2041 : /* GetExtent() */
2042 : /***********************************************************************/
2043 :
2044 2074 : OGRErr OGROpenFileGDBLayer::GetExtent(OGREnvelope *psExtent, int /* bForce */)
2045 : {
2046 2074 : if (!BuildLayerDefinition())
2047 0 : return OGRERR_FAILURE;
2048 :
2049 2074 : if (m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2050 : {
2051 : FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2052 2038 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2053 2038 : if (!std::isnan(poGDBGeomField->GetXMin()))
2054 : {
2055 1985 : psExtent->MinX = poGDBGeomField->GetXMin();
2056 1985 : psExtent->MinY = poGDBGeomField->GetYMin();
2057 1985 : psExtent->MaxX = poGDBGeomField->GetXMax();
2058 1985 : psExtent->MaxY = poGDBGeomField->GetYMax();
2059 1985 : return OGRERR_NONE;
2060 : }
2061 : }
2062 :
2063 89 : return OGRERR_FAILURE;
2064 : }
2065 :
2066 : /***********************************************************************/
2067 : /* GetExtent3D() */
2068 : /***********************************************************************/
2069 :
2070 5 : OGRErr OGROpenFileGDBLayer::GetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
2071 : int bForce)
2072 : {
2073 5 : if (!BuildLayerDefinition())
2074 0 : return OGRERR_FAILURE;
2075 :
2076 5 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2077 10 : m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2078 : {
2079 : FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2080 3 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2081 3 : if (!std::isnan(poGDBGeomField->GetXMin()))
2082 : {
2083 3 : psExtent->MinX = poGDBGeomField->GetXMin();
2084 3 : psExtent->MinY = poGDBGeomField->GetYMin();
2085 3 : psExtent->MaxX = poGDBGeomField->GetXMax();
2086 3 : psExtent->MaxY = poGDBGeomField->GetYMax();
2087 3 : if (!std::isnan(poGDBGeomField->GetZMin()))
2088 : {
2089 1 : psExtent->MinZ = poGDBGeomField->GetZMin();
2090 1 : psExtent->MaxZ = poGDBGeomField->GetZMax();
2091 : }
2092 : else
2093 : {
2094 2 : if (OGR_GT_HasZ(m_eGeomType))
2095 : {
2096 1 : return OGRLayer::GetExtent3D(iGeomField, psExtent, bForce);
2097 : }
2098 1 : psExtent->MinZ = std::numeric_limits<double>::infinity();
2099 1 : psExtent->MaxZ = -std::numeric_limits<double>::infinity();
2100 : }
2101 2 : return OGRERR_NONE;
2102 : }
2103 : }
2104 :
2105 2 : return OGRLayer::GetExtent3D(iGeomField, psExtent, bForce);
2106 : }
2107 :
2108 : /***********************************************************************/
2109 : /* GetFeatureCount() */
2110 : /***********************************************************************/
2111 :
2112 1334 : GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce)
2113 : {
2114 1334 : if (!BuildLayerDefinition())
2115 1 : return 0;
2116 :
2117 : /* No filter */
2118 1333 : if ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2119 1242 : m_poAttrQuery == nullptr)
2120 : {
2121 860 : return m_poLyrTable->GetValidRecordCount();
2122 : }
2123 473 : else if (m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr)
2124 : {
2125 1 : return m_nFilteredFeatureCount;
2126 : }
2127 :
2128 : /* Only geometry filter ? */
2129 472 : if (m_poAttrQuery == nullptr && m_bFilterIsEnvelope)
2130 : {
2131 80 : if (m_poSpatialIndexIterator)
2132 : {
2133 61 : m_poSpatialIndexIterator->Reset();
2134 61 : int nCount = 0;
2135 : while (true)
2136 : {
2137 : const auto nRowIdx =
2138 278 : m_poSpatialIndexIterator->GetNextRowSortedByFID();
2139 278 : if (nRowIdx < 0)
2140 61 : break;
2141 217 : if (!m_poLyrTable->SelectRow(nRowIdx))
2142 : {
2143 0 : if (m_poLyrTable->HasGotError())
2144 0 : break;
2145 : else
2146 0 : continue;
2147 : }
2148 :
2149 : const OGRField *psField =
2150 217 : m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2151 217 : if (psField != nullptr)
2152 : {
2153 217 : if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
2154 217 : psField))
2155 : {
2156 : OGRGeometry *poGeom =
2157 174 : m_poGeomConverter->GetAsGeometry(psField);
2158 174 : if (poGeom != nullptr && FilterGeometry(poGeom))
2159 : {
2160 164 : nCount++;
2161 : }
2162 174 : delete poGeom;
2163 : }
2164 : }
2165 217 : }
2166 61 : return nCount;
2167 : }
2168 :
2169 19 : int nCount = 0;
2170 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0)
2171 0 : m_eSpatialIndexState = SPI_INVALID;
2172 :
2173 19 : int nFilteredFeatureCountAlloc = 0;
2174 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2175 : {
2176 2 : CPLFree(m_pahFilteredFeatures);
2177 2 : m_pahFilteredFeatures = nullptr;
2178 2 : m_nFilteredFeatureCount = 0;
2179 : }
2180 :
2181 1492240 : for (int64_t i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++)
2182 : {
2183 1492220 : if (!m_poLyrTable->SelectRow(i))
2184 : {
2185 1489660 : if (m_poLyrTable->HasGotError())
2186 0 : break;
2187 : else
2188 1489660 : continue;
2189 : }
2190 : #if SIZEOF_VOIDP < 8
2191 : if (i > INT32_MAX)
2192 : {
2193 : // CPLQuadTreeInsertWithBounds stores row index values as void*
2194 : // This would overflow here.
2195 : m_eSpatialIndexState = SPI_INVALID;
2196 : break;
2197 : }
2198 : #endif
2199 :
2200 : const OGRField *psField =
2201 2562 : m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2202 2562 : if (psField != nullptr)
2203 : {
2204 2562 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2205 : {
2206 14 : OGREnvelope sFeatureEnvelope;
2207 14 : if (m_poLyrTable->GetFeatureExtent(psField,
2208 14 : &sFeatureEnvelope))
2209 : {
2210 : CPLRectObj sBounds;
2211 14 : sBounds.minx = sFeatureEnvelope.MinX;
2212 14 : sBounds.miny = sFeatureEnvelope.MinY;
2213 14 : sBounds.maxx = sFeatureEnvelope.MaxX;
2214 14 : sBounds.maxy = sFeatureEnvelope.MaxY;
2215 14 : CPLQuadTreeInsertWithBounds(
2216 : m_pQuadTree,
2217 : reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
2218 : &sBounds);
2219 : }
2220 : }
2221 :
2222 2562 : if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField))
2223 : {
2224 : OGRGeometry *poGeom =
2225 2315 : m_poGeomConverter->GetAsGeometry(psField);
2226 2315 : if (poGeom != nullptr && FilterGeometry(poGeom))
2227 : {
2228 2305 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2229 : {
2230 1 : if (nCount == nFilteredFeatureCountAlloc)
2231 : {
2232 1 : nFilteredFeatureCountAlloc =
2233 1 : 4 * nFilteredFeatureCountAlloc / 3 + 1024;
2234 1 : m_pahFilteredFeatures = static_cast<void **>(
2235 1 : CPLRealloc(m_pahFilteredFeatures,
2236 : sizeof(void *) *
2237 1 : nFilteredFeatureCountAlloc));
2238 : }
2239 1 : m_pahFilteredFeatures[nCount] =
2240 : reinterpret_cast<void *>(
2241 : static_cast<uintptr_t>(i));
2242 : }
2243 2305 : nCount++;
2244 : }
2245 2315 : delete poGeom;
2246 : }
2247 : }
2248 : }
2249 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2250 : {
2251 2 : m_nFilteredFeatureCount = nCount;
2252 2 : m_eSpatialIndexState = SPI_COMPLETED;
2253 : }
2254 :
2255 19 : return nCount;
2256 : }
2257 : /* Only simple attribute filter ? */
2258 392 : else if (m_poFilterGeom == nullptr && m_poAttributeIterator != nullptr &&
2259 127 : m_bIteratorSufficientToEvaluateFilter)
2260 : {
2261 118 : return m_poAttributeIterator->GetRowCount();
2262 : }
2263 :
2264 274 : return OGRLayer::GetFeatureCount(bForce);
2265 : }
2266 :
2267 : /***********************************************************************/
2268 : /* TestCapability() */
2269 : /***********************************************************************/
2270 :
2271 3767 : int OGROpenFileGDBLayer::TestCapability(const char *pszCap)
2272 : {
2273 3767 : if (!BuildLayerDefinition())
2274 0 : return FALSE;
2275 :
2276 3767 : if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
2277 3486 : EQUAL(pszCap, OLCAlterFieldDefn) ||
2278 3396 : EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
2279 3395 : EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
2280 3200 : EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRename))
2281 : {
2282 658 : return m_bEditable;
2283 : }
2284 :
2285 3109 : if (EQUAL(pszCap, OLCFastFeatureCount))
2286 : {
2287 4 : return ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2288 4 : m_poAttrQuery == nullptr);
2289 : }
2290 3107 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
2291 : {
2292 1 : return (m_poLyrTable->GetValidRecordCount() ==
2293 1 : m_poLyrTable->GetTotalRecordCount() &&
2294 2 : m_poAttributeIterator == nullptr &&
2295 2 : m_poSpatialIndexIterator == nullptr);
2296 : }
2297 3106 : else if (EQUAL(pszCap, OLCRandomRead))
2298 : {
2299 0 : return TRUE;
2300 : }
2301 3106 : else if (EQUAL(pszCap, OLCFastGetExtent))
2302 : {
2303 112 : return TRUE;
2304 : }
2305 2994 : else if (EQUAL(pszCap, OLCFastGetExtent3D))
2306 : {
2307 5 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2308 10 : m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2309 : {
2310 : FileGDBGeomField *poGDBGeomField =
2311 : reinterpret_cast<FileGDBGeomField *>(
2312 3 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2313 3 : if (!std::isnan(poGDBGeomField->GetXMin()))
2314 : {
2315 3 : if (!std::isnan(poGDBGeomField->GetZMin()))
2316 : {
2317 1 : return TRUE;
2318 : }
2319 : else
2320 : {
2321 2 : return !OGR_GT_HasZ(m_eGeomType);
2322 : }
2323 : }
2324 : }
2325 2 : return FALSE;
2326 : }
2327 2989 : else if (EQUAL(pszCap, OLCIgnoreFields))
2328 : {
2329 89 : return TRUE;
2330 : }
2331 2900 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2332 : {
2333 1260 : return TRUE; /* ? */
2334 : }
2335 :
2336 1640 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
2337 572 : return TRUE;
2338 :
2339 1068 : else if (EQUAL(pszCap, OLCCurveGeometries))
2340 596 : return TRUE;
2341 :
2342 472 : else if (EQUAL(pszCap, OLCZGeometries))
2343 267 : return TRUE;
2344 :
2345 205 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2346 : {
2347 3 : return m_eSpatialIndexState == SPI_COMPLETED ||
2348 1 : (m_poLyrTable->CanUseIndices() &&
2349 2 : m_poLyrTable->HasSpatialIndex());
2350 : }
2351 :
2352 204 : return FALSE;
2353 : }
2354 :
2355 : /***********************************************************************/
2356 : /* HasIndexForField() */
2357 : /***********************************************************************/
2358 :
2359 25 : bool OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName)
2360 : {
2361 25 : if (!BuildLayerDefinition())
2362 0 : return false;
2363 25 : if (!m_poLyrTable->CanUseIndices())
2364 0 : return false;
2365 25 : int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2366 49 : return (nTableColIdx >= 0 &&
2367 49 : m_poLyrTable->GetField(nTableColIdx)->HasIndex());
2368 : }
2369 :
2370 : /***********************************************************************/
2371 : /* BuildIndex() */
2372 : /***********************************************************************/
2373 :
2374 17 : FileGDBIterator *OGROpenFileGDBLayer::BuildIndex(const char *pszFieldName,
2375 : int bAscending, int op,
2376 : swq_expr_node *poValue)
2377 : {
2378 17 : if (!BuildLayerDefinition())
2379 0 : return nullptr;
2380 :
2381 17 : int idx = GetLayerDefn()->GetFieldIndex(pszFieldName);
2382 17 : if (idx < 0)
2383 0 : return nullptr;
2384 17 : OGRFieldDefn *poFieldDefn = GetLayerDefn()->GetFieldDefn(idx);
2385 :
2386 17 : int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2387 17 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2388 : {
2389 17 : if (op < 0)
2390 15 : return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx,
2391 17 : bAscending);
2392 :
2393 : OGRField sValue;
2394 2 : if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
2395 : {
2396 : FileGDBSQLOp eOp;
2397 2 : switch (op)
2398 : {
2399 0 : case SWQ_LE:
2400 0 : eOp = FGSO_LE;
2401 0 : break;
2402 0 : case SWQ_LT:
2403 0 : eOp = FGSO_LT;
2404 0 : break;
2405 2 : case SWQ_EQ:
2406 2 : eOp = FGSO_EQ;
2407 2 : break;
2408 0 : case SWQ_GE:
2409 0 : eOp = FGSO_GE;
2410 0 : break;
2411 0 : case SWQ_GT:
2412 0 : eOp = FGSO_GT;
2413 0 : break;
2414 0 : default:
2415 0 : return nullptr;
2416 : }
2417 :
2418 2 : return FileGDBIterator::Build(m_poLyrTable, nTableColIdx,
2419 : bAscending, eOp,
2420 2 : poFieldDefn->GetType(), &sValue);
2421 : }
2422 : }
2423 0 : return nullptr;
2424 : }
2425 :
2426 : /***********************************************************************/
2427 : /* GetMinMaxValue() */
2428 : /***********************************************************************/
2429 :
2430 55 : const OGRField *OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn *poFieldDefn,
2431 : int bIsMin, int &eOutType)
2432 : {
2433 55 : eOutType = -1;
2434 55 : if (!BuildLayerDefinition())
2435 0 : return nullptr;
2436 55 : if (!m_poLyrTable->CanUseIndices())
2437 0 : return nullptr;
2438 :
2439 : const int nTableColIdx =
2440 55 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2441 55 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2442 : {
2443 50 : delete m_poIterMinMax;
2444 50 : m_poIterMinMax =
2445 50 : FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE);
2446 50 : if (m_poIterMinMax != nullptr)
2447 : {
2448 : const OGRField *poRet = (bIsMin)
2449 50 : ? m_poIterMinMax->GetMinValue(eOutType)
2450 36 : : m_poIterMinMax->GetMaxValue(eOutType);
2451 50 : if (poRet == nullptr)
2452 2 : eOutType = poFieldDefn->GetType();
2453 50 : return poRet;
2454 : }
2455 : }
2456 5 : return nullptr;
2457 : }
2458 :
2459 : /***********************************************************************/
2460 : /* GetMinMaxSumCount() */
2461 : /***********************************************************************/
2462 :
2463 8 : int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn *poFieldDefn,
2464 : double &dfMin, double &dfMax,
2465 : double &dfSum, int &nCount)
2466 : {
2467 8 : dfMin = 0.0;
2468 8 : dfMax = 0.0;
2469 8 : dfSum = 0.0;
2470 8 : nCount = 0;
2471 8 : if (!BuildLayerDefinition())
2472 0 : return false;
2473 8 : if (!m_poLyrTable->CanUseIndices())
2474 0 : return false;
2475 :
2476 8 : int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2477 8 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2478 : {
2479 : auto poIter = std::unique_ptr<FileGDBIterator>(
2480 8 : FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE));
2481 8 : if (poIter)
2482 : {
2483 8 : return poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount);
2484 : }
2485 : }
2486 0 : return false;
2487 : }
2488 :
2489 : /************************************************************************/
2490 : /* GetDataset() */
2491 : /************************************************************************/
2492 :
2493 67 : GDALDataset *OGROpenFileGDBLayer::GetDataset()
2494 : {
2495 67 : return m_poDS;
2496 : }
|