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