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

Generated by: LCOV version 1.14