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