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