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

Generated by: LCOV version 1.14