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 281532 : int OGROpenFileGDBLayer::BuildLayerDefinition()
367 : {
368 281532 : if (m_bValidLayerDefn >= 0)
369 280754 : 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()
863 : {
864 1218 : if (m_eGeomType == wkbUnknown ||
865 607 : m_osDefinition.empty() /* FileGDB v9 case */)
866 : {
867 82 : (void)BuildLayerDefinition();
868 : }
869 :
870 611 : return m_eGeomType;
871 : }
872 :
873 : /***********************************************************************/
874 : /* GetLayerDefn() */
875 : /***********************************************************************/
876 :
877 46783 : OGRFeatureDefn *OGROpenFileGDBLayer::GetLayerDefn()
878 : {
879 46783 : return m_poFeatureDefn;
880 : }
881 :
882 : /***********************************************************************/
883 : /* GetFIDColumn() */
884 : /***********************************************************************/
885 :
886 5202 : const char *OGROpenFileGDBLayer::GetFIDColumn()
887 : {
888 5202 : if (!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 9561 : void OGROpenFileGDBLayer::ResetReading()
901 : {
902 9561 : if (m_iCurFeat != 0)
903 : {
904 2834 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
905 7 : m_eSpatialIndexState = SPI_INVALID;
906 : }
907 9561 : m_bEOF = FALSE;
908 9561 : m_iCurFeat = 0;
909 9561 : if (m_poAttributeIterator)
910 62 : m_poAttributeIterator->Reset();
911 9561 : if (m_poSpatialIndexIterator)
912 2613 : m_poSpatialIndexIterator->Reset();
913 9561 : if (m_poCombinedIterator)
914 7 : m_poCombinedIterator->Reset();
915 9561 : }
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(OGRFieldDefn *poFieldDefn, const swq_expr_node *poValue1,
1008 : const swq_expr_node *poValue2)
1009 : {
1010 18 : int ret = 0;
1011 18 : switch (poFieldDefn->GetType())
1012 : {
1013 13 : case OFTInteger:
1014 : {
1015 : int n1, n2;
1016 13 : if (poValue1->field_type == SWQ_FLOAT)
1017 1 : n1 = static_cast<int>(poValue1->float_value);
1018 : else
1019 12 : n1 = static_cast<int>(poValue1->int_value);
1020 13 : if (poValue2->field_type == SWQ_FLOAT)
1021 0 : n2 = static_cast<int>(poValue2->float_value);
1022 : else
1023 13 : n2 = static_cast<int>(poValue2->int_value);
1024 13 : if (n1 < n2)
1025 5 : ret = -1;
1026 8 : else if (n1 == n2)
1027 5 : ret = 0;
1028 : else
1029 3 : ret = 1;
1030 13 : break;
1031 : }
1032 :
1033 4 : case OFTReal:
1034 4 : if (poValue1->float_value < poValue2->float_value)
1035 2 : ret = -1;
1036 2 : else if (poValue1->float_value == poValue2->float_value)
1037 2 : ret = 0;
1038 : else
1039 0 : ret = 1;
1040 4 : break;
1041 :
1042 0 : case OFTString:
1043 0 : ret = strcmp(poValue1->string_value, poValue2->string_value);
1044 0 : break;
1045 :
1046 1 : case OFTDate:
1047 : case OFTTime:
1048 : case OFTDateTime:
1049 : {
1050 1 : if ((poValue1->field_type == SWQ_TIMESTAMP ||
1051 0 : poValue1->field_type == SWQ_DATE ||
1052 0 : poValue1->field_type == SWQ_TIME) &&
1053 1 : (poValue2->field_type == SWQ_TIMESTAMP ||
1054 0 : poValue2->field_type == SWQ_DATE ||
1055 0 : poValue2->field_type == SWQ_TIME))
1056 : {
1057 1 : ret = strcmp(poValue1->string_value, poValue2->string_value);
1058 : }
1059 1 : break;
1060 : }
1061 :
1062 0 : default:
1063 0 : break;
1064 : }
1065 18 : return ret;
1066 : }
1067 :
1068 : /***********************************************************************/
1069 : /* OGROpenFileGDBIsComparisonOp() */
1070 : /***********************************************************************/
1071 :
1072 1228 : int OGROpenFileGDBIsComparisonOp(int op)
1073 : {
1074 261 : return (op == SWQ_EQ || op == SWQ_NE || op == SWQ_LT || op == SWQ_LE ||
1075 1489 : op == SWQ_GT || op == SWQ_GE);
1076 : }
1077 :
1078 : /***********************************************************************/
1079 : /* AreExprExclusive() */
1080 : /***********************************************************************/
1081 :
1082 : static const struct
1083 : {
1084 : swq_op op1;
1085 : swq_op op2;
1086 : int expected_comp_1;
1087 : int expected_comp_2;
1088 : } asPairsOfComparisons[] = {
1089 : {SWQ_EQ, SWQ_EQ, -1, 1}, {SWQ_LT, SWQ_GT, -1, 0},
1090 : {SWQ_GT, SWQ_LT, 0, 1}, {SWQ_LT, SWQ_GE, -1, 999},
1091 : {SWQ_LE, SWQ_GE, -1, 999}, {SWQ_LE, SWQ_GT, -1, 999},
1092 : {SWQ_GE, SWQ_LE, 1, 999}, {SWQ_GE, SWQ_LT, 1, 999},
1093 : {SWQ_GT, SWQ_LE, 1, 999}};
1094 :
1095 22 : static int AreExprExclusive(OGRFeatureDefn *poFeatureDefn,
1096 : const swq_expr_node *poNode1,
1097 : const swq_expr_node *poNode2)
1098 : {
1099 22 : if (poNode1->eNodeType != SNT_OPERATION)
1100 0 : return FALSE;
1101 22 : if (poNode2->eNodeType != SNT_OPERATION)
1102 0 : return FALSE;
1103 :
1104 22 : const size_t nPairs =
1105 : sizeof(asPairsOfComparisons) / sizeof(asPairsOfComparisons[0]);
1106 94 : for (size_t i = 0; i < nPairs; i++)
1107 : {
1108 90 : if (poNode1->nOperation == asPairsOfComparisons[i].op1 &&
1109 20 : poNode2->nOperation == asPairsOfComparisons[i].op2 &&
1110 18 : poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 2)
1111 : {
1112 18 : swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1113 18 : swq_expr_node *poValue1 = poNode1->papoSubExpr[1];
1114 18 : swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1115 18 : swq_expr_node *poValue2 = poNode2->papoSubExpr[1];
1116 54 : if (poColumn1->eNodeType == SNT_COLUMN &&
1117 18 : poValue1->eNodeType == SNT_CONSTANT &&
1118 18 : poColumn2->eNodeType == SNT_COLUMN &&
1119 18 : poValue2->eNodeType == SNT_CONSTANT &&
1120 54 : poColumn1->field_index == poColumn2->field_index &&
1121 18 : poColumn1->field_index < poFeatureDefn->GetFieldCount())
1122 : {
1123 : OGRFieldDefn *poFieldDefn =
1124 18 : poFeatureDefn->GetFieldDefn(poColumn1->field_index);
1125 :
1126 18 : const int nComp = CompValues(poFieldDefn, poValue1, poValue2);
1127 26 : return nComp == asPairsOfComparisons[i].expected_comp_1 ||
1128 26 : nComp == asPairsOfComparisons[i].expected_comp_2;
1129 : }
1130 0 : return FALSE;
1131 : }
1132 : }
1133 :
1134 1 : if ((poNode2->nOperation == SWQ_ISNULL &&
1135 1 : OGROpenFileGDBIsComparisonOp(poNode1->nOperation) &&
1136 8 : poNode1->nSubExprCount == 2 && poNode2->nSubExprCount == 1) ||
1137 6 : (poNode1->nOperation == SWQ_ISNULL &&
1138 3 : OGROpenFileGDBIsComparisonOp(poNode2->nOperation) &&
1139 1 : poNode2->nSubExprCount == 2 && poNode1->nSubExprCount == 1))
1140 : {
1141 2 : swq_expr_node *poColumn1 = poNode1->papoSubExpr[0];
1142 2 : swq_expr_node *poColumn2 = poNode2->papoSubExpr[0];
1143 6 : if (poColumn1->eNodeType == SNT_COLUMN &&
1144 2 : poColumn2->eNodeType == SNT_COLUMN &&
1145 6 : poColumn1->field_index == poColumn2->field_index &&
1146 2 : poColumn1->field_index < poFeatureDefn->GetFieldCount())
1147 : {
1148 2 : return TRUE;
1149 : }
1150 : }
1151 :
1152 : /* In doubt: return FALSE */
1153 2 : return FALSE;
1154 : }
1155 :
1156 : /***********************************************************************/
1157 : /* FillTargetValueFromSrcExpr() */
1158 : /***********************************************************************/
1159 :
1160 336 : static int FillTargetValueFromSrcExpr(OGRFieldDefn *poFieldDefn,
1161 : OGRField *poTargetValue,
1162 : const swq_expr_node *poSrcValue)
1163 : {
1164 336 : switch (poFieldDefn->GetType())
1165 : {
1166 102 : case OFTInteger:
1167 102 : if (poSrcValue->field_type == SWQ_FLOAT)
1168 1 : poTargetValue->Integer =
1169 1 : static_cast<int>(poSrcValue->float_value);
1170 : else
1171 101 : poTargetValue->Integer =
1172 101 : static_cast<int>(poSrcValue->int_value);
1173 102 : break;
1174 :
1175 4 : case OFTInteger64:
1176 4 : if (poSrcValue->field_type == SWQ_FLOAT)
1177 0 : poTargetValue->Integer64 =
1178 0 : static_cast<GIntBig>(poSrcValue->float_value);
1179 : else
1180 4 : poTargetValue->Integer64 = poSrcValue->int_value;
1181 4 : break;
1182 :
1183 38 : case OFTReal:
1184 38 : poTargetValue->Real = poSrcValue->float_value;
1185 38 : break;
1186 :
1187 184 : case OFTString:
1188 184 : poTargetValue->String = poSrcValue->string_value;
1189 184 : break;
1190 :
1191 8 : case OFTDate:
1192 : case OFTTime:
1193 : case OFTDateTime:
1194 8 : if (poSrcValue->field_type == SWQ_TIMESTAMP ||
1195 0 : poSrcValue->field_type == SWQ_DATE ||
1196 0 : poSrcValue->field_type == SWQ_TIME)
1197 : {
1198 8 : int nYear = 0, nMonth = 0, nDay = 0, nHour = 0, nMin = 0,
1199 8 : nSec = 0;
1200 16 : if (sscanf(poSrcValue->string_value,
1201 : "%04d/%02d/%02d %02d:%02d:%02d", &nYear, &nMonth,
1202 1 : &nDay, &nHour, &nMin, &nSec) == 6 ||
1203 1 : sscanf(poSrcValue->string_value, "%04d/%02d/%02d", &nYear,
1204 9 : &nMonth, &nDay) == 3 ||
1205 0 : sscanf(poSrcValue->string_value, "%02d:%02d:%02d", &nHour,
1206 : &nMin, &nSec) == 3)
1207 : {
1208 8 : poTargetValue->Date.Year = static_cast<GInt16>(nYear);
1209 8 : poTargetValue->Date.Month = static_cast<GByte>(nMonth);
1210 8 : poTargetValue->Date.Day = static_cast<GByte>(nDay);
1211 8 : poTargetValue->Date.Hour = static_cast<GByte>(nHour);
1212 8 : poTargetValue->Date.Minute = static_cast<GByte>(nMin);
1213 8 : poTargetValue->Date.Second = static_cast<GByte>(nSec);
1214 8 : poTargetValue->Date.TZFlag = 0;
1215 8 : poTargetValue->Date.Reserved = 0;
1216 : }
1217 : else
1218 8 : return FALSE;
1219 : }
1220 : else
1221 0 : return FALSE;
1222 8 : break;
1223 :
1224 0 : default:
1225 0 : return FALSE;
1226 : }
1227 336 : return TRUE;
1228 : }
1229 :
1230 : /***********************************************************************/
1231 : /* GetColumnSubNode() */
1232 : /***********************************************************************/
1233 :
1234 1195 : static swq_expr_node *GetColumnSubNode(swq_expr_node *poNode)
1235 : {
1236 1195 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1237 : {
1238 1195 : if (poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN)
1239 618 : return poNode->papoSubExpr[0];
1240 577 : if (poNode->papoSubExpr[1]->eNodeType == SNT_COLUMN)
1241 5 : return poNode->papoSubExpr[1];
1242 : }
1243 572 : return nullptr;
1244 : }
1245 :
1246 : /***********************************************************************/
1247 : /* GetConstantSubNode() */
1248 : /***********************************************************************/
1249 :
1250 1195 : static swq_expr_node *GetConstantSubNode(swq_expr_node *poNode)
1251 : {
1252 1195 : if (poNode->eNodeType == SNT_OPERATION && poNode->nSubExprCount == 2)
1253 : {
1254 1195 : if (poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
1255 1188 : return poNode->papoSubExpr[1];
1256 7 : if (poNode->papoSubExpr[0]->eNodeType == SNT_CONSTANT)
1257 5 : return poNode->papoSubExpr[0];
1258 : }
1259 2 : return nullptr;
1260 : }
1261 :
1262 : /***********************************************************************/
1263 : /* BuildIteratorFromExprNode() */
1264 : /***********************************************************************/
1265 :
1266 : FileGDBIterator *
1267 1257 : OGROpenFileGDBLayer::BuildIteratorFromExprNode(swq_expr_node *poNode)
1268 : {
1269 1257 : if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1270 0 : return nullptr;
1271 :
1272 1257 : if (poNode->eNodeType == SNT_OPERATION && poNode->nOperation == SWQ_AND &&
1273 13 : poNode->nSubExprCount == 2)
1274 : {
1275 : // Even if there is only one branch of the 2 that results to an
1276 : // iterator, it is useful. Of course, the iterator will not be
1277 : // sufficient to evaluatethe filter, but it will be a super-set of the
1278 : // features
1279 : FileGDBIterator *poIter1 =
1280 13 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1281 :
1282 : /* In case the first branch didn't result to an iterator, temporarily */
1283 : /* restore the flag */
1284 : const bool bSaveIteratorSufficientToEvaluateFilter =
1285 13 : CPL_TO_BOOL(m_bIteratorSufficientToEvaluateFilter);
1286 13 : m_bIteratorSufficientToEvaluateFilter = -1;
1287 : FileGDBIterator *poIter2 =
1288 13 : BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1289 13 : m_bIteratorSufficientToEvaluateFilter =
1290 13 : bSaveIteratorSufficientToEvaluateFilter;
1291 :
1292 13 : if (poIter1 != nullptr && poIter2 != nullptr)
1293 9 : return FileGDBIterator::BuildAnd(poIter1, poIter2, true);
1294 4 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1295 4 : if (poIter1 != nullptr)
1296 2 : return poIter1;
1297 2 : if (poIter2 != nullptr)
1298 2 : return poIter2;
1299 : }
1300 :
1301 1244 : else if (poNode->eNodeType == SNT_OPERATION &&
1302 1244 : poNode->nOperation == SWQ_OR && poNode->nSubExprCount == 2)
1303 : {
1304 : /* For a OR, we need an iterator for the 2 branches */
1305 : FileGDBIterator *poIter1 =
1306 24 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1307 24 : if (poIter1 != nullptr)
1308 : {
1309 : FileGDBIterator *poIter2 =
1310 23 : BuildIteratorFromExprNode(poNode->papoSubExpr[1]);
1311 23 : if (poIter2 == nullptr)
1312 : {
1313 1 : delete poIter1;
1314 : }
1315 : else
1316 : {
1317 22 : return FileGDBIterator::BuildOr(
1318 : poIter1, poIter2,
1319 22 : AreExprExclusive(GetLayerDefn(), poNode->papoSubExpr[0],
1320 44 : poNode->papoSubExpr[1]));
1321 : }
1322 2 : }
1323 : }
1324 :
1325 1220 : else if (poNode->eNodeType == SNT_OPERATION &&
1326 1220 : (OGROpenFileGDBIsComparisonOp(poNode->nOperation) ||
1327 2445 : poNode->nOperation == SWQ_ILIKE) &&
1328 1195 : poNode->nSubExprCount == 2)
1329 : {
1330 1195 : swq_expr_node *poColumn = GetColumnSubNode(poNode);
1331 1195 : swq_expr_node *poValue = GetConstantSubNode(poNode);
1332 1816 : if (poColumn != nullptr && poValue != nullptr &&
1333 621 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1334 : {
1335 : OGRFieldDefn *poFieldDefn =
1336 616 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1337 :
1338 : int nTableColIdx =
1339 616 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1340 1232 : if (nTableColIdx >= 0 &&
1341 616 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1342 : {
1343 : OGRField sValue;
1344 :
1345 320 : if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
1346 : {
1347 320 : FileGDBSQLOp eOp = FGSO_EQ;
1348 320 : CPL_IGNORE_RET_VAL(eOp);
1349 320 : if (poColumn == poNode->papoSubExpr[0])
1350 : {
1351 315 : switch (poNode->nOperation)
1352 : {
1353 21 : case SWQ_LE:
1354 21 : eOp = FGSO_LE;
1355 21 : break;
1356 22 : case SWQ_LT:
1357 22 : eOp = FGSO_LT;
1358 22 : break;
1359 10 : case SWQ_NE:
1360 10 : eOp = FGSO_EQ; /* yes : EQ */
1361 10 : break;
1362 214 : case SWQ_EQ:
1363 214 : eOp = FGSO_EQ;
1364 214 : break;
1365 22 : case SWQ_GE:
1366 22 : eOp = FGSO_GE;
1367 22 : break;
1368 21 : case SWQ_GT:
1369 21 : eOp = FGSO_GT;
1370 21 : break;
1371 5 : case SWQ_ILIKE:
1372 5 : eOp = FGSO_ILIKE;
1373 5 : break;
1374 0 : default:
1375 0 : CPLAssert(false);
1376 : break;
1377 : }
1378 : }
1379 : else
1380 : {
1381 : /* If "constant op column", then we must reverse */
1382 : /* the operator */
1383 5 : switch (poNode->nOperation)
1384 : {
1385 1 : case SWQ_LE:
1386 1 : eOp = FGSO_GE;
1387 1 : break;
1388 1 : case SWQ_LT:
1389 1 : eOp = FGSO_GT;
1390 1 : break;
1391 0 : case SWQ_NE:
1392 0 : eOp = FGSO_EQ; /* yes : EQ */
1393 0 : break;
1394 1 : case SWQ_EQ:
1395 1 : eOp = FGSO_EQ;
1396 1 : break;
1397 1 : case SWQ_GE:
1398 1 : eOp = FGSO_LE;
1399 1 : break;
1400 1 : case SWQ_GT:
1401 1 : eOp = FGSO_LT;
1402 1 : break;
1403 0 : case SWQ_ILIKE:
1404 0 : eOp = FGSO_ILIKE;
1405 0 : break;
1406 0 : default:
1407 0 : CPLAssert(false);
1408 : break;
1409 : }
1410 : }
1411 :
1412 320 : bool bIteratorSufficient = true;
1413 320 : auto poField = m_poLyrTable->GetField(nTableColIdx);
1414 640 : std::string osTruncatedStr; // keep it in this scope !
1415 489 : if (poField->GetType() == FGFT_STRING &&
1416 169 : poFieldDefn->GetType() == OFTString)
1417 : {
1418 : // If we have an equality comparison, but the index
1419 : // uses LOWER(), transform it to a ILIKE comparison
1420 321 : if (eOp == FGSO_EQ && poField->HasIndex() &&
1421 152 : STARTS_WITH_CI(
1422 : poField->GetIndex()->GetExpression().c_str(),
1423 : "LOWER("))
1424 : {
1425 : // Note: FileGDBIndexIterator::SetConstraint()
1426 : // checks that the string to compare with has no
1427 : // wildcard
1428 3 : eOp = FGSO_ILIKE;
1429 :
1430 : // In theory, a ILIKE is not sufficient as it is
1431 : // case insensitive, whereas one could expect
1432 : // equality testing to be case sensitive... but
1433 : // it is not in OGR SQL...
1434 : // So we can comment the below line
1435 : // bIteratorSufficient = false;
1436 : }
1437 :
1438 : // As the index use ' ' as padding value, we cannot
1439 : // fully trust the index.
1440 166 : else if ((eOp == FGSO_EQ &&
1441 149 : poNode->nOperation != SWQ_NE) ||
1442 20 : eOp == FGSO_GE)
1443 149 : bIteratorSufficient = false;
1444 : else
1445 17 : return nullptr;
1446 :
1447 : const int nMaxWidthIndexedStr =
1448 152 : poField->GetIndex()->GetMaxWidthInBytes(
1449 152 : m_poLyrTable);
1450 152 : if (nMaxWidthIndexedStr > 0)
1451 : {
1452 302 : wchar_t *pWide = CPLRecodeToWChar(
1453 151 : sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1454 151 : if (pWide)
1455 : {
1456 151 : const size_t nUCS2Len = wcslen(pWide);
1457 151 : if (nUCS2Len * sizeof(uint16_t) >
1458 151 : static_cast<size_t>(nMaxWidthIndexedStr))
1459 : {
1460 2 : pWide[nMaxWidthIndexedStr /
1461 2 : sizeof(uint16_t)] = 0;
1462 2 : char *pszTruncated = CPLRecodeFromWChar(
1463 : pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1464 2 : if (pszTruncated)
1465 : {
1466 2 : osTruncatedStr = pszTruncated;
1467 2 : sValue.String = &osTruncatedStr[0];
1468 2 : CPLFree(pszTruncated);
1469 : }
1470 : }
1471 151 : CPLFree(pWide);
1472 : }
1473 : }
1474 : }
1475 151 : else if (eOp == FGSO_ILIKE)
1476 0 : return nullptr;
1477 :
1478 303 : FileGDBIterator *poIter = FileGDBIterator::Build(
1479 : m_poLyrTable, nTableColIdx, TRUE, eOp,
1480 : poFieldDefn->GetType(), &sValue);
1481 303 : if (poIter != nullptr)
1482 298 : m_bIteratorSufficientToEvaluateFilter =
1483 : bIteratorSufficient;
1484 303 : if (poIter && poNode->nOperation == SWQ_NE)
1485 7 : return FileGDBIterator::BuildNot(poIter);
1486 : else
1487 296 : return poIter;
1488 : }
1489 : }
1490 : }
1491 : }
1492 25 : else if (poNode->eNodeType == SNT_OPERATION &&
1493 25 : poNode->nOperation == SWQ_ISNULL && poNode->nSubExprCount == 1)
1494 : {
1495 8 : swq_expr_node *poColumn = poNode->papoSubExpr[0];
1496 16 : if (poColumn->eNodeType == SNT_COLUMN &&
1497 8 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1498 : {
1499 : OGRFieldDefn *poFieldDefn =
1500 7 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1501 :
1502 : int nTableColIdx =
1503 7 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1504 14 : if (nTableColIdx >= 0 &&
1505 7 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1506 : {
1507 7 : FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1508 : m_poLyrTable, nTableColIdx, TRUE);
1509 7 : if (poIter)
1510 : {
1511 7 : m_bIteratorSufficientToEvaluateFilter = TRUE;
1512 7 : poIter = FileGDBIterator::BuildNot(poIter);
1513 : }
1514 7 : return poIter;
1515 : }
1516 1 : }
1517 : }
1518 17 : else if (poNode->eNodeType == SNT_OPERATION &&
1519 17 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1 &&
1520 8 : poNode->papoSubExpr[0]->eNodeType == SNT_OPERATION &&
1521 8 : poNode->papoSubExpr[0]->nOperation == SWQ_ISNULL &&
1522 6 : poNode->papoSubExpr[0]->nSubExprCount == 1)
1523 : {
1524 6 : swq_expr_node *poColumn = poNode->papoSubExpr[0]->papoSubExpr[0];
1525 12 : if (poColumn->eNodeType == SNT_COLUMN &&
1526 6 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1527 : {
1528 : OGRFieldDefn *poFieldDefn =
1529 5 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1530 :
1531 : int nTableColIdx =
1532 5 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1533 10 : if (nTableColIdx >= 0 &&
1534 5 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1535 : {
1536 5 : FileGDBIterator *poIter = FileGDBIterator::BuildIsNotNull(
1537 : m_poLyrTable, nTableColIdx, TRUE);
1538 5 : if (poIter)
1539 5 : m_bIteratorSufficientToEvaluateFilter = TRUE;
1540 5 : return poIter;
1541 : }
1542 1 : }
1543 : }
1544 11 : else if (poNode->eNodeType == SNT_OPERATION &&
1545 11 : poNode->nOperation == SWQ_IN && poNode->nSubExprCount >= 2)
1546 : {
1547 9 : swq_expr_node *poColumn = poNode->papoSubExpr[0];
1548 18 : if (poColumn->eNodeType == SNT_COLUMN &&
1549 9 : poColumn->field_index < GetLayerDefn()->GetFieldCount())
1550 : {
1551 8 : bool bAllConstants = true;
1552 22 : for (int i = 1; i < poNode->nSubExprCount; i++)
1553 : {
1554 14 : if (poNode->papoSubExpr[i]->eNodeType != SNT_CONSTANT)
1555 0 : bAllConstants = false;
1556 : }
1557 : OGRFieldDefn *poFieldDefn =
1558 8 : GetLayerDefn()->GetFieldDefn(poColumn->field_index);
1559 :
1560 : int nTableColIdx =
1561 8 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
1562 16 : if (bAllConstants && nTableColIdx >= 0 &&
1563 8 : m_poLyrTable->GetField(nTableColIdx)->HasIndex())
1564 : {
1565 8 : FileGDBIterator *poRet = nullptr;
1566 :
1567 8 : bool bIteratorSufficient = true;
1568 8 : auto poField = m_poLyrTable->GetField(nTableColIdx);
1569 :
1570 22 : for (int i = 1; i < poNode->nSubExprCount; i++)
1571 : {
1572 : OGRField sValue;
1573 14 : if (!FillTargetValueFromSrcExpr(poFieldDefn, &sValue,
1574 14 : poNode->papoSubExpr[i]))
1575 : {
1576 0 : delete poRet;
1577 0 : poRet = nullptr;
1578 0 : break;
1579 : }
1580 :
1581 14 : std::string osTruncatedStr; // keep it in this scope !
1582 22 : if (poField->GetType() == FGFT_STRING &&
1583 8 : poFieldDefn->GetType() == OFTString)
1584 : {
1585 : const int nMaxWidthIndexedStr =
1586 8 : poField->GetIndex()->GetMaxWidthInBytes(
1587 8 : m_poLyrTable);
1588 8 : if (nMaxWidthIndexedStr > 0)
1589 : {
1590 16 : wchar_t *pWide = CPLRecodeToWChar(
1591 8 : sValue.String, CPL_ENC_UTF8, CPL_ENC_UCS2);
1592 8 : if (pWide)
1593 : {
1594 8 : const size_t nUCS2Len = wcslen(pWide);
1595 8 : if (nUCS2Len * sizeof(uint16_t) >
1596 8 : static_cast<size_t>(nMaxWidthIndexedStr))
1597 : {
1598 1 : pWide[nMaxWidthIndexedStr /
1599 1 : sizeof(uint16_t)] = 0;
1600 1 : char *pszTruncated = CPLRecodeFromWChar(
1601 : pWide, CPL_ENC_UCS2, CPL_ENC_UTF8);
1602 1 : if (pszTruncated)
1603 : {
1604 1 : osTruncatedStr = pszTruncated;
1605 1 : sValue.String = &osTruncatedStr[0];
1606 1 : CPLFree(pszTruncated);
1607 : }
1608 : }
1609 8 : CPLFree(pWide);
1610 : }
1611 : }
1612 :
1613 : // As the index use ' ' as padding value, we cannot
1614 : // fully trust the index.
1615 8 : bIteratorSufficient = false;
1616 : }
1617 :
1618 14 : FileGDBIterator *poIter = FileGDBIterator::Build(
1619 : m_poLyrTable, nTableColIdx, TRUE, FGSO_EQ,
1620 : poFieldDefn->GetType(), &sValue);
1621 14 : if (poIter == nullptr)
1622 : {
1623 0 : delete poRet;
1624 0 : poRet = nullptr;
1625 0 : break;
1626 : }
1627 14 : if (poRet == nullptr)
1628 8 : poRet = poIter;
1629 : else
1630 6 : poRet = FileGDBIterator::BuildOr(poRet, poIter);
1631 : }
1632 8 : if (poRet != nullptr)
1633 : {
1634 8 : m_bIteratorSufficientToEvaluateFilter = bIteratorSufficient;
1635 8 : return poRet;
1636 : }
1637 : }
1638 1 : }
1639 : }
1640 2 : else if (poNode->eNodeType == SNT_OPERATION &&
1641 2 : poNode->nOperation == SWQ_NOT && poNode->nSubExprCount == 1)
1642 : {
1643 : FileGDBIterator *poIter =
1644 2 : BuildIteratorFromExprNode(poNode->papoSubExpr[0]);
1645 : /* If we have an iterator that is only partial w.r.t the full clause */
1646 : /* then we cannot do anything with it unfortunately */
1647 2 : if (m_bIteratorSufficientToEvaluateFilter == FALSE)
1648 : {
1649 1 : if (poIter != nullptr)
1650 1 : CPLDebug("OpenFileGDB", "Disabling use of indexes");
1651 1 : delete poIter;
1652 : }
1653 1 : else if (poIter != nullptr)
1654 : {
1655 1 : return FileGDBIterator::BuildNot(poIter);
1656 : }
1657 : }
1658 :
1659 882 : if (m_bIteratorSufficientToEvaluateFilter == TRUE)
1660 1 : CPLDebug("OpenFileGDB", "Disabling use of indexes");
1661 882 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1662 882 : return nullptr;
1663 : }
1664 :
1665 : /***********************************************************************/
1666 : /* SetAttributeFilter() */
1667 : /***********************************************************************/
1668 :
1669 2874 : OGRErr OGROpenFileGDBLayer::SetAttributeFilter(const char *pszFilter)
1670 : {
1671 2874 : if (!BuildLayerDefinition())
1672 0 : return OGRERR_FAILURE;
1673 :
1674 2874 : delete m_poAttributeIterator;
1675 2874 : m_poAttributeIterator = nullptr;
1676 2874 : delete m_poCombinedIterator;
1677 2874 : m_poCombinedIterator = nullptr;
1678 2874 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1679 :
1680 2874 : OGRErr eErr = OGRLayer::SetAttributeFilter(pszFilter);
1681 5748 : if (eErr != OGRERR_NONE ||
1682 2874 : !CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
1683 0 : return eErr;
1684 :
1685 2874 : if (m_poAttrQuery != nullptr && m_nFilteredFeatureCount < 0)
1686 : {
1687 : swq_expr_node *poNode =
1688 1182 : static_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
1689 1182 : poNode->ReplaceBetweenByGEAndLERecurse();
1690 1182 : m_bIteratorSufficientToEvaluateFilter = -1;
1691 1182 : m_poAttributeIterator = BuildIteratorFromExprNode(poNode);
1692 1182 : if (m_poAttributeIterator != nullptr &&
1693 285 : m_eSpatialIndexState == SPI_IN_BUILDING)
1694 47 : m_eSpatialIndexState = SPI_INVALID;
1695 1182 : if (m_bIteratorSufficientToEvaluateFilter < 0)
1696 21 : m_bIteratorSufficientToEvaluateFilter = FALSE;
1697 : }
1698 :
1699 2874 : BuildCombinedIterator();
1700 :
1701 2874 : return eErr;
1702 : }
1703 :
1704 : /***********************************************************************/
1705 : /* BuildCombinedIterator() */
1706 : /***********************************************************************/
1707 :
1708 6080 : void OGROpenFileGDBLayer::BuildCombinedIterator()
1709 : {
1710 6080 : delete m_poCombinedIterator;
1711 6080 : if (m_poAttributeIterator && m_poSpatialIndexIterator)
1712 : {
1713 2 : m_poCombinedIterator = FileGDBIterator::BuildAnd(
1714 2 : m_poAttributeIterator, m_poSpatialIndexIterator, false);
1715 : }
1716 : else
1717 : {
1718 6078 : m_poCombinedIterator = nullptr;
1719 : }
1720 6080 : }
1721 :
1722 : /***********************************************************************/
1723 : /* GetCurrentFeature() */
1724 : /***********************************************************************/
1725 :
1726 38559 : OGRFeature *OGROpenFileGDBLayer::GetCurrentFeature()
1727 : {
1728 38559 : OGRFeature *poFeature = nullptr;
1729 38559 : int iOGRIdx = 0;
1730 38559 : int64_t iRow = m_poLyrTable->GetCurRow();
1731 213800 : for (int iGDBIdx = 0; iGDBIdx < m_poLyrTable->GetFieldCount(); iGDBIdx++)
1732 : {
1733 186256 : if (iOGRIdx == m_iFIDAsRegularColumnIndex)
1734 6 : iOGRIdx++;
1735 :
1736 186256 : if (iGDBIdx == m_iGeomFieldIdx)
1737 : {
1738 22922 : if (m_poFeatureDefn->GetGeomFieldDefn(0)->IsIgnored())
1739 : {
1740 285 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
1741 0 : m_eSpatialIndexState = SPI_INVALID;
1742 285 : continue;
1743 : }
1744 :
1745 22637 : const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1746 22637 : if (psField != nullptr)
1747 : {
1748 19967 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
1749 : {
1750 67 : OGREnvelope sFeatureEnvelope;
1751 67 : if (m_poLyrTable->GetFeatureExtent(psField,
1752 67 : &sFeatureEnvelope))
1753 : {
1754 : #if SIZEOF_VOIDP < 8
1755 : if (iRow > INT32_MAX)
1756 : {
1757 : // m_pQuadTree stores iRow values as void*
1758 : // This would overflow here.
1759 : m_eSpatialIndexState = SPI_INVALID;
1760 : }
1761 : else
1762 : #endif
1763 : {
1764 : CPLRectObj sBounds;
1765 67 : sBounds.minx = sFeatureEnvelope.MinX;
1766 67 : sBounds.miny = sFeatureEnvelope.MinY;
1767 67 : sBounds.maxx = sFeatureEnvelope.MaxX;
1768 67 : sBounds.maxy = sFeatureEnvelope.MaxY;
1769 67 : CPLQuadTreeInsertWithBounds(
1770 : m_pQuadTree,
1771 : reinterpret_cast<void *>(
1772 : static_cast<uintptr_t>(iRow)),
1773 : &sBounds);
1774 : }
1775 : }
1776 : }
1777 :
1778 52345 : if (m_poFilterGeom != nullptr &&
1779 32367 : m_eSpatialIndexState != SPI_COMPLETED &&
1780 12400 : !m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
1781 : psField))
1782 : {
1783 11015 : delete poFeature;
1784 11015 : return nullptr;
1785 : }
1786 :
1787 8952 : OGRGeometry *poGeom = m_poGeomConverter->GetAsGeometry(psField);
1788 8952 : if (poGeom != nullptr)
1789 : {
1790 : OGRwkbGeometryType eFlattenType =
1791 8952 : wkbFlatten(poGeom->getGeometryType());
1792 8952 : if (eFlattenType == wkbPolygon)
1793 : poGeom =
1794 2169 : OGRGeometryFactory::forceToMultiPolygon(poGeom);
1795 6783 : else if (eFlattenType == wkbCurvePolygon)
1796 : {
1797 29 : OGRMultiSurface *poMS = new OGRMultiSurface();
1798 29 : poMS->addGeometryDirectly(poGeom);
1799 29 : poGeom = poMS;
1800 : }
1801 6754 : else if (eFlattenType == wkbLineString)
1802 : poGeom =
1803 1681 : OGRGeometryFactory::forceToMultiLineString(poGeom);
1804 5073 : else if (eFlattenType == wkbCompoundCurve)
1805 : {
1806 17 : OGRMultiCurve *poMC = new OGRMultiCurve();
1807 17 : poMC->addGeometryDirectly(poGeom);
1808 17 : poGeom = poMC;
1809 : }
1810 :
1811 8952 : poGeom->assignSpatialReference(
1812 8952 : m_poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef());
1813 :
1814 8952 : if (poFeature == nullptr)
1815 8948 : poFeature = new OGRFeature(m_poFeatureDefn);
1816 8952 : poFeature->SetGeometryDirectly(poGeom);
1817 : }
1818 : }
1819 : }
1820 163334 : else if (iGDBIdx != m_poLyrTable->GetObjectIdFieldIdx())
1821 : {
1822 : const OGRFieldDefn *poFieldDefn =
1823 135786 : m_poFeatureDefn->GetFieldDefn(iOGRIdx);
1824 135786 : if (!poFieldDefn->IsIgnored())
1825 : {
1826 134327 : const OGRField *psField = m_poLyrTable->GetFieldValue(iGDBIdx);
1827 134327 : if (poFeature == nullptr)
1828 17336 : poFeature = new OGRFeature(m_poFeatureDefn);
1829 134327 : if (psField == nullptr)
1830 : {
1831 19396 : poFeature->SetFieldNull(iOGRIdx);
1832 : }
1833 : else
1834 : {
1835 :
1836 114931 : if (iGDBIdx == m_iFieldToReadAsBinary)
1837 0 : poFeature->SetField(iOGRIdx,
1838 : reinterpret_cast<const char *>(
1839 0 : psField->Binary.paData));
1840 114931 : else if (poFieldDefn->GetType() == OFTDateTime)
1841 : {
1842 7623 : OGRField sField = *psField;
1843 7623 : if (m_poLyrTable->GetField(iGDBIdx)->GetType() ==
1844 : FGFT_DATETIME)
1845 : {
1846 7599 : sField.Date.TZFlag = m_bTimeInUTC ? 100 : 0;
1847 : }
1848 7623 : poFeature->SetField(iOGRIdx, &sField);
1849 : }
1850 : else
1851 107308 : poFeature->SetField(iOGRIdx, psField);
1852 : }
1853 : }
1854 135786 : iOGRIdx++;
1855 : }
1856 : }
1857 :
1858 27544 : if (poFeature == nullptr)
1859 1260 : poFeature = new OGRFeature(m_poFeatureDefn);
1860 :
1861 27544 : if (m_poLyrTable->HasDeletedFeaturesListed())
1862 : {
1863 0 : poFeature->SetField(poFeature->GetFieldCount() - 1,
1864 0 : m_poLyrTable->IsCurRowDeleted());
1865 : }
1866 :
1867 27544 : poFeature->SetFID(iRow + 1);
1868 :
1869 27544 : if (m_iFIDAsRegularColumnIndex >= 0)
1870 6 : poFeature->SetField(m_iFIDAsRegularColumnIndex, poFeature->GetFID());
1871 :
1872 27544 : return poFeature;
1873 : }
1874 :
1875 : /***********************************************************************/
1876 : /* GetNextFeature() */
1877 : /***********************************************************************/
1878 :
1879 22111 : OGRFeature *OGROpenFileGDBLayer::GetNextFeature()
1880 : {
1881 22111 : if (!BuildLayerDefinition() || m_bEOF)
1882 4 : return nullptr;
1883 :
1884 44208 : FileGDBIterator *poIterator = m_poCombinedIterator ? m_poCombinedIterator
1885 22101 : : m_poSpatialIndexIterator
1886 22101 : ? m_poSpatialIndexIterator
1887 : : m_poAttributeIterator;
1888 :
1889 : while (true)
1890 : {
1891 28628 : OGRFeature *poFeature = nullptr;
1892 :
1893 28628 : if (m_nFilteredFeatureCount >= 0)
1894 : {
1895 : while (true)
1896 : {
1897 3 : if (m_iCurFeat >= m_nFilteredFeatureCount)
1898 : {
1899 1 : return nullptr;
1900 : }
1901 : const auto iRow =
1902 : static_cast<int64_t>(reinterpret_cast<GUIntptr_t>(
1903 2 : m_pahFilteredFeatures[m_iCurFeat++]));
1904 2 : if (m_poLyrTable->SelectRow(iRow))
1905 : {
1906 2 : poFeature = GetCurrentFeature();
1907 2 : if (poFeature)
1908 2 : break;
1909 : }
1910 0 : else if (m_poLyrTable->HasGotError())
1911 : {
1912 0 : m_bEOF = TRUE;
1913 0 : return nullptr;
1914 : }
1915 0 : }
1916 : }
1917 28625 : else if (poIterator != nullptr)
1918 : {
1919 : while (true)
1920 : {
1921 12666 : const auto iRow = poIterator->GetNextRowSortedByFID();
1922 12666 : if (iRow < 0)
1923 315 : return nullptr;
1924 12351 : if (m_poLyrTable->SelectRow(iRow))
1925 : {
1926 12351 : poFeature = GetCurrentFeature();
1927 12351 : if (poFeature)
1928 3455 : break;
1929 : }
1930 0 : else if (m_poLyrTable->HasGotError())
1931 : {
1932 0 : m_bEOF = TRUE;
1933 0 : return nullptr;
1934 : }
1935 8896 : }
1936 : }
1937 : else
1938 : {
1939 : while (true)
1940 : {
1941 26974 : if (m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1942 : {
1943 1752 : return nullptr;
1944 : }
1945 25222 : m_iCurFeat =
1946 25222 : m_poLyrTable->GetAndSelectNextNonEmptyRow(m_iCurFeat);
1947 25222 : if (m_iCurFeat < 0)
1948 : {
1949 75 : m_bEOF = TRUE;
1950 75 : return nullptr;
1951 : }
1952 : else
1953 : {
1954 25147 : m_iCurFeat++;
1955 25147 : poFeature = GetCurrentFeature();
1956 26701 : if (m_eSpatialIndexState == SPI_IN_BUILDING &&
1957 1554 : m_iCurFeat == m_poLyrTable->GetTotalRecordCount())
1958 : {
1959 151 : CPLDebug("OpenFileGDB", "SPI_COMPLETED");
1960 151 : m_eSpatialIndexState = SPI_COMPLETED;
1961 : }
1962 25147 : if (poFeature)
1963 23028 : break;
1964 : }
1965 : }
1966 : }
1967 :
1968 54448 : if ((m_poFilterGeom == nullptr ||
1969 52870 : FilterGeometry(poFeature->GetGeometryRef())) &&
1970 26385 : (m_poAttrQuery == nullptr ||
1971 14085 : (m_poAttributeIterator != nullptr &&
1972 2302 : m_bIteratorSufficientToEvaluateFilter) ||
1973 11938 : m_poAttrQuery->Evaluate(poFeature)))
1974 : {
1975 19964 : return poFeature;
1976 : }
1977 :
1978 6521 : delete poFeature;
1979 6521 : }
1980 : }
1981 :
1982 : /***********************************************************************/
1983 : /* GetFeature() */
1984 : /***********************************************************************/
1985 :
1986 1344 : OGRFeature *OGROpenFileGDBLayer::GetFeature(GIntBig nFeatureId)
1987 : {
1988 1344 : if (!BuildLayerDefinition())
1989 0 : return nullptr;
1990 :
1991 1344 : if (nFeatureId < 1 || nFeatureId > m_poLyrTable->GetTotalRecordCount())
1992 282 : return nullptr;
1993 1062 : if (!m_poLyrTable->SelectRow(nFeatureId - 1))
1994 3 : return nullptr;
1995 :
1996 : /* Temporarily disable spatial filter */
1997 1059 : OGRGeometry *poOldSpatialFilter = m_poFilterGeom;
1998 1059 : m_poFilterGeom = nullptr;
1999 : /* and also spatial index state to avoid features to be inserted */
2000 : /* multiple times in spatial index */
2001 1059 : SPIState eOldState = m_eSpatialIndexState;
2002 1059 : m_eSpatialIndexState = SPI_INVALID;
2003 :
2004 1059 : OGRFeature *poFeature = GetCurrentFeature();
2005 :
2006 : /* Set it back */
2007 1059 : m_poFilterGeom = poOldSpatialFilter;
2008 1059 : m_eSpatialIndexState = eOldState;
2009 :
2010 1059 : return poFeature;
2011 : }
2012 :
2013 : /***********************************************************************/
2014 : /* SetNextByIndex() */
2015 : /***********************************************************************/
2016 :
2017 319 : OGRErr OGROpenFileGDBLayer::SetNextByIndex(GIntBig nIndex)
2018 : {
2019 319 : if (m_poAttributeIterator != nullptr || m_poSpatialIndexIterator != nullptr)
2020 0 : return OGRLayer::SetNextByIndex(nIndex);
2021 :
2022 319 : if (!BuildLayerDefinition())
2023 0 : return OGRERR_FAILURE;
2024 :
2025 319 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2026 1 : m_eSpatialIndexState = SPI_INVALID;
2027 :
2028 319 : if (m_nFilteredFeatureCount >= 0)
2029 : {
2030 3 : if (nIndex < 0 || nIndex >= m_nFilteredFeatureCount)
2031 2 : return OGRERR_FAILURE;
2032 1 : m_iCurFeat = nIndex;
2033 1 : return OGRERR_NONE;
2034 : }
2035 632 : else if (m_poLyrTable->GetValidRecordCount() ==
2036 316 : m_poLyrTable->GetTotalRecordCount())
2037 : {
2038 288 : if (nIndex < 0 || nIndex >= m_poLyrTable->GetValidRecordCount())
2039 160 : return OGRERR_FAILURE;
2040 128 : m_iCurFeat = nIndex;
2041 128 : return OGRERR_NONE;
2042 : }
2043 : else
2044 28 : return OGRLayer::SetNextByIndex(nIndex);
2045 : }
2046 :
2047 : /***********************************************************************/
2048 : /* IGetExtent() */
2049 : /***********************************************************************/
2050 :
2051 2062 : OGRErr OGROpenFileGDBLayer::IGetExtent(int /* iGeomField */,
2052 : OGREnvelope *psExtent, bool /* bForce */)
2053 : {
2054 2062 : if (!BuildLayerDefinition())
2055 0 : return OGRERR_FAILURE;
2056 :
2057 2062 : if (m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2058 : {
2059 : FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2060 2048 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2061 2048 : if (!std::isnan(poGDBGeomField->GetXMin()))
2062 : {
2063 1995 : psExtent->MinX = poGDBGeomField->GetXMin();
2064 1995 : psExtent->MinY = poGDBGeomField->GetYMin();
2065 1995 : psExtent->MaxX = poGDBGeomField->GetXMax();
2066 1995 : psExtent->MaxY = poGDBGeomField->GetYMax();
2067 1995 : return OGRERR_NONE;
2068 : }
2069 : }
2070 :
2071 67 : return OGRERR_FAILURE;
2072 : }
2073 :
2074 : /***********************************************************************/
2075 : /* IGetExtent3D() */
2076 : /***********************************************************************/
2077 :
2078 4 : OGRErr OGROpenFileGDBLayer::IGetExtent3D(int iGeomField,
2079 : OGREnvelope3D *psExtent, bool bForce)
2080 : {
2081 4 : if (!BuildLayerDefinition())
2082 0 : return OGRERR_FAILURE;
2083 :
2084 4 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2085 8 : m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2086 : {
2087 : FileGDBGeomField *poGDBGeomField = reinterpret_cast<FileGDBGeomField *>(
2088 3 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2089 3 : if (!std::isnan(poGDBGeomField->GetXMin()))
2090 : {
2091 3 : psExtent->MinX = poGDBGeomField->GetXMin();
2092 3 : psExtent->MinY = poGDBGeomField->GetYMin();
2093 3 : psExtent->MaxX = poGDBGeomField->GetXMax();
2094 3 : psExtent->MaxY = poGDBGeomField->GetYMax();
2095 3 : if (!std::isnan(poGDBGeomField->GetZMin()))
2096 : {
2097 1 : psExtent->MinZ = poGDBGeomField->GetZMin();
2098 1 : psExtent->MaxZ = poGDBGeomField->GetZMax();
2099 : }
2100 : else
2101 : {
2102 2 : if (OGR_GT_HasZ(m_eGeomType))
2103 : {
2104 1 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
2105 : }
2106 1 : psExtent->MinZ = std::numeric_limits<double>::infinity();
2107 1 : psExtent->MaxZ = -std::numeric_limits<double>::infinity();
2108 : }
2109 2 : return OGRERR_NONE;
2110 : }
2111 : }
2112 :
2113 1 : return OGRLayer::IGetExtent3D(iGeomField, psExtent, bForce);
2114 : }
2115 :
2116 : /***********************************************************************/
2117 : /* GetFeatureCount() */
2118 : /***********************************************************************/
2119 :
2120 1351 : GIntBig OGROpenFileGDBLayer::GetFeatureCount(int bForce)
2121 : {
2122 1351 : if (!BuildLayerDefinition())
2123 1 : return 0;
2124 :
2125 : /* No filter */
2126 1350 : if ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2127 1255 : m_poAttrQuery == nullptr)
2128 : {
2129 871 : return m_poLyrTable->GetValidRecordCount();
2130 : }
2131 479 : else if (m_nFilteredFeatureCount >= 0 && m_poAttrQuery == nullptr)
2132 : {
2133 1 : return m_nFilteredFeatureCount;
2134 : }
2135 :
2136 : /* Only geometry filter ? */
2137 478 : if (m_poAttrQuery == nullptr && m_bFilterIsEnvelope)
2138 : {
2139 82 : if (m_poSpatialIndexIterator)
2140 : {
2141 63 : m_poSpatialIndexIterator->Reset();
2142 63 : int nCount = 0;
2143 : while (true)
2144 : {
2145 : const auto nRowIdx =
2146 300 : m_poSpatialIndexIterator->GetNextRowSortedByFID();
2147 300 : if (nRowIdx < 0)
2148 63 : break;
2149 237 : if (!m_poLyrTable->SelectRow(nRowIdx))
2150 : {
2151 0 : if (m_poLyrTable->HasGotError())
2152 0 : break;
2153 : else
2154 0 : continue;
2155 : }
2156 :
2157 : const OGRField *psField =
2158 237 : m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2159 237 : if (psField != nullptr)
2160 : {
2161 237 : if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(
2162 237 : psField))
2163 : {
2164 : OGRGeometry *poGeom =
2165 187 : m_poGeomConverter->GetAsGeometry(psField);
2166 187 : if (poGeom != nullptr && FilterGeometry(poGeom))
2167 : {
2168 177 : nCount++;
2169 : }
2170 187 : delete poGeom;
2171 : }
2172 : }
2173 237 : }
2174 63 : return nCount;
2175 : }
2176 :
2177 19 : int nCount = 0;
2178 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING && m_iCurFeat != 0)
2179 0 : m_eSpatialIndexState = SPI_INVALID;
2180 :
2181 19 : int nFilteredFeatureCountAlloc = 0;
2182 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2183 : {
2184 2 : CPLFree(m_pahFilteredFeatures);
2185 2 : m_pahFilteredFeatures = nullptr;
2186 2 : m_nFilteredFeatureCount = 0;
2187 : }
2188 :
2189 1492240 : for (int64_t i = 0; i < m_poLyrTable->GetTotalRecordCount(); i++)
2190 : {
2191 1492220 : if (!m_poLyrTable->SelectRow(i))
2192 : {
2193 1489660 : if (m_poLyrTable->HasGotError())
2194 0 : break;
2195 : else
2196 1489660 : continue;
2197 : }
2198 : #if SIZEOF_VOIDP < 8
2199 : if (i > INT32_MAX)
2200 : {
2201 : // CPLQuadTreeInsertWithBounds stores row index values as void*
2202 : // This would overflow here.
2203 : m_eSpatialIndexState = SPI_INVALID;
2204 : break;
2205 : }
2206 : #endif
2207 :
2208 : const OGRField *psField =
2209 2562 : m_poLyrTable->GetFieldValue(m_iGeomFieldIdx);
2210 2562 : if (psField != nullptr)
2211 : {
2212 2562 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2213 : {
2214 14 : OGREnvelope sFeatureEnvelope;
2215 14 : if (m_poLyrTable->GetFeatureExtent(psField,
2216 14 : &sFeatureEnvelope))
2217 : {
2218 : CPLRectObj sBounds;
2219 14 : sBounds.minx = sFeatureEnvelope.MinX;
2220 14 : sBounds.miny = sFeatureEnvelope.MinY;
2221 14 : sBounds.maxx = sFeatureEnvelope.MaxX;
2222 14 : sBounds.maxy = sFeatureEnvelope.MaxY;
2223 14 : CPLQuadTreeInsertWithBounds(
2224 : m_pQuadTree,
2225 : reinterpret_cast<void *>(static_cast<uintptr_t>(i)),
2226 : &sBounds);
2227 : }
2228 : }
2229 :
2230 2562 : if (m_poLyrTable->DoesGeometryIntersectsFilterEnvelope(psField))
2231 : {
2232 : OGRGeometry *poGeom =
2233 2315 : m_poGeomConverter->GetAsGeometry(psField);
2234 2315 : if (poGeom != nullptr && FilterGeometry(poGeom))
2235 : {
2236 2305 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2237 : {
2238 1 : if (nCount == nFilteredFeatureCountAlloc)
2239 : {
2240 1 : nFilteredFeatureCountAlloc =
2241 1 : 4 * nFilteredFeatureCountAlloc / 3 + 1024;
2242 1 : m_pahFilteredFeatures = static_cast<void **>(
2243 1 : CPLRealloc(m_pahFilteredFeatures,
2244 : sizeof(void *) *
2245 1 : nFilteredFeatureCountAlloc));
2246 : }
2247 1 : m_pahFilteredFeatures[nCount] =
2248 : reinterpret_cast<void *>(
2249 : static_cast<uintptr_t>(i));
2250 : }
2251 2305 : nCount++;
2252 : }
2253 2315 : delete poGeom;
2254 : }
2255 : }
2256 : }
2257 19 : if (m_eSpatialIndexState == SPI_IN_BUILDING)
2258 : {
2259 2 : m_nFilteredFeatureCount = nCount;
2260 2 : m_eSpatialIndexState = SPI_COMPLETED;
2261 : }
2262 :
2263 19 : return nCount;
2264 : }
2265 : /* Only simple attribute filter ? */
2266 396 : else if (m_poFilterGeom == nullptr && m_poAttributeIterator != nullptr &&
2267 127 : m_bIteratorSufficientToEvaluateFilter)
2268 : {
2269 118 : return m_poAttributeIterator->GetRowCount();
2270 : }
2271 :
2272 278 : return OGRLayer::GetFeatureCount(bForce);
2273 : }
2274 :
2275 : /***********************************************************************/
2276 : /* TestCapability() */
2277 : /***********************************************************************/
2278 :
2279 3934 : int OGROpenFileGDBLayer::TestCapability(const char *pszCap)
2280 : {
2281 3934 : if (!BuildLayerDefinition())
2282 0 : return FALSE;
2283 :
2284 3934 : if (EQUAL(pszCap, OLCCreateField) || EQUAL(pszCap, OLCDeleteField) ||
2285 3649 : EQUAL(pszCap, OLCAlterFieldDefn) ||
2286 3557 : EQUAL(pszCap, OLCAlterGeomFieldDefn) ||
2287 3556 : EQUAL(pszCap, OLCSequentialWrite) || EQUAL(pszCap, OLCRandomWrite) ||
2288 3357 : EQUAL(pszCap, OLCDeleteFeature) || EQUAL(pszCap, OLCRename))
2289 : {
2290 669 : return m_bEditable;
2291 : }
2292 :
2293 3265 : if (EQUAL(pszCap, OLCFastFeatureCount))
2294 : {
2295 4 : return ((m_poFilterGeom == nullptr || m_iGeomFieldIdx < 0) &&
2296 4 : m_poAttrQuery == nullptr);
2297 : }
2298 3263 : else if (EQUAL(pszCap, OLCFastSetNextByIndex))
2299 : {
2300 1 : return (m_poLyrTable->GetValidRecordCount() ==
2301 1 : m_poLyrTable->GetTotalRecordCount() &&
2302 2 : m_poAttributeIterator == nullptr &&
2303 2 : m_poSpatialIndexIterator == nullptr);
2304 : }
2305 3262 : else if (EQUAL(pszCap, OLCRandomRead))
2306 : {
2307 2 : return TRUE;
2308 : }
2309 3260 : else if (EQUAL(pszCap, OLCFastGetExtent))
2310 : {
2311 114 : return TRUE;
2312 : }
2313 3146 : else if (EQUAL(pszCap, OLCFastGetExtent3D))
2314 : {
2315 5 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr &&
2316 10 : m_iGeomFieldIdx >= 0 && m_poLyrTable->GetValidRecordCount() > 0)
2317 : {
2318 : FileGDBGeomField *poGDBGeomField =
2319 : reinterpret_cast<FileGDBGeomField *>(
2320 3 : m_poLyrTable->GetField(m_iGeomFieldIdx));
2321 3 : if (!std::isnan(poGDBGeomField->GetXMin()))
2322 : {
2323 3 : if (!std::isnan(poGDBGeomField->GetZMin()))
2324 : {
2325 1 : return TRUE;
2326 : }
2327 : else
2328 : {
2329 2 : return !OGR_GT_HasZ(m_eGeomType);
2330 : }
2331 : }
2332 : }
2333 2 : return FALSE;
2334 : }
2335 3141 : else if (EQUAL(pszCap, OLCIgnoreFields))
2336 : {
2337 90 : return TRUE;
2338 : }
2339 3051 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
2340 : {
2341 1272 : return TRUE; /* ? */
2342 : }
2343 :
2344 1779 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
2345 621 : return TRUE;
2346 :
2347 1158 : else if (EQUAL(pszCap, OLCCurveGeometries))
2348 666 : return TRUE;
2349 :
2350 492 : else if (EQUAL(pszCap, OLCZGeometries))
2351 270 : return TRUE;
2352 :
2353 222 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
2354 : {
2355 3 : return m_eSpatialIndexState == SPI_COMPLETED ||
2356 1 : (m_poLyrTable->CanUseIndices() &&
2357 2 : m_poLyrTable->HasSpatialIndex());
2358 : }
2359 :
2360 221 : return FALSE;
2361 : }
2362 :
2363 : /***********************************************************************/
2364 : /* HasIndexForField() */
2365 : /***********************************************************************/
2366 :
2367 25 : bool OGROpenFileGDBLayer::HasIndexForField(const char *pszFieldName)
2368 : {
2369 25 : if (!BuildLayerDefinition())
2370 0 : return false;
2371 25 : if (!m_poLyrTable->CanUseIndices())
2372 0 : return false;
2373 25 : int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2374 49 : return (nTableColIdx >= 0 &&
2375 49 : m_poLyrTable->GetField(nTableColIdx)->HasIndex());
2376 : }
2377 :
2378 : /***********************************************************************/
2379 : /* BuildIndex() */
2380 : /***********************************************************************/
2381 :
2382 17 : FileGDBIterator *OGROpenFileGDBLayer::BuildIndex(const char *pszFieldName,
2383 : int bAscending, int op,
2384 : swq_expr_node *poValue)
2385 : {
2386 17 : if (!BuildLayerDefinition())
2387 0 : return nullptr;
2388 :
2389 17 : int idx = GetLayerDefn()->GetFieldIndex(pszFieldName);
2390 17 : if (idx < 0)
2391 0 : return nullptr;
2392 17 : OGRFieldDefn *poFieldDefn = GetLayerDefn()->GetFieldDefn(idx);
2393 :
2394 17 : int nTableColIdx = m_poLyrTable->GetFieldIdx(pszFieldName);
2395 17 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2396 : {
2397 17 : if (op < 0)
2398 15 : return FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx,
2399 17 : bAscending);
2400 :
2401 : OGRField sValue;
2402 2 : if (FillTargetValueFromSrcExpr(poFieldDefn, &sValue, poValue))
2403 : {
2404 : FileGDBSQLOp eOp;
2405 2 : switch (op)
2406 : {
2407 0 : case SWQ_LE:
2408 0 : eOp = FGSO_LE;
2409 0 : break;
2410 0 : case SWQ_LT:
2411 0 : eOp = FGSO_LT;
2412 0 : break;
2413 2 : case SWQ_EQ:
2414 2 : eOp = FGSO_EQ;
2415 2 : break;
2416 0 : case SWQ_GE:
2417 0 : eOp = FGSO_GE;
2418 0 : break;
2419 0 : case SWQ_GT:
2420 0 : eOp = FGSO_GT;
2421 0 : break;
2422 0 : default:
2423 0 : return nullptr;
2424 : }
2425 :
2426 2 : return FileGDBIterator::Build(m_poLyrTable, nTableColIdx,
2427 : bAscending, eOp,
2428 2 : poFieldDefn->GetType(), &sValue);
2429 : }
2430 : }
2431 0 : return nullptr;
2432 : }
2433 :
2434 : /***********************************************************************/
2435 : /* GetMinMaxValue() */
2436 : /***********************************************************************/
2437 :
2438 55 : const OGRField *OGROpenFileGDBLayer::GetMinMaxValue(OGRFieldDefn *poFieldDefn,
2439 : int bIsMin, int &eOutType)
2440 : {
2441 55 : eOutType = -1;
2442 55 : if (!BuildLayerDefinition())
2443 0 : return nullptr;
2444 55 : if (!m_poLyrTable->CanUseIndices())
2445 0 : return nullptr;
2446 :
2447 : const int nTableColIdx =
2448 55 : m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2449 55 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2450 : {
2451 50 : delete m_poIterMinMax;
2452 50 : m_poIterMinMax =
2453 50 : FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE);
2454 50 : if (m_poIterMinMax != nullptr)
2455 : {
2456 : const OGRField *poRet = (bIsMin)
2457 50 : ? m_poIterMinMax->GetMinValue(eOutType)
2458 36 : : m_poIterMinMax->GetMaxValue(eOutType);
2459 50 : if (poRet == nullptr)
2460 2 : eOutType = poFieldDefn->GetType();
2461 50 : return poRet;
2462 : }
2463 : }
2464 5 : return nullptr;
2465 : }
2466 :
2467 : /***********************************************************************/
2468 : /* GetMinMaxSumCount() */
2469 : /***********************************************************************/
2470 :
2471 8 : int OGROpenFileGDBLayer::GetMinMaxSumCount(OGRFieldDefn *poFieldDefn,
2472 : double &dfMin, double &dfMax,
2473 : double &dfSum, int &nCount)
2474 : {
2475 8 : dfMin = 0.0;
2476 8 : dfMax = 0.0;
2477 8 : dfSum = 0.0;
2478 8 : nCount = 0;
2479 8 : if (!BuildLayerDefinition())
2480 0 : return false;
2481 8 : if (!m_poLyrTable->CanUseIndices())
2482 0 : return false;
2483 :
2484 8 : int nTableColIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
2485 8 : if (nTableColIdx >= 0 && m_poLyrTable->GetField(nTableColIdx)->HasIndex())
2486 : {
2487 : auto poIter = std::unique_ptr<FileGDBIterator>(
2488 8 : FileGDBIterator::BuildIsNotNull(m_poLyrTable, nTableColIdx, TRUE));
2489 8 : if (poIter)
2490 : {
2491 8 : return poIter->GetMinMaxSumCount(dfMin, dfMax, dfSum, nCount);
2492 : }
2493 : }
2494 0 : return false;
2495 : }
2496 :
2497 : /************************************************************************/
2498 : /* GetDataset() */
2499 : /************************************************************************/
2500 :
2501 67 : GDALDataset *OGROpenFileGDBLayer::GetDataset()
2502 : {
2503 67 : return m_poDS;
2504 : }
|