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