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

Generated by: LCOV version 1.14