LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - ogropenfilegdblayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1162 1296 89.7 %
Date: 2026-04-15 22:10:00 Functions: 35 35 100.0 %

          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             : }

Generated by: LCOV version 1.14