LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - ogropenfilegdbdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 955 1062 89.9 %
Date: 2026-05-07 20:04:54 Functions: 44 45 97.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB OGR driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_openfilegdb.h"
      15             : 
      16             : #include <stddef.h>
      17             : #include <stdio.h>
      18             : #include <string.h>
      19             : #include <map>
      20             : #include <memory>
      21             : #include <set>
      22             : #include <string>
      23             : #include <utility>
      24             : #include <vector>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi.h"
      30             : #include "filegdbtable.h"
      31             : #include "gdal.h"
      32             : #include "ogr_core.h"
      33             : #include "ogr_feature.h"
      34             : #include "ogr_geometry.h"
      35             : #include "memdataset.h"
      36             : #include "ogrsf_frmts.h"
      37             : #include "ogr_swq.h"
      38             : 
      39             : #include "filegdb_fielddomain.h"
      40             : #include "filegdb_relationship.h"
      41             : 
      42             : /************************************************************************/
      43             : /*                         OGROpenFileGDBGroup                          */
      44             : /************************************************************************/
      45             : 
      46             : class OGROpenFileGDBGroup final : public GDALGroup
      47             : {
      48             :   protected:
      49             :     friend class OGROpenFileGDBDataSource;
      50             :     std::vector<std::shared_ptr<GDALGroup>> m_apoSubGroups{};
      51             :     std::vector<OGRLayer *> m_apoLayers{};
      52             :     std::string m_osDefinition{};
      53             : 
      54             :   public:
      55         518 :     OGROpenFileGDBGroup(const std::string &osParentName, const char *pszName)
      56         518 :         : GDALGroup(osParentName, pszName)
      57             :     {
      58         518 :     }
      59             : 
      60          21 :     void SetDefinition(const std::string &osDefinition)
      61             :     {
      62          21 :         m_osDefinition = osDefinition;
      63          21 :     }
      64             : 
      65          28 :     const std::string &GetDefinition() const
      66             :     {
      67          28 :         return m_osDefinition;
      68             :     }
      69             : 
      70             :     std::vector<std::string>
      71             :     GetGroupNames(CSLConstList papszOptions) const override;
      72             :     std::shared_ptr<GDALGroup>
      73             :     OpenGroup(const std::string &osName,
      74             :               CSLConstList papszOptions) const override;
      75             : 
      76             :     std::vector<std::string>
      77             :     GetVectorLayerNames(CSLConstList papszOptions) const override;
      78             :     OGRLayer *OpenVectorLayer(const std::string &osName,
      79             :                               CSLConstList papszOptions) const override;
      80             : };
      81             : 
      82             : /************************************************************************/
      83             : /*                      OGROpenFileGDBDataSource()                      */
      84             : /************************************************************************/
      85         808 : OGROpenFileGDBDataSource::OGROpenFileGDBDataSource()
      86         808 :     : m_papszFiles(nullptr), bLastSQLUsedOptimizedImplementation(false)
      87             : {
      88         808 : }
      89             : 
      90             : /************************************************************************/
      91             : /*                     ~OGROpenFileGDBDataSource()                      */
      92             : /************************************************************************/
      93        1616 : OGROpenFileGDBDataSource::~OGROpenFileGDBDataSource()
      94             : 
      95             : {
      96         808 :     OGROpenFileGDBDataSource::Close();
      97        1616 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                               Close()                                */
     101             : /************************************************************************/
     102             : 
     103        1525 : CPLErr OGROpenFileGDBDataSource::Close(GDALProgressFunc, void *)
     104             : {
     105        1525 :     CPLErr eErr = CE_None;
     106        1525 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     107             :     {
     108         808 :         if (m_bInTransaction)
     109           2 :             OGROpenFileGDBDataSource::RollbackTransaction();
     110             : 
     111         808 :         if (OGROpenFileGDBDataSource::FlushCache(true) != CE_None)
     112           0 :             eErr = CE_Failure;
     113             : 
     114         808 :         m_apoLayers.clear();
     115         808 :         m_apoHiddenLayers.clear();
     116         808 :         CSLDestroy(m_papszFiles);
     117             : 
     118         808 :         if (GDALDataset::Close() != CE_None)
     119           0 :             eErr = CE_Failure;
     120             :     }
     121        1525 :     return eErr;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                             FileExists()                             */
     126             : /************************************************************************/
     127             : 
     128       17512 : int OGROpenFileGDBDataSource::FileExists(const char *pszFilename)
     129             : {
     130       17512 :     if (m_papszFiles)
     131       17503 :         return CSLFindString(m_papszFiles, CPLGetFilename(pszFilename)) >= 0;
     132             : 
     133             :     VSIStatBufL sStat;
     134           9 :     CPLString osFilename(pszFilename);
     135           9 :     return VSIStatExL(osFilename, &sStat, VSI_STAT_EXISTS_FLAG) == 0;
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                                Open()                                */
     140             : /************************************************************************/
     141             : 
     142         563 : bool OGROpenFileGDBDataSource::Open(const GDALOpenInfo *poOpenInfo,
     143             :                                     bool &bRetryFileGDBOut)
     144             : {
     145         563 :     bRetryFileGDBOut = false;
     146             : 
     147         563 :     if (poOpenInfo->nOpenFlags == (GDAL_OF_RASTER | GDAL_OF_UPDATE))
     148             :     {
     149           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     150             :                  "Update mode of rasters is not supported");
     151           0 :         return false;
     152             :     }
     153             : 
     154         563 :     m_osDirName = poOpenInfo->pszFilename;
     155             : 
     156        1126 :     std::string osRasterLayerName;
     157         563 :     if (STARTS_WITH(poOpenInfo->pszFilename, "OpenFileGDB:"))
     158             :     {
     159             :         const CPLStringList aosTokens(CSLTokenizeString2(
     160          29 :             poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
     161          29 :         if (aosTokens.size() == 4 && strlen(aosTokens[1]) == 1)
     162             :         {
     163           0 :             m_osDirName = aosTokens[1];
     164           0 :             m_osDirName += ':';
     165           0 :             m_osDirName += aosTokens[2];
     166           0 :             osRasterLayerName = aosTokens[3];
     167             :         }
     168          29 :         else if (aosTokens.size() == 3)
     169             :         {
     170          29 :             m_osDirName = aosTokens[1];
     171          29 :             osRasterLayerName = aosTokens[2];
     172             :         }
     173             :         else
     174             :         {
     175           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid connection string");
     176           0 :             return false;
     177             :         }
     178             :     }
     179             : 
     180        1126 :     FileGDBTable oTable;
     181             : 
     182             :     // Whether to open directly a given .gdbtable file (mostly for debugging
     183             :     // purposes)
     184         563 :     int nInterestTable = 0;
     185         563 :     unsigned int unInterestTable = 0;
     186         563 :     const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
     187        1205 :     if (poOpenInfo->nHeaderBytes > 0 &&
     188          79 :         strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
     189          22 :         pszFilenameWithoutPath[0] == 'a' &&
     190         663 :         EQUAL(pszFilenameWithoutPath + strlen("a00000000"), ".gdbtable") &&
     191          21 :         sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
     192             :     {
     193          21 :         nInterestTable = static_cast<int>(unInterestTable);
     194          21 :         m_osDirName = CPLGetPathSafe(m_osDirName);
     195             :     }
     196             : 
     197         655 :     if (EQUAL(CPLGetExtensionSafe(m_osDirName).c_str(), "zip") &&
     198          92 :         !STARTS_WITH(m_osDirName, "/vsizip/"))
     199             :     {
     200          85 :         m_osDirName = "/vsizip/" + m_osDirName;
     201             :     }
     202         478 :     else if (EQUAL(CPLGetExtensionSafe(m_osDirName).c_str(), "tar") &&
     203           0 :              !STARTS_WITH(m_osDirName, "/vsitar/"))
     204             :     {
     205           0 :         m_osDirName = "/vsitar/" + m_osDirName;
     206             :     }
     207             : 
     208        1031 :     if (STARTS_WITH(m_osDirName, "/vsizip/") ||
     209         468 :         STARTS_WITH(m_osDirName, "/vsitar/"))
     210             :     {
     211             :         /* Look for one subdirectory ending with .gdb extension */
     212          96 :         char **papszDir = VSIReadDir(m_osDirName);
     213          96 :         int iCandidate = -1;
     214         822 :         for (int i = 0; papszDir && papszDir[i] != nullptr; i++)
     215             :         {
     216             :             VSIStatBufL sStat;
     217         726 :             if (EQUAL(CPLGetExtensionSafe(papszDir[i]).c_str(), "gdb") &&
     218          85 :                 VSIStatL(CPLSPrintf("%s/%s", m_osDirName.c_str(), papszDir[i]),
     219         811 :                          &sStat) == 0 &&
     220          85 :                 VSI_ISDIR(sStat.st_mode))
     221             :             {
     222          85 :                 if (iCandidate < 0)
     223          85 :                     iCandidate = i;
     224             :                 else
     225             :                 {
     226           0 :                     iCandidate = -1;
     227           0 :                     break;
     228             :                 }
     229             :             }
     230             :         }
     231          96 :         if (iCandidate >= 0)
     232             :         {
     233          85 :             m_osDirName += "/";
     234          85 :             m_osDirName += papszDir[iCandidate];
     235             :         }
     236          96 :         CSLDestroy(papszDir);
     237             :     }
     238             : 
     239             :     const std::string osTransactionBackupDirname = CPLFormFilenameSafe(
     240        1126 :         m_osDirName.c_str(), ".ogrtransaction_backup", nullptr);
     241             :     VSIStatBufL sStat;
     242         563 :     if (VSIStatL(osTransactionBackupDirname.c_str(), &sStat) == 0)
     243             :     {
     244           2 :         if (poOpenInfo->eAccess == GA_Update)
     245             :         {
     246           1 :             CPLError(
     247             :                 CE_Failure, CPLE_AppDefined,
     248             :                 "A previous backup directory %s already exists, which means "
     249             :                 "that a previous transaction was not cleanly committed or "
     250             :                 "rolled back.\n"
     251             :                 "Either manually restore the previous state from that "
     252             :                 "directory or remove it, before opening in update mode.",
     253             :                 osTransactionBackupDirname.c_str());
     254           1 :             return false;
     255             :         }
     256             :         else
     257             :         {
     258           1 :             CPLError(
     259             :                 CE_Warning, CPLE_AppDefined,
     260             :                 "A previous backup directory %s already exists, which means "
     261             :                 "that a previous transaction was not cleanly committed or "
     262             :                 "rolled back.\n"
     263             :                 "You may want to restore the previous state from that "
     264             :                 "directory or remove it.",
     265             :                 osTransactionBackupDirname.c_str());
     266             :         }
     267             :     }
     268             : 
     269         562 :     m_papszFiles = VSIReadDir(m_osDirName);
     270             : 
     271             :     /* Explore catalog table */
     272             :     m_osGDBSystemCatalogFilename =
     273         562 :         CPLFormFilenameSafe(m_osDirName, "a00000001", "gdbtable");
     274        1114 :     if (!FileExists(m_osGDBSystemCatalogFilename.c_str()) ||
     275         552 :         !oTable.Open(m_osGDBSystemCatalogFilename.c_str(),
     276         552 :                      poOpenInfo->eAccess == GA_Update))
     277             :     {
     278          36 :         if (nInterestTable > 0 && FileExists(poOpenInfo->pszFilename))
     279             :         {
     280           1 :             const char *pszLyrName = CPLSPrintf("a%08x", nInterestTable);
     281             :             auto poLayer = std::make_unique<OGROpenFileGDBLayer>(
     282           1 :                 this, poOpenInfo->pszFilename, pszLyrName, "", "",
     283           2 :                 eAccess == GA_Update);
     284             :             const std::string osTablX =
     285           2 :                 CPLResetExtensionSafe(poOpenInfo->pszFilename, "gdbtablx");
     286           1 :             if ((!FileExists(osTablX.c_str()) &&
     287           0 :                  poLayer->GetLayerDefn()->GetFieldCount() == 0 &&
     288           2 :                  poLayer->GetFeatureCount() == 0) ||
     289           1 :                 !poLayer->IsValidLayerDefn())
     290             :             {
     291           0 :                 return false;
     292             :             }
     293           1 :             m_apoLayers.push_back(std::move(poLayer));
     294           1 :             return true;
     295             :         }
     296          35 :         return false;
     297             :     }
     298             : 
     299         526 :     const int idxName = oTable.GetFieldIdx("Name");
     300         526 :     const int idxFileformat = oTable.GetFieldIdx("FileFormat");
     301        2098 :     if (!(idxName >= 0 && idxFileformat >= 0 &&
     302         524 :           oTable.GetField(idxName)->GetType() == FGFT_STRING &&
     303        1048 :           (oTable.GetField(idxFileformat)->GetType() == FGFT_INT16 ||
     304         524 :            oTable.GetField(idxFileformat)->GetType() == FGFT_INT32)))
     305             :     {
     306           2 :         return false;
     307             :     }
     308             : 
     309         524 :     int iGDBItems = -1;          /* V10 */
     310         524 :     int iGDBFeatureClasses = -1; /* V9.X */
     311         524 :     int iGDBObjectClasses = -1;  /* V9.X */
     312             : 
     313        1048 :     std::vector<std::string> aosTableNames;
     314             :     try
     315             :     {
     316       20180 :         for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     317             :         {
     318       19656 :             if (!oTable.SelectRow(i))
     319             :             {
     320        5003 :                 if (oTable.HasGotError())
     321           0 :                     break;
     322        5003 :                 aosTableNames.push_back("");
     323        5003 :                 continue;
     324             :             }
     325             : 
     326       14653 :             const OGRField *psField = oTable.GetFieldValue(idxName);
     327       14653 :             if (psField != nullptr)
     328             :             {
     329       14563 :                 aosTableNames.push_back(psField->String);
     330       14563 :                 if (strcmp(psField->String, "GDB_SpatialRefs") == 0)
     331             :                 {
     332             :                     // Normally a00000003
     333        1044 :                     m_osGDBSpatialRefsFilename = CPLFormFilenameSafe(
     334             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     335         522 :                         nullptr);
     336             :                 }
     337       14041 :                 else if (strcmp(psField->String, "GDB_Items") == 0)
     338             :                 {
     339             :                     // Normally a00000004 but it has been seen in some datasets
     340             :                     // to not be a00000004
     341         994 :                     m_osGDBItemsFilename = CPLFormFilenameSafe(
     342             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     343         497 :                         nullptr);
     344         497 :                     iGDBItems = i;
     345             :                 }
     346       13544 :                 else if (strcmp(psField->String, "GDB_ItemRelationships") == 0)
     347             :                 {
     348             :                     // Normally a00000006 but can be a00000005 sometimes (e.g
     349             :                     // autotest/ogr/data/filegdb/Domains.gdb)
     350         998 :                     m_osGDBItemRelationshipsFilename = CPLFormFilenameSafe(
     351             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     352         499 :                         nullptr);
     353             :                 }
     354       13045 :                 else if (strcmp(psField->String, "GDB_FeatureClasses") == 0)
     355             :                 {
     356             :                     // FileGDB v9
     357          23 :                     iGDBFeatureClasses = i;
     358             :                 }
     359       13022 :                 else if (strcmp(psField->String, "GDB_ObjectClasses") == 0)
     360             :                 {
     361             :                     // FileGDB v9
     362          23 :                     iGDBObjectClasses = i;
     363             :                 }
     364       14563 :                 m_osMapNameToIdx[psField->String] = 1 + i;
     365             :             }
     366             :             else
     367             :             {
     368          90 :                 aosTableNames.push_back("");
     369             :             }
     370             :         }
     371             :     }
     372           0 :     catch (const std::exception &)
     373             :     {
     374           0 :         return false;
     375             :     }
     376             : 
     377         524 :     oTable.Close();
     378             : 
     379        1048 :     std::set<int> oSetIgnoredRasterLayerTableNum;
     380         524 :     if (iGDBItems >= 0)
     381             :     {
     382         497 :         eAccess = poOpenInfo->eAccess;
     383             : 
     384         497 :         const bool bRet = OpenFileGDBv10(
     385             :             iGDBItems, nInterestTable, poOpenInfo, osRasterLayerName,
     386             :             oSetIgnoredRasterLayerTableNum, bRetryFileGDBOut);
     387         497 :         if (!bRet)
     388           2 :             return false;
     389             :     }
     390          27 :     else if (iGDBFeatureClasses >= 0 && iGDBObjectClasses >= 0)
     391             :     {
     392          23 :         if (poOpenInfo->eAccess == GA_Update)
     393             :         {
     394           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     395             :                      "Edition of existing FileGDB v9 datasets not supported");
     396           0 :             return false;
     397             :         }
     398             : 
     399          23 :         int bRet = OpenFileGDBv9(iGDBFeatureClasses, iGDBObjectClasses,
     400             :                                  nInterestTable, poOpenInfo, osRasterLayerName,
     401             :                                  oSetIgnoredRasterLayerTableNum);
     402          23 :         if (!bRet)
     403          23 :             return false;
     404             :     }
     405             :     else
     406             :     {
     407           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     408             :                  "No GDB_Items nor GDB_FeatureClasses table");
     409           4 :         return false;
     410             :     }
     411             : 
     412         518 :     if (m_apoLayers.empty() && nInterestTable > 0)
     413             :     {
     414          19 :         if (FileExists(poOpenInfo->pszFilename))
     415             :         {
     416          19 :             const char *pszLyrName = nullptr;
     417          38 :             if (nInterestTable <= static_cast<int>(aosTableNames.size()) &&
     418          19 :                 !aosTableNames[nInterestTable - 1].empty())
     419          19 :                 pszLyrName = aosTableNames[nInterestTable - 1].c_str();
     420             :             else
     421           0 :                 pszLyrName = CPLSPrintf("a%08x", nInterestTable);
     422          19 :             m_apoLayers.push_back(std::make_unique<OGROpenFileGDBLayer>(
     423          19 :                 this, poOpenInfo->pszFilename, pszLyrName, "", "",
     424          38 :                 eAccess == GA_Update));
     425             :         }
     426             :         else
     427             :         {
     428           0 :             return false;
     429             :         }
     430             :     }
     431             : 
     432         518 :     if (nInterestTable == 0)
     433             :     {
     434         498 :         const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
     435         498 :             poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "NO"));
     436             : 
     437             :         // add additional tables which are not present in the
     438             :         // GDB_Items/GDB_FeatureClasses/GDB_ObjectClasses tables
     439       11000 :         for (const auto &oIter : m_osMapNameToIdx)
     440             :         {
     441             :             // test if layer is already added
     442       10502 :             if (GDALDataset::GetLayerByName(oIter.first.c_str()))
     443           0 :                 continue;
     444             : 
     445       10502 :             if (bListAllTables || !IsPrivateLayerName(oIter.first))
     446             :             {
     447        5905 :                 const int idx = oIter.second;
     448        5905 :                 const CPLString osFilename(CPLFormFilenameSafe(
     449       11810 :                     m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"));
     450       11570 :                 if (!cpl::contains(oSetIgnoredRasterLayerTableNum, idx) &&
     451        5665 :                     FileExists(osFilename))
     452             :                 {
     453             :                     m_apoLayers.emplace_back(
     454        5284 :                         std::make_unique<OGROpenFileGDBLayer>(
     455        5284 :                             this, osFilename, oIter.first.c_str(), "", "",
     456       10568 :                             eAccess == GA_Update));
     457        5284 :                     if (m_poRootGroup)
     458             :                     {
     459        5260 :                         cpl::down_cast<OGROpenFileGDBGroup *>(
     460             :                             m_poRootGroup.get())
     461             :                             ->m_apoLayers.emplace_back(
     462        5260 :                                 m_apoLayers.back().get());
     463             :                     }
     464             :                 }
     465             :             }
     466             :         }
     467             :     }
     468             : 
     469         518 :     if (!osRasterLayerName.empty())
     470             :     {
     471          29 :         m_aosSubdatasets.Clear();
     472          29 :         SetDescription(poOpenInfo->pszFilename);
     473          29 :         return nBands != 0;
     474             :     }
     475             : 
     476             :     // If opening in raster-only mode, return false if there are no raster
     477             :     // layers
     478         489 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
     479          50 :         (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
     480             :     {
     481          21 :         if (m_aosSubdatasets.empty())
     482             :         {
     483          13 :             return false;
     484             :         }
     485           8 :         else if (m_aosSubdatasets.size() == 2)
     486             :         {
     487             :             // If there is a single raster dataset, open it right away.
     488           7 :             return true;
     489             :         }
     490             :     }
     491             :     // If opening in vector-only mode, return false if there are no vector
     492             :     // layers and opened in read-only mode
     493         468 :     else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     494         439 :              (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
     495             :     {
     496         436 :         if (m_apoLayers.empty() && poOpenInfo->eAccess == GA_ReadOnly)
     497             :         {
     498           2 :             return false;
     499             :         }
     500             :     }
     501             : 
     502         467 :     return true;
     503             : }
     504             : 
     505             : /************************************************************************/
     506             : /*                              AddLayer()                              */
     507             : /************************************************************************/
     508             : 
     509        3801 : OGRLayer *OGROpenFileGDBDataSource::AddLayer(
     510             :     const CPLString &osName, int nInterestTable, int &nCandidateLayers,
     511             :     int &nLayersCDF, const CPLString &osDefinition,
     512             :     const CPLString &osDocumentation, OGRwkbGeometryType eGeomType,
     513             :     const std::string &osParentDefinition)
     514             : {
     515        3801 :     const auto oIter = m_osMapNameToIdx.find(osName);
     516        3801 :     int idx = 0;
     517        3801 :     if (oIter != m_osMapNameToIdx.end())
     518        3787 :         idx = oIter->second;
     519        3801 :     if (idx > 0 && (nInterestTable <= 0 || nInterestTable == idx))
     520             :     {
     521        3710 :         m_osMapNameToIdx.erase(oIter);
     522             : 
     523        7420 :         const CPLString osFilename = CPLFormFilenameSafe(
     524        7420 :             m_osDirName, CPLOPrintf("a%08x", idx).c_str(), "gdbtable");
     525        3710 :         if (FileExists(osFilename))
     526             :         {
     527        3696 :             nCandidateLayers++;
     528             : 
     529        3696 :             if (m_papszFiles != nullptr)
     530             :             {
     531             :                 const CPLString osSDC =
     532        3696 :                     CPLResetExtensionSafe(osFilename, "gdbtable.sdc");
     533             :                 const CPLString osCDF =
     534        3696 :                     CPLResetExtensionSafe(osFilename, "gdbtable.cdf");
     535        3696 :                 if (FileExists(osCDF))
     536             :                 {
     537           3 :                     nLayersCDF++;
     538             : 
     539           3 :                     if (GDALGetDriverByName("FileGDB"))
     540             :                     {
     541           2 :                         CPLDebug(
     542             :                             "OpenFileGDB",
     543             :                             "%s layer has a %s file using Compressed Data "
     544             :                             "Format (CDF) that is unhandled by the OpenFileGDB "
     545             :                             "driver, but could be handled by the FileGDB "
     546             :                             "driver (which will be attempted).",
     547             :                             osName.c_str(), osCDF.c_str());
     548             :                     }
     549             :                     else
     550             :                     {
     551             :                         // Look into hidden drivers, that is drivers that have
     552             :                         // been built as plugins, but are not currently available
     553             :                         // on the system.
     554           2 :                         std::string osFileGDBDriverMsg;
     555           1 :                         auto poDM = GetGDALDriverManager();
     556             :                         const int nAllDrivercount =
     557           1 :                             poDM->GetDriverCount(/* bIncludeHidden = */ true);
     558         229 :                         for (int i = 0; i < nAllDrivercount; ++i)
     559             :                         {
     560             :                             auto poMissingPluginDriver =
     561         228 :                                 poDM->GetDriver(i, /* bIncludeHidden = */ true);
     562         228 :                             if (EQUAL(poMissingPluginDriver->GetDescription(),
     563             :                                       "FileGDB"))
     564             :                             {
     565           0 :                                 osFileGDBDriverMsg += " However the plugin ";
     566             :                                 osFileGDBDriverMsg +=
     567             :                                     poMissingPluginDriver->GetMetadataItem(
     568           0 :                                         "MISSING_PLUGIN_FILENAME");
     569             :                                 osFileGDBDriverMsg +=
     570             :                                     " is not available in your "
     571           0 :                                     "installation.";
     572           0 :                                 if (const char *pszInstallationMsg =
     573           0 :                                         poMissingPluginDriver->GetMetadataItem(
     574           0 :                                             GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE))
     575             :                                 {
     576           0 :                                     osFileGDBDriverMsg += " ";
     577           0 :                                     osFileGDBDriverMsg += pszInstallationMsg;
     578             :                                 }
     579           0 :                                 break;
     580             :                             }
     581             :                         }
     582           1 :                         CPLError(CE_Warning, CPLE_AppDefined,
     583             :                                  "%s layer has a %s file using Compressed Data "
     584             :                                  "Format (CDF) that is unhandled by the "
     585             :                                  "OpenFileGDB driver, but could be handled by "
     586             :                                  "the FileGDB driver.%s",
     587             :                                  osName.c_str(), osCDF.c_str(),
     588             :                                  osFileGDBDriverMsg.c_str());
     589             :                     }
     590           3 :                     return nullptr;
     591             :                 }
     592        3693 :                 else if (FileExists(osSDC))
     593             :                 {
     594             :                     // SDC is not handled by the FileGDB driver
     595           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     596             :                              "%s layer has a %s file using Smart Data "
     597             :                              "Compression (SDC) that is unhandled.",
     598             :                              osName.c_str(), osSDC.c_str());
     599           0 :                     return nullptr;
     600             :                 }
     601             :             }
     602             : 
     603        3693 :             m_apoLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
     604             :                 this, osFilename, osName, osDefinition, osDocumentation,
     605        3693 :                 eAccess == GA_Update, eGeomType, osParentDefinition));
     606        3693 :             return m_apoLayers.back().get();
     607             :         }
     608             :     }
     609         105 :     return nullptr;
     610             : }
     611             : 
     612          11 : std::vector<std::string> OGROpenFileGDBGroup::GetGroupNames(CSLConstList) const
     613             : {
     614          11 :     std::vector<std::string> ret;
     615          18 :     for (const auto &poSubGroup : m_apoSubGroups)
     616           7 :         ret.emplace_back(poSubGroup->GetName());
     617          11 :     return ret;
     618             : }
     619             : 
     620             : std::shared_ptr<GDALGroup>
     621           6 : OGROpenFileGDBGroup::OpenGroup(const std::string &osName, CSLConstList) const
     622             : {
     623          10 :     for (const auto &poSubGroup : m_apoSubGroups)
     624             :     {
     625           9 :         if (poSubGroup->GetName() == osName)
     626           5 :             return poSubGroup;
     627             :     }
     628           1 :     return nullptr;
     629             : }
     630             : 
     631             : std::vector<std::string>
     632          11 : OGROpenFileGDBGroup::GetVectorLayerNames(CSLConstList) const
     633             : {
     634          11 :     std::vector<std::string> ret;
     635          49 :     for (const auto &poLayer : m_apoLayers)
     636          38 :         ret.emplace_back(poLayer->GetName());
     637          11 :     return ret;
     638             : }
     639             : 
     640          23 : OGRLayer *OGROpenFileGDBGroup::OpenVectorLayer(const std::string &osName,
     641             :                                                CSLConstList) const
     642             : {
     643         105 :     for (const auto &poLayer : m_apoLayers)
     644             :     {
     645         104 :         if (poLayer->GetName() == osName)
     646          22 :             return poLayer;
     647             :     }
     648           1 :     return nullptr;
     649             : }
     650             : 
     651             : /************************************************************************/
     652             : /*                           OpenFileGDBv10()                           */
     653             : /************************************************************************/
     654             : 
     655         497 : bool OGROpenFileGDBDataSource::OpenFileGDBv10(
     656             :     int iGDBItems, int nInterestTable, const GDALOpenInfo *poOpenInfo,
     657             :     const std::string &osRasterLayerName,
     658             :     std::set<int> &oSetIgnoredRasterLayerTableNum, bool &bRetryFileGDBOut)
     659             : {
     660             : 
     661         497 :     CPLDebug("OpenFileGDB", "FileGDB v10 or later");
     662             : 
     663         994 :     FileGDBTable oTable;
     664             : 
     665         497 :     const CPLString osFilename(CPLFormFilenameSafe(
     666         994 :         m_osDirName, CPLSPrintf("a%08x.gdbtable", iGDBItems + 1), nullptr));
     667             : 
     668             :     // Normally we don't need to update in update mode the GDB_Items table,
     669             :     // but this may help repairing it, if we have corrupted it with past
     670             :     // GDAL versions.
     671         497 :     const bool bOpenInUpdateMode =
     672         497 :         (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0;
     673         497 :     if (!oTable.Open(osFilename, bOpenInUpdateMode))
     674           0 :         return false;
     675             : 
     676         497 :     const int iUUID = oTable.GetFieldIdx("UUID");
     677         497 :     const int iType = oTable.GetFieldIdx("Type");
     678         497 :     const int iName = oTable.GetFieldIdx("Name");
     679         497 :     const int iPath = oTable.GetFieldIdx("Path");
     680         497 :     const int iDefinition = oTable.GetFieldIdx("Definition");
     681         497 :     const int iDocumentation = oTable.GetFieldIdx("Documentation");
     682         497 :     if (iUUID < 0 || iType < 0 || iName < 0 || iPath < 0 || iDefinition < 0 ||
     683         497 :         iDocumentation < 0 ||
     684         994 :         oTable.GetField(iUUID)->GetType() != FGFT_GLOBALID ||
     685         994 :         oTable.GetField(iType)->GetType() != FGFT_GUID ||
     686         994 :         oTable.GetField(iName)->GetType() != FGFT_STRING ||
     687         994 :         oTable.GetField(iPath)->GetType() != FGFT_STRING ||
     688        1491 :         oTable.GetField(iDefinition)->GetType() != FGFT_XML ||
     689         497 :         oTable.GetField(iDocumentation)->GetType() != FGFT_XML)
     690             :     {
     691           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     692             :                  "Wrong structure for GDB_Items table");
     693           0 :         return false;
     694             :     }
     695             : 
     696         994 :     auto poRootGroup = std::make_shared<OGROpenFileGDBGroup>(std::string(), "");
     697         497 :     m_poRootGroup = poRootGroup;
     698             :     std::map<std::string, std::shared_ptr<OGROpenFileGDBGroup>>
     699         994 :         oMapPathToFeatureDataset;
     700             : 
     701             :     // First pass to collect FeatureDatasets
     702         994 :     std::set<int> oSetBrokenRos;
     703        9775 :     for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     704             :     {
     705        9278 :         if (!oTable.SelectRow(i, /* bWarnOnlyOnDeletedRows = */ true))
     706             :         {
     707        2137 :             oSetBrokenRos.insert(i);
     708        2137 :             if (oTable.HasGotError())
     709           0 :                 break;
     710        2137 :             continue;
     711             :         }
     712             : 
     713       14282 :         CPLString osType;
     714        7141 :         const OGRField *psTypeField = oTable.GetFieldValue(iType);
     715        7141 :         if (psTypeField != nullptr)
     716             :         {
     717        7141 :             const char *pszType = psTypeField->String;
     718        7141 :             if (pszType)
     719        7141 :                 osType = pszType;
     720             :         }
     721             : 
     722       14282 :         std::string osPath;
     723        7141 :         const OGRField *psField = oTable.GetFieldValue(iPath);
     724        7141 :         if (psField != nullptr)
     725             :         {
     726        7141 :             const char *pszPath = psField->String;
     727        7141 :             if (pszPath)
     728        7141 :                 osPath = pszPath;
     729             :         }
     730             : 
     731        7141 :         if (osPath == "\\")
     732             :         {
     733         497 :             psField = oTable.GetFieldValue(iUUID);
     734         497 :             if (psField != nullptr)
     735             :             {
     736         497 :                 const char *pszUUID = psField->String;
     737         497 :                 if (pszUUID)
     738         497 :                     m_osRootGUID = pszUUID;
     739             :             }
     740             :         }
     741             : 
     742        7141 :         psField = oTable.GetFieldValue(iDefinition);
     743        7141 :         if (psField != nullptr &&
     744        6644 :             strstr(psField->String, "DEFeatureDataset") != nullptr)
     745             :         {
     746          42 :             const std::string osDefinition(psField->String);
     747             : 
     748          42 :             std::string osName;
     749          21 :             psField = oTable.GetFieldValue(iName);
     750          21 :             if (psField != nullptr)
     751             :             {
     752          21 :                 const char *pszName = psField->String;
     753          21 :                 if (pszName)
     754          21 :                     osName = pszName;
     755             :             }
     756             : 
     757          21 :             if (!osName.empty() && !osPath.empty())
     758             :             {
     759             :                 auto poSubGroup = std::make_shared<OGROpenFileGDBGroup>(
     760          42 :                     poRootGroup->GetName(), osName.c_str());
     761          21 :                 poSubGroup->SetDefinition(osDefinition);
     762          21 :                 oMapPathToFeatureDataset[osPath] = poSubGroup;
     763          21 :                 poRootGroup->m_apoSubGroups.emplace_back(poSubGroup);
     764          21 :             }
     765             :         }
     766       13743 :         else if (psField != nullptr &&
     767        6623 :                  osType.tolower().compare(pszRelationshipTypeUUID) == 0)
     768             :         {
     769             :             // relationship item
     770         720 :             auto poRelationship = ParseXMLRelationshipDef(psField->String);
     771         240 :             if (poRelationship)
     772             :             {
     773         240 :                 const auto &relationshipName = poRelationship->GetName();
     774         480 :                 m_osMapRelationships.insert(std::pair(
     775         720 :                     std::string(relationshipName), std::move(poRelationship)));
     776             :             }
     777             :         }
     778             :     }
     779             : 
     780             :     // Now collect layers
     781         497 :     int nCandidateLayers = 0;
     782         497 :     int nLayersCDF = 0;
     783         497 :     bool bRet = true;
     784        9775 :     for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     785             :     {
     786        9278 :         if (cpl::contains(oSetBrokenRos, i))
     787        2137 :             continue;
     788             : 
     789        7141 :         if (!oTable.SelectRow(i))
     790             :         {
     791           0 :             if (oTable.HasGotError())
     792           0 :                 break;
     793           0 :             continue;
     794             :         }
     795             : 
     796        7141 :         const OGRField *psField = oTable.GetFieldValue(iDefinition);
     797        7141 :         if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
     798        5334 :             (strstr(psField->String, "DEFeatureClassInfo") != nullptr ||
     799        2464 :              strstr(psField->String, "DETableInfo") != nullptr))
     800             :         {
     801        6844 :             CPLString osDefinition(psField->String);
     802             : 
     803        3422 :             psField = oTable.GetFieldValue(iDocumentation);
     804             :             CPLString osDocumentation(psField != nullptr ? psField->String
     805        6844 :                                                          : "");
     806             : 
     807        3422 :             std::shared_ptr<OGROpenFileGDBGroup> poParent;
     808        3422 :             psField = oTable.GetFieldValue(iPath);
     809        3422 :             if (psField != nullptr)
     810             :             {
     811        3422 :                 const char *pszPath = psField->String;
     812        3422 :                 if (pszPath)
     813             :                 {
     814        6844 :                     std::string osPath(pszPath);
     815        3422 :                     const auto nPos = osPath.rfind('\\');
     816        3422 :                     if (nPos != 0 && nPos != std::string::npos)
     817             :                     {
     818          56 :                         std::string osPathParent = osPath.substr(0, nPos);
     819             :                         const auto oIter =
     820          28 :                             oMapPathToFeatureDataset.find(osPathParent);
     821          28 :                         if (oIter == oMapPathToFeatureDataset.end())
     822             :                         {
     823           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
     824             :                                      "Cannot find feature dataset of "
     825             :                                      "path %s referenced by table %s",
     826             :                                      osPathParent.c_str(), pszPath);
     827             :                         }
     828             :                         else
     829             :                         {
     830          28 :                             poParent = oIter->second;
     831             :                         }
     832             :                     }
     833             :                 }
     834             :             }
     835             : 
     836        3422 :             psField = oTable.GetFieldValue(iName);
     837        3422 :             if (psField != nullptr)
     838             :             {
     839        3422 :                 OGRLayer *poLayer = AddLayer(
     840        3422 :                     psField->String, nInterestTable, nCandidateLayers,
     841             :                     nLayersCDF, osDefinition, osDocumentation, wkbUnknown,
     842        6844 :                     poParent ? poParent->GetDefinition() : std::string());
     843             : 
     844        3422 :                 if (poLayer)
     845             :                 {
     846        3314 :                     if (poParent)
     847             :                     {
     848             :                         // Add the layer to the group of the corresponding
     849             :                         // feature dataset
     850          24 :                         poParent->m_apoLayers.emplace_back(poLayer);
     851             :                     }
     852             :                     else
     853             :                     {
     854        3290 :                         poRootGroup->m_apoLayers.emplace_back(poLayer);
     855             :                     }
     856             :                 }
     857        3422 :             }
     858             :         }
     859        3719 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
     860        1912 :                  (strstr(psField->String, "GPCodedValueDomain2") != nullptr ||
     861         834 :                   strstr(psField->String, "GPRangeDomain2") != nullptr))
     862             :         {
     863        3363 :             auto poDomain = ParseXMLFieldDomainDef(psField->String);
     864        1121 :             if (poDomain)
     865             :             {
     866        1121 :                 const auto &domainName = poDomain->GetName();
     867             :                 m_oMapFieldDomains.insert(
     868        1121 :                     std::pair(std::string(domainName), std::move(poDomain)));
     869        1121 :             }
     870             :         }
     871        2598 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
     872        1440 :                  strstr(psField->String, "DERasterDataset"))
     873             :         {
     874        2048 :             const std::string osDefinition(psField->String);
     875             : 
     876        1024 :             psField = oTable.GetFieldValue(iName);
     877        1024 :             if (psField)
     878             :             {
     879        2048 :                 std::string osLayerName(psField->String);
     880             : 
     881        1024 :                 psField = oTable.GetFieldValue(iDocumentation);
     882             :                 const std::string osDocumentation(psField ? psField->String
     883        2048 :                                                           : "");
     884             : 
     885        1024 :                 if (!osRasterLayerName.empty())
     886             :                 {
     887         930 :                     if (osRasterLayerName == osLayerName)
     888             :                     {
     889          27 :                         bRet = OpenRaster(poOpenInfo, osLayerName, osDefinition,
     890             :                                           osDocumentation);
     891             :                     }
     892             :                 }
     893             :                 else
     894             :                 {
     895          94 :                     const int iSubDSNum = 1 + m_aosSubdatasets.size() / 2;
     896             :                     m_aosSubdatasets.SetNameValue(
     897             :                         CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
     898             :                         CPLSPrintf("OpenFileGDB:\"%s\":%s",
     899          94 :                                    poOpenInfo->pszFilename,
     900          94 :                                    osLayerName.c_str()));
     901             : 
     902          94 :                     std::string osDesc(std::move(osLayerName));
     903          94 :                     if (!osDocumentation.empty())
     904             :                     {
     905             :                         CPLXMLTreeCloser psTree(
     906         186 :                             CPLParseXMLString(osDocumentation.c_str()));
     907          93 :                         if (psTree)
     908             :                         {
     909          93 :                             const auto psRastInfo = CPLGetXMLNode(
     910             :                                 psTree.get(), "=metadata.spdoinfo.rastinfo");
     911          93 :                             if (psRastInfo)
     912             :                             {
     913          93 :                                 const char *pszRowCount = CPLGetXMLValue(
     914             :                                     psRastInfo, "rowcount", nullptr);
     915          93 :                                 const char *pszColCount = CPLGetXMLValue(
     916             :                                     psRastInfo, "colcount", nullptr);
     917          93 :                                 const char *pszRastBand = CPLGetXMLValue(
     918             :                                     psRastInfo, "rastband", nullptr);
     919          93 :                                 const char *pszRastBPP = CPLGetXMLValue(
     920             :                                     psRastInfo, "rastbpp", nullptr);
     921          93 :                                 if (pszRowCount && pszColCount && pszRastBand &&
     922             :                                     pszRastBPP)
     923             :                                 {
     924             :                                     osDesc += CPLSPrintf(
     925             :                                         " [%sx%sx%s], %s bits", pszColCount,
     926          93 :                                         pszRowCount, pszRastBand, pszRastBPP);
     927             :                                 }
     928             :                             }
     929             :                         }
     930             :                     }
     931             : 
     932             :                     m_aosSubdatasets.SetNameValue(
     933             :                         CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
     934          94 :                         ("Raster " + osDesc).c_str());
     935             :                 }
     936        1024 :             }
     937             :         }
     938        1574 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     939         661 :                  strstr(psField->String, "DERasterDataset"))
     940             :         {
     941          50 :             psField = oTable.GetFieldValue(iName);
     942          50 :             if (psField)
     943             :             {
     944         100 :                 const std::string osLayerName(psField->String);
     945          50 :                 auto oIter = m_osMapNameToIdx.find(osLayerName);
     946          50 :                 if (oIter != m_osMapNameToIdx.end())
     947             :                 {
     948          47 :                     oSetIgnoredRasterLayerTableNum.insert(oIter->second);
     949             : 
     950         188 :                     for (const char *pszPrefix :
     951         235 :                          {"fras_ras_", "fras_aux_", "fras_bnd_", "fras_blk_"})
     952             :                     {
     953             :                         oIter = m_osMapNameToIdx.find(
     954         188 :                             std::string(pszPrefix).append(osLayerName).c_str());
     955         188 :                         if (oIter != m_osMapNameToIdx.end())
     956             :                         {
     957             :                             oSetIgnoredRasterLayerTableNum.insert(
     958         188 :                                 oIter->second);
     959             :                         }
     960             :                     }
     961             :                 }
     962             :             }
     963             :         }
     964             :     }
     965             : 
     966             :     // If there's at least one .cdf layer and the FileGDB driver is present,
     967             :     // retry with it.
     968         497 :     if (nLayersCDF > 0 && GDALGetDriverByName("FileGDB") != nullptr)
     969             :     {
     970           2 :         bRetryFileGDBOut = true;
     971           2 :         return false;
     972             :     }
     973             : 
     974         495 :     return bRet;
     975             : }
     976             : 
     977             : /************************************************************************/
     978             : /*                           OpenFileGDBv9()                            */
     979             : /************************************************************************/
     980             : 
     981          23 : int OGROpenFileGDBDataSource::OpenFileGDBv9(
     982             :     int iGDBFeatureClasses, int iGDBObjectClasses, int nInterestTable,
     983             :     const GDALOpenInfo *poOpenInfo, const std::string &osRasterLayerName,
     984             :     std::set<int> &oSetIgnoredRasterLayerTableNum)
     985             : {
     986          46 :     auto poTable = std::make_unique<FileGDBTable>();
     987             : 
     988          23 :     CPLDebug("OpenFileGDB", "FileGDB v9");
     989             : 
     990             :     /* Fetch names of layers */
     991          23 :     CPLString osFilename(CPLFormFilenameSafe(
     992          46 :         m_osDirName, CPLSPrintf("a%08x", iGDBObjectClasses + 1), "gdbtable"));
     993          23 :     if (!poTable->Open(osFilename, false))
     994           0 :         return FALSE;
     995             : 
     996          23 :     int iName = poTable->GetFieldIdx("Name");
     997          23 :     int iCLSID = poTable->GetFieldIdx("CLSID");
     998          23 :     if (iName < 0 || poTable->GetField(iName)->GetType() != FGFT_STRING ||
     999          46 :         iCLSID < 0 || poTable->GetField(iCLSID)->GetType() != FGFT_STRING)
    1000             :     {
    1001           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1002             :                  "Wrong structure for GDB_ObjectClasses table");
    1003           0 :         return FALSE;
    1004             :     }
    1005             : 
    1006          46 :     std::vector<std::string> aosName;
    1007          23 :     int nCandidateLayers = 0, nLayersCDF = 0;
    1008         433 :     for (int i = 0; i < poTable->GetTotalRecordCount(); i++)
    1009             :     {
    1010         410 :         if (!poTable->SelectRow(i))
    1011             :         {
    1012          27 :             if (poTable->HasGotError())
    1013           0 :                 break;
    1014          27 :             aosName.push_back("");
    1015          27 :             continue;
    1016             :         }
    1017             : 
    1018         383 :         const OGRField *psField = poTable->GetFieldValue(iName);
    1019         383 :         if (psField != nullptr)
    1020             :         {
    1021         766 :             std::string osName(psField->String);
    1022         383 :             psField = poTable->GetFieldValue(iCLSID);
    1023         383 :             if (psField != nullptr)
    1024             :             {
    1025             :                 /* Is it a non-spatial table ? */
    1026         383 :                 if (strcmp(psField->String,
    1027             :                            "{7A566981-C114-11D2-8A28-006097AFF44E}") == 0)
    1028             :                 {
    1029          51 :                     aosName.push_back("");
    1030          51 :                     AddLayer(osName, nInterestTable, nCandidateLayers,
    1031         102 :                              nLayersCDF, "", "", wkbNone, std::string());
    1032             :                 }
    1033             :                 else
    1034             :                 {
    1035             :                     /* We should perhaps also check that the CLSID is the one of
    1036             :                      * a spatial table */
    1037         332 :                     aosName.push_back(std::move(osName));
    1038             :                 }
    1039             :             }
    1040             :         }
    1041             :     }
    1042          23 :     poTable->Close();
    1043             : 
    1044          23 :     poTable = std::make_unique<FileGDBTable>();
    1045             : 
    1046             :     /* Find tables that are spatial layers */
    1047          23 :     osFilename = CPLFormFilenameSafe(
    1048          23 :         m_osDirName, CPLSPrintf("a%08x", iGDBFeatureClasses + 1), "gdbtable");
    1049          23 :     if (!poTable->Open(osFilename, false))
    1050           0 :         return FALSE;
    1051             : 
    1052          23 :     const int iObjectClassID = poTable->GetFieldIdx("ObjectClassID");
    1053          23 :     const int iFeatureType = poTable->GetFieldIdx("FeatureType");
    1054          23 :     const int iGeometryType = poTable->GetFieldIdx("GeometryType");
    1055          46 :     if (iObjectClassID < 0 || iGeometryType < 0 || iFeatureType < 0 ||
    1056          46 :         poTable->GetField(iObjectClassID)->GetType() != FGFT_INT32 ||
    1057          69 :         poTable->GetField(iFeatureType)->GetType() != FGFT_INT32 ||
    1058          23 :         poTable->GetField(iGeometryType)->GetType() != FGFT_INT32)
    1059             :     {
    1060           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1061             :                  "Wrong structure for GDB_FeatureClasses table");
    1062           0 :         return FALSE;
    1063             :     }
    1064             : 
    1065          23 :     bool bRet = true;
    1066         379 :     for (int i = 0; i < poTable->GetTotalRecordCount(); i++)
    1067             :     {
    1068         356 :         if (!poTable->SelectRow(i))
    1069             :         {
    1070          24 :             if (poTable->HasGotError())
    1071           0 :                 break;
    1072          24 :             continue;
    1073             :         }
    1074             : 
    1075         332 :         const OGRField *psField = poTable->GetFieldValue(iGeometryType);
    1076         332 :         if (psField == nullptr)
    1077           0 :             continue;
    1078         332 :         const int nGeomType = psField->Integer;
    1079         332 :         OGRwkbGeometryType eGeomType = wkbUnknown;
    1080         332 :         switch (nGeomType)
    1081             :         {
    1082           0 :             case FGTGT_NONE: /* doesn't make sense ! */
    1083           0 :                 break;
    1084          64 :             case FGTGT_POINT:
    1085          64 :                 eGeomType = wkbPoint;
    1086          64 :                 break;
    1087          52 :             case FGTGT_MULTIPOINT:
    1088          52 :                 eGeomType = wkbMultiPoint;
    1089          52 :                 break;
    1090          76 :             case FGTGT_LINE:
    1091          76 :                 eGeomType = wkbMultiLineString;
    1092          76 :                 break;
    1093         124 :             case FGTGT_POLYGON:
    1094         124 :                 eGeomType = wkbMultiPolygon;
    1095         124 :                 break;
    1096          16 :             case FGTGT_MULTIPATCH:
    1097          16 :                 eGeomType = wkbUnknown;
    1098          16 :                 break;
    1099             :         }
    1100             : 
    1101         332 :         psField = poTable->GetFieldValue(iObjectClassID);
    1102         332 :         if (psField == nullptr)
    1103           0 :             continue;
    1104             : 
    1105         332 :         const int idx = psField->Integer;
    1106         664 :         if (idx > 0 && idx <= static_cast<int>(aosName.size()) &&
    1107         332 :             !aosName[idx - 1].empty())
    1108             :         {
    1109         664 :             const std::string osName(aosName[idx - 1]);
    1110             : 
    1111         332 :             psField = poTable->GetFieldValue(iFeatureType);
    1112         332 :             const bool bIsRaster = psField && psField->Integer == 14;
    1113             : 
    1114         332 :             if (bIsRaster && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
    1115             :             {
    1116           3 :                 if (!osRasterLayerName.empty())
    1117             :                 {
    1118           2 :                     if (osRasterLayerName == osName)
    1119             :                     {
    1120           2 :                         bRet = OpenRaster(poOpenInfo, osName, "", "");
    1121             :                     }
    1122             :                 }
    1123             :                 else
    1124             :                 {
    1125           1 :                     const int iSubDSNum = 1 + m_aosSubdatasets.size() / 2;
    1126             :                     m_aosSubdatasets.SetNameValue(
    1127             :                         CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
    1128             :                         CPLSPrintf("OpenFileGDB:\"%s\":%s",
    1129           1 :                                    poOpenInfo->pszFilename, osName.c_str()));
    1130             :                     m_aosSubdatasets.SetNameValue(
    1131             :                         CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
    1132           1 :                         ("Raster " + osName).c_str());
    1133           3 :                 }
    1134             :             }
    1135         329 :             else if (bIsRaster)
    1136             :             {
    1137           1 :                 auto oIter = m_osMapNameToIdx.find(osName);
    1138           1 :                 if (oIter != m_osMapNameToIdx.end())
    1139             :                 {
    1140           1 :                     oSetIgnoredRasterLayerTableNum.insert(oIter->second);
    1141             : 
    1142           4 :                     for (const char *pszPrefix :
    1143           5 :                          {"fras_ras_", "fras_aux_", "fras_bnd_", "fras_blk_"})
    1144             :                     {
    1145             :                         oIter = m_osMapNameToIdx.find(
    1146           4 :                             std::string(pszPrefix).append(osName).c_str());
    1147           4 :                         if (oIter != m_osMapNameToIdx.end())
    1148             :                         {
    1149             :                             oSetIgnoredRasterLayerTableNum.insert(
    1150           4 :                                 oIter->second);
    1151             :                         }
    1152             :                     }
    1153             :                 }
    1154             :             }
    1155             :             else
    1156             :             {
    1157         328 :                 AddLayer(osName, nInterestTable, nCandidateLayers, nLayersCDF,
    1158         656 :                          "", "", eGeomType, std::string());
    1159             :             }
    1160             :         }
    1161             :     }
    1162             : 
    1163          23 :     if (m_apoLayers.empty() && nCandidateLayers > 0 &&
    1164           0 :         nCandidateLayers == nLayersCDF)
    1165           0 :         return FALSE;
    1166             : 
    1167          23 :     return bRet;
    1168             : }
    1169             : 
    1170             : /************************************************************************/
    1171             : /*                           TestCapability()                           */
    1172             : /************************************************************************/
    1173             : 
    1174         727 : int OGROpenFileGDBDataSource::TestCapability(const char *pszCap) const
    1175             : {
    1176         727 :     if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
    1177         633 :         EQUAL(pszCap, ODsCAddFieldDomain) ||
    1178         629 :         EQUAL(pszCap, ODsCDeleteFieldDomain) ||
    1179         628 :         EQUAL(pszCap, ODsCUpdateFieldDomain) ||
    1180         627 :         EQUAL(pszCap, GDsCAddRelationship) ||
    1181         627 :         EQUAL(pszCap, GDsCDeleteRelationship) ||
    1182         627 :         EQUAL(pszCap, GDsCUpdateRelationship) ||
    1183         627 :         EQUAL(pszCap, ODsCEmulatedTransactions))
    1184             :     {
    1185         103 :         return eAccess == GA_Update;
    1186             :     }
    1187             : 
    1188         624 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
    1189         183 :         return TRUE;
    1190         441 :     else if (EQUAL(pszCap, ODsCZGeometries))
    1191         183 :         return TRUE;
    1192         258 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
    1193         200 :         return TRUE;
    1194             : 
    1195          58 :     return FALSE;
    1196             : }
    1197             : 
    1198             : /************************************************************************/
    1199             : /*                              GetLayer()                              */
    1200             : /************************************************************************/
    1201             : 
    1202     1195470 : const OGRLayer *OGROpenFileGDBDataSource::GetLayer(int iIndex) const
    1203             : {
    1204     1195470 :     if (iIndex < 0 || iIndex >= static_cast<int>(m_apoLayers.size()))
    1205          18 :         return nullptr;
    1206     1195450 :     return m_apoLayers[iIndex].get();
    1207             : }
    1208             : 
    1209             : /************************************************************************/
    1210             : /*                         BuildLayerFromName()                         */
    1211             : /************************************************************************/
    1212             : 
    1213             : std::unique_ptr<OGROpenFileGDBLayer>
    1214         903 : OGROpenFileGDBDataSource::BuildLayerFromName(const char *pszName)
    1215             : {
    1216             : 
    1217         903 :     const auto oIter = m_osMapNameToIdx.find(pszName);
    1218         903 :     if (oIter != m_osMapNameToIdx.end())
    1219             :     {
    1220         165 :         const int idx = oIter->second;
    1221         165 :         const CPLString osFilename(CPLFormFilenameSafe(
    1222         165 :             m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"));
    1223         165 :         if (FileExists(osFilename))
    1224             :         {
    1225             :             return std::make_unique<OGROpenFileGDBLayer>(
    1226         164 :                 this, osFilename, pszName, "", "", eAccess == GA_Update);
    1227             :         }
    1228             :     }
    1229         739 :     return nullptr;
    1230             : }
    1231             : 
    1232             : /************************************************************************/
    1233             : /*                           GetLayerByName()                           */
    1234             : /************************************************************************/
    1235             : 
    1236             : OGROpenFileGDBLayer *
    1237        3435 : OGROpenFileGDBDataSource::GetLayerByName(const char *pszName)
    1238             : {
    1239       35050 :     for (auto &poLayer : m_apoLayers)
    1240             :     {
    1241       34207 :         if (EQUAL(poLayer->GetName(), pszName))
    1242        2592 :             return poLayer.get();
    1243             :     }
    1244             : 
    1245        5583 :     for (auto &poLayer : m_apoHiddenLayers)
    1246             :     {
    1247        4741 :         if (EQUAL(poLayer->GetName(), pszName))
    1248           1 :             return poLayer.get();
    1249             :     }
    1250             : 
    1251        1684 :     auto poLayer = BuildLayerFromName(pszName);
    1252         842 :     if (poLayer)
    1253             :     {
    1254         104 :         m_apoHiddenLayers.emplace_back(std::move(poLayer));
    1255         104 :         return m_apoHiddenLayers.back().get();
    1256             :     }
    1257         738 :     return nullptr;
    1258             : }
    1259             : 
    1260             : /************************************************************************/
    1261             : /*                         IsPrivateLayerName()                         */
    1262             : /************************************************************************/
    1263             : 
    1264       10550 : bool OGROpenFileGDBDataSource::IsPrivateLayerName(const CPLString &osName)
    1265             : {
    1266       10550 :     const CPLString osLCTableName(CPLString(osName).tolower());
    1267             : 
    1268             :     // tables beginning with "GDB_" are private tables
    1269             :     // Also consider "VAT_" tables as private ones.
    1270       27034 :     return osLCTableName.size() >= 4 && (osLCTableName.substr(0, 4) == "gdb_" ||
    1271       27034 :                                          osLCTableName.substr(0, 4) == "vat_");
    1272             : }
    1273             : 
    1274             : /************************************************************************/
    1275             : /*                           IsLayerPrivate()                           */
    1276             : /************************************************************************/
    1277             : 
    1278         103 : bool OGROpenFileGDBDataSource::IsLayerPrivate(int iLayer) const
    1279             : {
    1280         103 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
    1281           0 :         return false;
    1282             : 
    1283         103 :     const std::string osName(m_apoLayers[iLayer]->GetName());
    1284         103 :     return IsPrivateLayerName(osName);
    1285             : }
    1286             : 
    1287             : /************************************************************************/
    1288             : /*                            GetMetadata()                             */
    1289             : /************************************************************************/
    1290             : 
    1291          26 : CSLConstList OGROpenFileGDBDataSource::GetMetadata(const char *pszDomain)
    1292             : {
    1293          26 :     if (pszDomain && EQUAL(pszDomain, "SUBDATASETS"))
    1294           6 :         return m_aosSubdatasets.List();
    1295          20 :     return GDALDataset::GetMetadata(pszDomain);
    1296             : }
    1297             : 
    1298             : /************************************************************************/
    1299             : /*                  OGROpenFileGDBSingleFeatureLayer()                  */
    1300             : /************************************************************************/
    1301             : 
    1302         239 : OGROpenFileGDBSingleFeatureLayer::OGROpenFileGDBSingleFeatureLayer(
    1303         239 :     const char *pszLayerName, const char *pszValIn)
    1304         239 :     : pszVal(pszValIn ? CPLStrdup(pszValIn) : nullptr),
    1305         478 :       poFeatureDefn(new OGRFeatureDefn(pszLayerName)), iNextShapeId(0)
    1306             : {
    1307         239 :     SetDescription(poFeatureDefn->GetName());
    1308         239 :     poFeatureDefn->Reference();
    1309         478 :     OGRFieldDefn oField("FIELD_1", OFTString);
    1310         239 :     poFeatureDefn->AddFieldDefn(&oField);
    1311         239 : }
    1312             : 
    1313             : /************************************************************************/
    1314             : /*                 ~OGROpenFileGDBSingleFeatureLayer()                  */
    1315             : /************************************************************************/
    1316             : 
    1317         478 : OGROpenFileGDBSingleFeatureLayer::~OGROpenFileGDBSingleFeatureLayer()
    1318             : {
    1319         239 :     if (poFeatureDefn != nullptr)
    1320         239 :         poFeatureDefn->Release();
    1321         239 :     CPLFree(pszVal);
    1322         478 : }
    1323             : 
    1324             : /************************************************************************/
    1325             : /*                           GetNextFeature()                           */
    1326             : /************************************************************************/
    1327             : 
    1328         258 : OGRFeature *OGROpenFileGDBSingleFeatureLayer::GetNextFeature()
    1329             : {
    1330         258 :     if (iNextShapeId != 0)
    1331          19 :         return nullptr;
    1332             : 
    1333         239 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    1334         239 :     if (pszVal)
    1335         239 :         poFeature->SetField(0, pszVal);
    1336         239 :     poFeature->SetFID(iNextShapeId++);
    1337         239 :     return poFeature;
    1338             : }
    1339             : 
    1340             : /************************************************************************/
    1341             : /*                     OGROpenFileGDBSimpleSQLLayer                     */
    1342             : /************************************************************************/
    1343             : 
    1344             : class OGROpenFileGDBSimpleSQLLayer final : public OGRLayer
    1345             : {
    1346             :     OGRLayer *poBaseLayer;
    1347             :     FileGDBIterator *poIter;
    1348             :     OGRFeatureDefn *poFeatureDefn;
    1349             :     GIntBig m_nOffset;
    1350             :     GIntBig m_nLimit;
    1351             :     GIntBig m_nSkipped = 0;
    1352             :     GIntBig m_nIterated = 0;
    1353             : 
    1354             :     CPL_DISALLOW_COPY_ASSIGN(OGROpenFileGDBSimpleSQLLayer)
    1355             : 
    1356             :   public:
    1357             :     OGROpenFileGDBSimpleSQLLayer(OGRLayer *poBaseLayer, FileGDBIterator *poIter,
    1358             :                                  int nColumns, const swq_col_def *pasColDefs,
    1359             :                                  GIntBig nOffset, GIntBig nLimit);
    1360             :     ~OGROpenFileGDBSimpleSQLLayer() override;
    1361             : 
    1362             :     void ResetReading() override;
    1363             :     OGRFeature *GetNextFeature() override;
    1364             :     OGRFeature *GetFeature(GIntBig nFeatureId) override;
    1365             : 
    1366         161 :     const OGRFeatureDefn *GetLayerDefn() const override
    1367             :     {
    1368         161 :         return poFeatureDefn;
    1369             :     }
    1370             : 
    1371             :     int TestCapability(const char *) const override;
    1372             : 
    1373          60 :     const char *GetFIDColumn() const override
    1374             :     {
    1375          60 :         return poBaseLayer->GetFIDColumn();
    1376             :     }
    1377             : 
    1378          12 :     OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
    1379             :                       bool bForce) override
    1380             :     {
    1381          12 :         return poBaseLayer->GetExtent(iGeomField, psExtent, bForce);
    1382             :     }
    1383             : 
    1384           0 :     OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
    1385             :                         bool bForce) override
    1386             :     {
    1387           0 :         return poBaseLayer->GetExtent3D(iGeomField, psExtent, bForce);
    1388             :     }
    1389             : 
    1390             :     GIntBig GetFeatureCount(int bForce) override;
    1391             : };
    1392             : 
    1393             : /************************************************************************/
    1394             : /*                    OGROpenFileGDBSimpleSQLLayer()                    */
    1395             : /************************************************************************/
    1396             : 
    1397          16 : OGROpenFileGDBSimpleSQLLayer::OGROpenFileGDBSimpleSQLLayer(
    1398             :     OGRLayer *poBaseLayerIn, FileGDBIterator *poIterIn, int nColumns,
    1399          16 :     const swq_col_def *pasColDefs, GIntBig nOffset, GIntBig nLimit)
    1400             :     : poBaseLayer(poBaseLayerIn), poIter(poIterIn), poFeatureDefn(nullptr),
    1401          16 :       m_nOffset(nOffset), m_nLimit(nLimit)
    1402             : {
    1403          16 :     if (nColumns == 1 && strcmp(pasColDefs[0].field_name, "*") == 0)
    1404             :     {
    1405          14 :         poFeatureDefn = poBaseLayer->GetLayerDefn();
    1406          14 :         poFeatureDefn->Reference();
    1407             :     }
    1408             :     else
    1409             :     {
    1410           2 :         poFeatureDefn = new OGRFeatureDefn(poBaseLayer->GetName());
    1411           2 :         poFeatureDefn->SetGeomType(poBaseLayer->GetGeomType());
    1412           2 :         poFeatureDefn->Reference();
    1413           2 :         if (poBaseLayer->GetGeomType() != wkbNone)
    1414             :         {
    1415           4 :             poFeatureDefn->GetGeomFieldDefn(0)->SetName(
    1416           2 :                 poBaseLayer->GetGeometryColumn());
    1417           4 :             poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
    1418           2 :                 poBaseLayer->GetSpatialRef());
    1419             :         }
    1420           6 :         for (int i = 0; i < nColumns; i++)
    1421             :         {
    1422           4 :             if (strcmp(pasColDefs[i].field_name, "*") == 0)
    1423             :             {
    1424           0 :                 for (int j = 0;
    1425           0 :                      j < poBaseLayer->GetLayerDefn()->GetFieldCount(); j++)
    1426           0 :                     poFeatureDefn->AddFieldDefn(
    1427           0 :                         poBaseLayer->GetLayerDefn()->GetFieldDefn(j));
    1428             :             }
    1429             :             else
    1430             :             {
    1431             :                 OGRFieldDefn *poFieldDefn =
    1432           8 :                     poBaseLayer->GetLayerDefn()->GetFieldDefn(
    1433           8 :                         poBaseLayer->GetLayerDefn()->GetFieldIndex(
    1434           4 :                             pasColDefs[i].field_name));
    1435           4 :                 CPLAssert(poFieldDefn != nullptr); /* already checked before */
    1436           4 :                 poFeatureDefn->AddFieldDefn(poFieldDefn);
    1437             :             }
    1438             :         }
    1439             :     }
    1440          16 :     SetDescription(poFeatureDefn->GetName());
    1441          16 :     OGROpenFileGDBSimpleSQLLayer::ResetReading();
    1442          16 : }
    1443             : 
    1444             : /************************************************************************/
    1445             : /*                   ~OGROpenFileGDBSimpleSQLLayer()                    */
    1446             : /************************************************************************/
    1447             : 
    1448          32 : OGROpenFileGDBSimpleSQLLayer::~OGROpenFileGDBSimpleSQLLayer()
    1449             : {
    1450          16 :     if (poFeatureDefn)
    1451             :     {
    1452          16 :         poFeatureDefn->Release();
    1453             :     }
    1454          16 :     delete poIter;
    1455          32 : }
    1456             : 
    1457             : /************************************************************************/
    1458             : /*                            ResetReading()                            */
    1459             : /************************************************************************/
    1460             : 
    1461         210 : void OGROpenFileGDBSimpleSQLLayer::ResetReading()
    1462             : {
    1463         210 :     poIter->Reset();
    1464         210 :     m_nSkipped = 0;
    1465         210 :     m_nIterated = 0;
    1466         210 : }
    1467             : 
    1468             : /************************************************************************/
    1469             : /*                             GetFeature()                             */
    1470             : /************************************************************************/
    1471             : 
    1472         696 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetFeature(GIntBig nFeatureId)
    1473             : {
    1474         696 :     OGRFeature *poSrcFeature = poBaseLayer->GetFeature(nFeatureId);
    1475         696 :     if (poSrcFeature == nullptr)
    1476           9 :         return nullptr;
    1477             : 
    1478         687 :     if (poFeatureDefn == poBaseLayer->GetLayerDefn())
    1479         536 :         return poSrcFeature;
    1480             :     else
    1481             :     {
    1482         151 :         OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    1483         151 :         poFeature->SetFrom(poSrcFeature);
    1484         151 :         poFeature->SetFID(poSrcFeature->GetFID());
    1485         151 :         delete poSrcFeature;
    1486         151 :         return poFeature;
    1487             :     }
    1488             : }
    1489             : 
    1490             : /************************************************************************/
    1491             : /*                           GetNextFeature()                           */
    1492             : /************************************************************************/
    1493             : 
    1494         758 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetNextFeature()
    1495             : {
    1496             :     while (true)
    1497             :     {
    1498         758 :         if (m_nLimit >= 0 && m_nIterated == m_nLimit)
    1499           1 :             return nullptr;
    1500             : 
    1501         757 :         const int64_t nRow = poIter->GetNextRowSortedByValue();
    1502         757 :         if (nRow < 0)
    1503          82 :             return nullptr;
    1504         675 :         OGRFeature *poFeature = GetFeature(nRow + 1);
    1505         675 :         if (poFeature == nullptr)
    1506           0 :             return nullptr;
    1507         675 :         if (m_nOffset >= 0 && m_nSkipped < m_nOffset)
    1508             :         {
    1509         343 :             delete poFeature;
    1510         343 :             m_nSkipped++;
    1511         343 :             continue;
    1512             :         }
    1513         332 :         m_nIterated++;
    1514             : 
    1515         774 :         if ((m_poFilterGeom == nullptr ||
    1516         631 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    1517         299 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1518             :         {
    1519         249 :             return poFeature;
    1520             :         }
    1521             : 
    1522          83 :         delete poFeature;
    1523         426 :     }
    1524             : }
    1525             : 
    1526             : /************************************************************************/
    1527             : /*                          GetFeatureCount()                           */
    1528             : /************************************************************************/
    1529             : 
    1530          51 : GIntBig OGROpenFileGDBSimpleSQLLayer::GetFeatureCount(int bForce)
    1531             : {
    1532             : 
    1533             :     /* No filter */
    1534          51 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1535             :     {
    1536          33 :         GIntBig nRowCount = poIter->GetRowCount();
    1537          33 :         if (m_nOffset > 0)
    1538             :         {
    1539           3 :             if (m_nOffset <= nRowCount)
    1540           2 :                 nRowCount -= m_nOffset;
    1541             :             else
    1542           1 :                 nRowCount = 0;
    1543             :         }
    1544          33 :         if (m_nLimit >= 0 && nRowCount > m_nLimit)
    1545           5 :             nRowCount = m_nLimit;
    1546          33 :         return nRowCount;
    1547             :     }
    1548             : 
    1549          18 :     return OGRLayer::GetFeatureCount(bForce);
    1550             : }
    1551             : 
    1552             : /************************************************************************/
    1553             : /*                           TestCapability()                           */
    1554             : /************************************************************************/
    1555             : 
    1556          99 : int OGROpenFileGDBSimpleSQLLayer::TestCapability(const char *pszCap) const
    1557             : {
    1558             : 
    1559          99 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1560             :     {
    1561           0 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
    1562             :     }
    1563          99 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1564             :     {
    1565           6 :         return TRUE;
    1566             :     }
    1567          93 :     else if (EQUAL(pszCap, OLCRandomRead))
    1568             :     {
    1569           0 :         return TRUE;
    1570             :     }
    1571          93 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1572             :     {
    1573          27 :         return TRUE; /* ? */
    1574             :     }
    1575             : 
    1576          66 :     return FALSE;
    1577             : }
    1578             : 
    1579             : /************************************************************************/
    1580             : /*                             ExecuteSQL()                             */
    1581             : /************************************************************************/
    1582             : 
    1583         799 : OGRLayer *OGROpenFileGDBDataSource::ExecuteSQL(const char *pszSQLCommand,
    1584             :                                                OGRGeometry *poSpatialFilter,
    1585             :                                                const char *pszDialect)
    1586             : {
    1587             : 
    1588             :     /* -------------------------------------------------------------------- */
    1589             :     /*      Special case GetLayerDefinition                                 */
    1590             :     /* -------------------------------------------------------------------- */
    1591         799 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
    1592             :     {
    1593          32 :         const char *pszLayerName =
    1594             :             pszSQLCommand + strlen("GetLayerDefinition ");
    1595          32 :         auto poLayer = GetLayerByName(pszLayerName);
    1596          32 :         if (poLayer)
    1597             :         {
    1598             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1599          31 :                 "LayerDefinition", poLayer->GetXMLDefinition().c_str());
    1600          31 :             return poRet;
    1601             :         }
    1602           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1603             :                  pszLayerName);
    1604           1 :         return nullptr;
    1605             :     }
    1606             : 
    1607             :     /* -------------------------------------------------------------------- */
    1608             :     /*      Special case GetLayerMetadata                                   */
    1609             :     /* -------------------------------------------------------------------- */
    1610         767 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
    1611             :     {
    1612          21 :         const char *pszLayerName = pszSQLCommand + strlen("GetLayerMetadata ");
    1613          21 :         auto poLayer = GetLayerByName(pszLayerName);
    1614          21 :         if (poLayer)
    1615             :         {
    1616             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1617          20 :                 "LayerMetadata", poLayer->GetXMLDocumentation().c_str());
    1618          20 :             return poRet;
    1619             :         }
    1620           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1621             :                  pszLayerName);
    1622           1 :         return nullptr;
    1623             :     }
    1624             : 
    1625             :     /* -------------------------------------------------------------------- */
    1626             :     /*      Special case GetLayerAttrIndexUse (only for debugging purposes) */
    1627             :     /* -------------------------------------------------------------------- */
    1628         746 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerAttrIndexUse "))
    1629             :     {
    1630             :         auto poLayer =
    1631         141 :             GetLayerByName(pszSQLCommand + strlen("GetLayerAttrIndexUse "));
    1632         141 :         if (poLayer)
    1633             :         {
    1634             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1635             :                 "LayerAttrIndexUse",
    1636         141 :                 CPLSPrintf("%d", poLayer->GetAttrIndexUse()));
    1637         141 :             return poRet;
    1638             :         }
    1639             : 
    1640           0 :         return nullptr;
    1641             :     }
    1642             : 
    1643             :     /* -------------------------------------------------------------------- */
    1644             :     /*      Special case GetLayerSpatialIndexState (only for debugging purposes)
    1645             :      */
    1646             :     /* -------------------------------------------------------------------- */
    1647         605 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerSpatialIndexState "))
    1648             :     {
    1649          12 :         auto poLayer = GetLayerByName(pszSQLCommand +
    1650             :                                       strlen("GetLayerSpatialIndexState "));
    1651          12 :         if (poLayer)
    1652             :         {
    1653             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1654             :                 "LayerSpatialIndexState",
    1655          12 :                 CPLSPrintf("%d", poLayer->GetSpatialIndexState()));
    1656          12 :             return poRet;
    1657             :         }
    1658             : 
    1659           0 :         return nullptr;
    1660             :     }
    1661             : 
    1662             :     /* -------------------------------------------------------------------- */
    1663             :     /*      Special case GetLastSQLUsedOptimizedImplementation (only for
    1664             :      * debugging purposes) */
    1665             :     /* -------------------------------------------------------------------- */
    1666         593 :     if (EQUAL(pszSQLCommand, "GetLastSQLUsedOptimizedImplementation"))
    1667             :     {
    1668             :         OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1669             :             "GetLastSQLUsedOptimizedImplementation",
    1670          46 :             CPLSPrintf("%d",
    1671          23 :                        static_cast<int>(bLastSQLUsedOptimizedImplementation)));
    1672          23 :         return poRet;
    1673             :     }
    1674             : 
    1675             :     /* -------------------------------------------------------------------- */
    1676             :     /*      Special case for "CREATE SPATIAL INDEX ON "                     */
    1677             :     /* -------------------------------------------------------------------- */
    1678         570 :     if (STARTS_WITH_CI(pszSQLCommand, "CREATE SPATIAL INDEX ON "))
    1679             :     {
    1680           0 :         const char *pszLayerName =
    1681             :             pszSQLCommand + strlen("CREATE SPATIAL INDEX ON ");
    1682           0 :         auto poLayer = GetLayerByName(pszLayerName);
    1683           0 :         if (poLayer)
    1684             :         {
    1685           0 :             poLayer->CreateSpatialIndex();
    1686             :         }
    1687           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1688             :                  pszLayerName);
    1689           0 :         return nullptr;
    1690             :     }
    1691             : 
    1692             :     /* -------------------------------------------------------------------- */
    1693             :     /*      Special case for "CREATE INDEX idx_name ON table_name(col_name) */
    1694             :     /* -------------------------------------------------------------------- */
    1695         570 :     if (STARTS_WITH_CI(pszSQLCommand, "CREATE INDEX "))
    1696             :     {
    1697          42 :         CPLString osSQL(pszSQLCommand);
    1698          21 :         const auto nPosON = osSQL.ifind(" ON ");
    1699          21 :         if (nPosON != std::string::npos)
    1700             :         {
    1701             :             const std::string osIdxName = osSQL.substr(
    1702          21 :                 strlen("CREATE INDEX "), nPosON - strlen("CREATE INDEX "));
    1703          21 :             const std::string afterOn = osSQL.substr(nPosON + strlen(" ON "));
    1704          21 :             const auto nOpenParPos = afterOn.find('(');
    1705          21 :             if (nOpenParPos != std::string::npos && afterOn.back() == ')')
    1706             :             {
    1707          42 :                 const std::string osLayerName = afterOn.substr(0, nOpenParPos);
    1708             :                 const std::string osExpression = afterOn.substr(
    1709          42 :                     nOpenParPos + 1, afterOn.size() - nOpenParPos - 2);
    1710          21 :                 if (osExpression.find(',') != std::string::npos)
    1711             :                 {
    1712           1 :                     CPLError(
    1713             :                         CE_Failure, CPLE_NotSupported,
    1714             :                         "Creation of multiple-column indices is not supported");
    1715           1 :                     return nullptr;
    1716             :                 }
    1717          20 :                 auto poLayer = GetLayerByName(osLayerName.c_str());
    1718          20 :                 if (poLayer)
    1719             :                 {
    1720          19 :                     poLayer->CreateIndex(osIdxName, osExpression);
    1721          19 :                     return nullptr;
    1722             :                 }
    1723           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1724             :                          osLayerName.c_str());
    1725           1 :                 return nullptr;
    1726             :             }
    1727             :         }
    1728           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1729             :                  "Bad syntax. Expected CREATE INDEX idx_name ON "
    1730             :                  "table_name(col_name)");
    1731           0 :         return nullptr;
    1732             :     }
    1733             : 
    1734             :     /* -------------------------------------------------------------------- */
    1735             :     /*      Special case for "RECOMPUTE EXTENT ON "                         */
    1736             :     /* -------------------------------------------------------------------- */
    1737         549 :     if (STARTS_WITH_CI(pszSQLCommand, "RECOMPUTE EXTENT ON "))
    1738             :     {
    1739           3 :         const char *pszLayerName =
    1740             :             pszSQLCommand + strlen("RECOMPUTE EXTENT ON ");
    1741           3 :         auto poLayer = GetLayerByName(pszLayerName);
    1742           3 :         if (poLayer)
    1743             :         {
    1744           2 :             poLayer->RecomputeExtent();
    1745             :         }
    1746             :         else
    1747             :         {
    1748           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1749             :                      pszLayerName);
    1750             :         }
    1751           3 :         return nullptr;
    1752             :     }
    1753             : 
    1754             :     /* -------------------------------------------------------------------- */
    1755             :     /*      Special case for "DELLAYER:"                                    */
    1756             :     /* -------------------------------------------------------------------- */
    1757         546 :     if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
    1758             :     {
    1759           3 :         const char *pszLayerName = pszSQLCommand + strlen("DELLAYER:");
    1760           5 :         for (int i = 0; i < static_cast<int>(m_apoLayers.size()); ++i)
    1761             :         {
    1762           3 :             if (strcmp(pszLayerName, m_apoLayers[i]->GetName()) == 0)
    1763             :             {
    1764           1 :                 DeleteLayer(i);
    1765           1 :                 return nullptr;
    1766             :             }
    1767             :         }
    1768           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1769             :                  pszLayerName);
    1770           2 :         return nullptr;
    1771             :     }
    1772             : 
    1773             :     /* -------------------------------------------------------------------- */
    1774             :     /*      Special case for "CHECK_FREELIST_CONSISTENCY:"                  */
    1775             :     /* -------------------------------------------------------------------- */
    1776         543 :     if (STARTS_WITH_CI(pszSQLCommand, "CHECK_FREELIST_CONSISTENCY:"))
    1777             :     {
    1778           8 :         const char *pszLayerName =
    1779             :             pszSQLCommand + strlen("CHECK_FREELIST_CONSISTENCY:");
    1780           8 :         auto poLayer = GetLayerByName(pszLayerName);
    1781           8 :         if (poLayer)
    1782             :         {
    1783             :             return new OGROpenFileGDBSingleFeatureLayer(
    1784             :                 "result",
    1785          16 :                 CPLSPrintf("%d", static_cast<int>(
    1786           8 :                                      poLayer->CheckFreeListConsistency())));
    1787             :         }
    1788           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1789             :                  pszLayerName);
    1790           0 :         return nullptr;
    1791             :     }
    1792             : 
    1793             :     /* -------------------------------------------------------------------- */
    1794             :     /*      Special case for "REPACK"                                       */
    1795             :     /* -------------------------------------------------------------------- */
    1796         535 :     if (EQUAL(pszSQLCommand, "REPACK"))
    1797             :     {
    1798           3 :         bool bSuccess = true;
    1799           6 :         for (auto &poLayer : m_apoLayers)
    1800             :         {
    1801           3 :             if (!poLayer->Repack(nullptr, nullptr))
    1802           0 :                 bSuccess = false;
    1803             :         }
    1804             :         return new OGROpenFileGDBSingleFeatureLayer(
    1805           3 :             "result", bSuccess ? "true" : "false");
    1806             :     }
    1807         532 :     else if (STARTS_WITH(pszSQLCommand, "REPACK "))
    1808             :     {
    1809           2 :         const char *pszLayerName = pszSQLCommand + strlen("REPACK ");
    1810           2 :         auto poLayer = GetLayerByName(pszLayerName);
    1811           2 :         if (poLayer)
    1812             :         {
    1813           1 :             const bool bSuccess = poLayer->Repack(nullptr, nullptr);
    1814             :             return new OGROpenFileGDBSingleFeatureLayer(
    1815           1 :                 "result", bSuccess ? "true" : "false");
    1816             :         }
    1817           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1818             :                  pszLayerName);
    1819           1 :         return nullptr;
    1820             :     }
    1821             : 
    1822         530 :     bLastSQLUsedOptimizedImplementation = false;
    1823             : 
    1824             :     /* -------------------------------------------------------------------- */
    1825             :     /*      Special cases for SQL optimizations                             */
    1826             :     /* -------------------------------------------------------------------- */
    1827         521 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT ") &&
    1828          39 :         (pszDialect == nullptr || EQUAL(pszDialect, "") ||
    1829        1051 :          EQUAL(pszDialect, "OGRSQL")) &&
    1830         521 :         CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
    1831             :     {
    1832         521 :         swq_select oSelect;
    1833         521 :         if (oSelect.preparse(pszSQLCommand) != CE_None)
    1834           1 :             return nullptr;
    1835             : 
    1836             :         /* --------------------------------------------------------------------
    1837             :          */
    1838             :         /*      MIN/MAX/SUM/AVG/COUNT optimization */
    1839             :         /* --------------------------------------------------------------------
    1840             :          */
    1841         520 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1842         520 :             oSelect.table_count == 1 && oSelect.order_specs == 0 &&
    1843         493 :             oSelect.query_mode != SWQM_DISTINCT_LIST &&
    1844         493 :             oSelect.where_expr == nullptr)
    1845             :         {
    1846             :             OGROpenFileGDBLayer *poLayer =
    1847         624 :                 reinterpret_cast<OGROpenFileGDBLayer *>(
    1848         312 :                     GetLayerByName(oSelect.table_defs[0].table_name));
    1849         312 :             if (poLayer)
    1850             :             {
    1851         311 :                 OGRMemLayer *poMemLayer = nullptr;
    1852             : 
    1853         311 :                 int i = 0;  // Used after for.
    1854         369 :                 for (; i < oSelect.result_columns(); i++)
    1855             :                 {
    1856         333 :                     swq_col_func col_func = oSelect.column_defs[i].col_func;
    1857         514 :                     if (!(col_func == SWQCF_MIN || col_func == SWQCF_MAX ||
    1858         187 :                           col_func == SWQCF_COUNT || col_func == SWQCF_AVG ||
    1859             :                           col_func == SWQCF_SUM))
    1860         275 :                         break;
    1861             : 
    1862         153 :                     if (oSelect.column_defs[i].field_name == nullptr)
    1863           0 :                         break;
    1864         153 :                     if (oSelect.column_defs[i].distinct_flag)
    1865           0 :                         break;
    1866         153 :                     if (oSelect.column_defs[i].target_type != SWQ_OTHER)
    1867           0 :                         break;
    1868             : 
    1869         153 :                     int idx = poLayer->GetLayerDefn()->GetFieldIndex(
    1870         153 :                         oSelect.column_defs[i].field_name);
    1871         153 :                     if (idx < 0)
    1872          90 :                         break;
    1873             : 
    1874             :                     const OGRFieldDefn *poFieldDefn =
    1875          63 :                         poLayer->GetLayerDefn()->GetFieldDefn(idx);
    1876             : 
    1877          64 :                     if (col_func == SWQCF_SUM &&
    1878           1 :                         poFieldDefn->GetType() == OFTDateTime)
    1879           0 :                         break;
    1880             : 
    1881          63 :                     int eOutOGRType = -1;
    1882          63 :                     const OGRField *psField = nullptr;
    1883             :                     OGRField sField;
    1884          63 :                     if (col_func == SWQCF_MIN || col_func == SWQCF_MAX)
    1885             :                     {
    1886          55 :                         psField = poLayer->GetMinMaxValue(
    1887             :                             poFieldDefn, col_func == SWQCF_MIN, eOutOGRType);
    1888          55 :                         if (eOutOGRType < 0)
    1889           5 :                             break;
    1890             :                     }
    1891             :                     else
    1892             :                     {
    1893           8 :                         double dfMin = 0.0;
    1894           8 :                         double dfMax = 0.0;
    1895           8 :                         int nCount = 0;
    1896           8 :                         double dfSum = 0.0;
    1897             : 
    1898           8 :                         if (!poLayer->GetMinMaxSumCount(poFieldDefn, dfMin,
    1899             :                                                         dfMax, dfSum, nCount))
    1900           0 :                             break;
    1901           8 :                         psField = &sField;
    1902           8 :                         if (col_func == SWQCF_AVG)
    1903             :                         {
    1904           6 :                             if (nCount == 0)
    1905             :                             {
    1906           1 :                                 eOutOGRType = OFTReal;
    1907           1 :                                 psField = nullptr;
    1908             :                             }
    1909             :                             else
    1910             :                             {
    1911           5 :                                 if (poFieldDefn->GetType() == OFTDateTime)
    1912             :                                 {
    1913           1 :                                     eOutOGRType = OFTDateTime;
    1914           1 :                                     FileGDBDoubleDateToOGRDate(dfSum / nCount,
    1915             :                                                                false, &sField);
    1916             :                                 }
    1917             :                                 else
    1918             :                                 {
    1919           4 :                                     eOutOGRType = OFTReal;
    1920           4 :                                     sField.Real = dfSum / nCount;
    1921             :                                 }
    1922             :                             }
    1923             :                         }
    1924           2 :                         else if (col_func == SWQCF_COUNT)
    1925             :                         {
    1926           1 :                             sField.Integer = nCount;
    1927           1 :                             eOutOGRType = OFTInteger;
    1928             :                         }
    1929             :                         else
    1930             :                         {
    1931           1 :                             sField.Real = dfSum;
    1932           1 :                             eOutOGRType = OFTReal;
    1933             :                         }
    1934             :                     }
    1935             : 
    1936          58 :                     if (poMemLayer == nullptr)
    1937             :                     {
    1938          36 :                         poMemLayer =
    1939          36 :                             new OGRMemLayer("SELECT", nullptr, wkbNone);
    1940          36 :                         CPL_IGNORE_RET_VAL(poMemLayer->CreateFeature(
    1941          36 :                             std::make_unique<OGRFeature>(
    1942          72 :                                 poMemLayer->GetLayerDefn())));
    1943             :                     }
    1944             : 
    1945             :                     const char *pszMinMaxFieldName =
    1946         102 :                         CPLSPrintf("%s_%s",
    1947             :                                    (col_func == SWQCF_MIN)   ? "MIN"
    1948          52 :                                    : (col_func == SWQCF_MAX) ? "MAX"
    1949          10 :                                    : (col_func == SWQCF_AVG) ? "AVG"
    1950           2 :                                    : (col_func == SWQCF_SUM) ? "SUM"
    1951             :                                                              : "COUNT",
    1952          58 :                                    oSelect.column_defs[i].field_name);
    1953             :                     OGRFieldDefn oFieldDefn(
    1954             :                         pszMinMaxFieldName,
    1955         116 :                         static_cast<OGRFieldType>(eOutOGRType));
    1956          58 :                     poMemLayer->CreateField(&oFieldDefn);
    1957          58 :                     if (psField != nullptr)
    1958             :                     {
    1959             :                         auto poFeature = std::unique_ptr<OGRFeature>(
    1960          55 :                             poMemLayer->GetFeature(0));
    1961          55 :                         poFeature->SetField(oFieldDefn.GetNameRef(), psField);
    1962          55 :                         CPL_IGNORE_RET_VAL(
    1963          55 :                             poMemLayer->SetFeature(std::move(poFeature)));
    1964             :                     }
    1965             :                 }
    1966         311 :                 if (i != oSelect.result_columns())
    1967             :                 {
    1968         275 :                     delete poMemLayer;
    1969             :                 }
    1970             :                 else
    1971             :                 {
    1972          36 :                     CPLDebug(
    1973             :                         "OpenFileGDB",
    1974             :                         "Using optimized MIN/MAX/SUM/AVG/COUNT implementation");
    1975          36 :                     bLastSQLUsedOptimizedImplementation = true;
    1976          36 :                     return poMemLayer;
    1977             :                 }
    1978             :             }
    1979             :         }
    1980             : 
    1981             :         /* --------------------------------------------------------------------
    1982             :          */
    1983             :         /*      ORDER BY optimization */
    1984             :         /* --------------------------------------------------------------------
    1985             :          */
    1986         484 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1987         484 :             oSelect.table_count == 1 && oSelect.order_specs == 1 &&
    1988          26 :             oSelect.query_mode != SWQM_DISTINCT_LIST)
    1989             :         {
    1990             :             OGROpenFileGDBLayer *poLayer =
    1991          50 :                 reinterpret_cast<OGROpenFileGDBLayer *>(
    1992          25 :                     GetLayerByName(oSelect.table_defs[0].table_name));
    1993          50 :             if (poLayer != nullptr &&
    1994          25 :                 poLayer->HasIndexForField(oSelect.order_defs[0].field_name))
    1995             :             {
    1996          23 :                 OGRErr eErr = OGRERR_NONE;
    1997          23 :                 if (oSelect.where_expr != nullptr)
    1998             :                 {
    1999             :                     /* The where must be a simple comparison on the column */
    2000             :                     /* that is used for ordering */
    2001          12 :                     if (oSelect.where_expr->eNodeType == SNT_OPERATION &&
    2002           4 :                         OGROpenFileGDBIsComparisonOp(
    2003           4 :                             oSelect.where_expr->nOperation) &&
    2004           3 :                         oSelect.where_expr->nOperation != SWQ_NE &&
    2005           3 :                         oSelect.where_expr->nSubExprCount == 2 &&
    2006           3 :                         (oSelect.where_expr->papoSubExpr[0]->eNodeType ==
    2007           0 :                              SNT_COLUMN ||
    2008           0 :                          oSelect.where_expr->papoSubExpr[0]->eNodeType ==
    2009           3 :                              SNT_CONSTANT) &&
    2010           3 :                         oSelect.where_expr->papoSubExpr[0]->field_type ==
    2011           3 :                             SWQ_STRING &&
    2012           3 :                         EQUAL(oSelect.where_expr->papoSubExpr[0]->string_value,
    2013           8 :                               oSelect.order_defs[0].field_name) &&
    2014           2 :                         oSelect.where_expr->papoSubExpr[1]->eNodeType ==
    2015             :                             SNT_CONSTANT)
    2016             :                     {
    2017             :                         /* ok */
    2018             :                     }
    2019             :                     else
    2020           2 :                         eErr = OGRERR_FAILURE;
    2021             :                 }
    2022          23 :                 if (eErr == OGRERR_NONE)
    2023             :                 {
    2024          21 :                     int i = 0;  // Used after for.
    2025          40 :                     for (; i < oSelect.result_columns(); i++)
    2026             :                     {
    2027          23 :                         if (oSelect.column_defs[i].col_func != SWQCF_NONE)
    2028           1 :                             break;
    2029          22 :                         if (oSelect.column_defs[i].field_name == nullptr)
    2030           0 :                             break;
    2031          22 :                         if (oSelect.column_defs[i].distinct_flag)
    2032           0 :                             break;
    2033          22 :                         if (oSelect.column_defs[i].target_type != SWQ_OTHER)
    2034           1 :                             break;
    2035          21 :                         if (strcmp(oSelect.column_defs[i].field_name, "*") !=
    2036          27 :                                 0 &&
    2037          12 :                             poLayer->GetLayerDefn()->GetFieldIndex(
    2038           6 :                                 oSelect.column_defs[i].field_name) < 0)
    2039           2 :                             break;
    2040             :                     }
    2041          21 :                     if (i != oSelect.result_columns())
    2042           4 :                         eErr = OGRERR_FAILURE;
    2043             :                 }
    2044          23 :                 if (eErr == OGRERR_NONE)
    2045             :                 {
    2046          17 :                     int op = -1;
    2047          17 :                     swq_expr_node *poValue = nullptr;
    2048          17 :                     if (oSelect.where_expr != nullptr)
    2049             :                     {
    2050           2 :                         op = oSelect.where_expr->nOperation;
    2051           2 :                         poValue = oSelect.where_expr->papoSubExpr[1];
    2052             :                     }
    2053             : 
    2054          34 :                     FileGDBIterator *poIter = poLayer->BuildIndex(
    2055          17 :                         oSelect.order_defs[0].field_name,
    2056          17 :                         oSelect.order_defs[0].ascending_flag, op, poValue);
    2057             : 
    2058             :                     /* Check that they are no NULL values */
    2059          32 :                     if (oSelect.where_expr == nullptr && poIter != nullptr &&
    2060          15 :                         poIter->GetRowCount() !=
    2061          15 :                             poLayer->GetFeatureCount(FALSE))
    2062             :                     {
    2063           1 :                         delete poIter;
    2064           1 :                         poIter = nullptr;
    2065             :                     }
    2066             : 
    2067          17 :                     if (poIter != nullptr)
    2068             :                     {
    2069          16 :                         CPLDebug("OpenFileGDB",
    2070             :                                  "Using OGROpenFileGDBSimpleSQLLayer");
    2071          16 :                         bLastSQLUsedOptimizedImplementation = true;
    2072             :                         return new OGROpenFileGDBSimpleSQLLayer(
    2073          16 :                             poLayer, poIter, oSelect.result_columns(),
    2074          16 :                             oSelect.column_defs.data(), oSelect.offset,
    2075          16 :                             oSelect.limit);
    2076             :                     }
    2077             :                 }
    2078             :             }
    2079             :         }
    2080             :     }
    2081             : 
    2082         477 :     return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
    2083             : }
    2084             : 
    2085             : /************************************************************************/
    2086             : /*                          ReleaseResultSet()                          */
    2087             : /************************************************************************/
    2088             : 
    2089         748 : void OGROpenFileGDBDataSource::ReleaseResultSet(OGRLayer *poResultsSet)
    2090             : {
    2091         748 :     delete poResultsSet;
    2092         748 : }
    2093             : 
    2094             : /************************************************************************/
    2095             : /*                            GetFileList()                             */
    2096             : /************************************************************************/
    2097             : 
    2098           9 : char **OGROpenFileGDBDataSource::GetFileList()
    2099             : {
    2100           9 :     int nInterestTable = -1;
    2101           9 :     const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
    2102          18 :     CPLString osFilenameRadix;
    2103           9 :     unsigned int unInterestTable = 0;
    2104          18 :     if (strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
    2105           9 :         pszFilenameWithoutPath[0] == 'a' &&
    2106           0 :         sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
    2107             :     {
    2108           0 :         nInterestTable = static_cast<int>(unInterestTable);
    2109           0 :         osFilenameRadix = CPLSPrintf("a%08x.", nInterestTable);
    2110             :     }
    2111             : 
    2112           9 :     char **papszFiles = VSIReadDir(m_osDirName);
    2113          18 :     CPLStringList osStringList;
    2114           9 :     char **papszIter = papszFiles;
    2115        1172 :     for (; papszIter != nullptr && *papszIter != nullptr; papszIter++)
    2116             :     {
    2117        1163 :         if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
    2118           6 :             continue;
    2119        1157 :         if (osFilenameRadix.empty() ||
    2120           0 :             strncmp(*papszIter, osFilenameRadix, osFilenameRadix.size()) == 0)
    2121             :         {
    2122        1157 :             osStringList.push_back(
    2123        2314 :                 CPLFormFilenameSafe(m_osDirName, *papszIter, nullptr));
    2124             :         }
    2125             :     }
    2126           9 :     CSLDestroy(papszFiles);
    2127          18 :     return osStringList.StealList();
    2128             : }
    2129             : 
    2130             : /************************************************************************/
    2131             : /*                              BuildSRS()                              */
    2132             : /************************************************************************/
    2133             : 
    2134             : OGRSpatialReferenceRefCountedPtr
    2135        2827 : OGROpenFileGDBDataSource::BuildSRS(const CPLXMLNode *psInfo)
    2136             : {
    2137             :     const char *pszWKT =
    2138        2827 :         CPLGetXMLValue(psInfo, "SpatialReference.WKT", nullptr);
    2139             :     const int nWKID =
    2140        2827 :         atoi(CPLGetXMLValue(psInfo, "SpatialReference.WKID", "0"));
    2141             :     // The concept of LatestWKID is explained in
    2142             :     // https://support.esri.com/en/technical-article/000013950
    2143             :     int nLatestWKID =
    2144        2827 :         atoi(CPLGetXMLValue(psInfo, "SpatialReference.LatestWKID", "0"));
    2145             : 
    2146        2827 :     OGRSpatialReferenceRefCountedPtr poSRS;
    2147        2827 :     if (nWKID > 0 || nLatestWKID > 0)
    2148             :     {
    2149             :         const auto ImportFromCode =
    2150        2421 :             [](OGRSpatialReference &oSRS, int nLatestCode, int nCode)
    2151             :         {
    2152        2421 :             bool bSuccess = false;
    2153        2421 :             CPLErrorStateBackuper oQuietError(CPLQuietErrorHandler);
    2154             : 
    2155             :             // Try first with nLatestWKID as there is a higher chance it is a
    2156             :             // EPSG code and not an ESRI one.
    2157        2421 :             if (nLatestCode > 0)
    2158             :             {
    2159         142 :                 if (nLatestCode > 32767)
    2160             :                 {
    2161           1 :                     if (oSRS.SetFromUserInput(
    2162           1 :                             CPLSPrintf("ESRI:%d", nLatestCode)) == OGRERR_NONE)
    2163             :                     {
    2164           0 :                         bSuccess = true;
    2165             :                     }
    2166             :                 }
    2167         141 :                 else if (oSRS.importFromEPSG(nLatestCode) == OGRERR_NONE)
    2168             :                 {
    2169         141 :                     bSuccess = true;
    2170             :                 }
    2171         142 :                 if (!bSuccess)
    2172             :                 {
    2173           1 :                     CPLDebug("OpenFileGDB", "Cannot import SRID %d",
    2174             :                              nLatestCode);
    2175             :                 }
    2176             :             }
    2177        2421 :             if (!bSuccess && nCode > 0)
    2178             :             {
    2179        2280 :                 if (nCode > 32767)
    2180             :                 {
    2181           1 :                     if (oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nCode)) ==
    2182             :                         OGRERR_NONE)
    2183             :                     {
    2184           0 :                         bSuccess = true;
    2185             :                     }
    2186             :                 }
    2187        2279 :                 else if (oSRS.importFromEPSG(nCode) == OGRERR_NONE)
    2188             :                 {
    2189        2279 :                     bSuccess = true;
    2190             :                 }
    2191        2280 :                 if (!bSuccess)
    2192             :                 {
    2193           1 :                     CPLDebug("OpenFileGDB", "Cannot import SRID %d", nCode);
    2194             :                 }
    2195             :             }
    2196             : 
    2197        4842 :             return bSuccess;
    2198             :         };
    2199             : 
    2200        2420 :         poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
    2201        2420 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2202        2420 :         if (!ImportFromCode(*poSRS.get(), nLatestWKID, nWKID))
    2203             :         {
    2204           0 :             poSRS.reset();
    2205             :         }
    2206             :         else
    2207             :         {
    2208        2420 :             const int nLatestVCSWKID = atoi(
    2209             :                 CPLGetXMLValue(psInfo, "SpatialReference.LatestVCSWKID", "0"));
    2210             :             const int nVCSWKID =
    2211        2420 :                 atoi(CPLGetXMLValue(psInfo, "SpatialReference.VCSWKID", "0"));
    2212        2420 :             if (nVCSWKID > 0 || nLatestVCSWKID > 0)
    2213             :             {
    2214             :                 auto poVertSRS =
    2215           2 :                     OGRSpatialReferenceRefCountedPtr::makeInstance();
    2216           1 :                 if (ImportFromCode(*poVertSRS.get(), nLatestVCSWKID, nVCSWKID))
    2217             :                 {
    2218             :                     auto poCompoundSRS =
    2219           0 :                         OGRSpatialReferenceRefCountedPtr::makeInstance();
    2220           0 :                     if (poCompoundSRS->SetCompoundCS(
    2221           0 :                             std::string(poSRS->GetName())
    2222           0 :                                 .append(" + ")
    2223           0 :                                 .append(poVertSRS->GetName())
    2224             :                                 .c_str(),
    2225           0 :                             poSRS.get(), poVertSRS.get()) == OGRERR_NONE)
    2226             :                     {
    2227           0 :                         poCompoundSRS->SetAxisMappingStrategy(
    2228             :                             OAMS_TRADITIONAL_GIS_ORDER);
    2229           0 :                         poSRS = std::move(poCompoundSRS);
    2230             :                     }
    2231             :                 }
    2232           2 :                 if (!poSRS->IsCompound() &&
    2233           1 :                     !(pszWKT != nullptr && pszWKT[0] != '{'))
    2234             :                 {
    2235           0 :                     poSRS.reset();
    2236             :                 }
    2237             :             }
    2238             :         }
    2239             :     }
    2240        7667 :     if (pszWKT != nullptr && pszWKT[0] != '{' &&
    2241        2421 :         (poSRS == nullptr ||
    2242        2419 :          (strstr(pszWKT, "VERTCS") && !poSRS->IsCompound())))
    2243             :     {
    2244           3 :         poSRS = BuildSRS(pszWKT);
    2245             :     }
    2246        2827 :     return poSRS;
    2247             : }
    2248             : 
    2249             : /************************************************************************/
    2250             : /*                              BuildSRS()                              */
    2251             : /************************************************************************/
    2252             : 
    2253             : OGRSpatialReferenceRefCountedPtr
    2254         365 : OGROpenFileGDBDataSource::BuildSRS(const char *pszWKT)
    2255             : {
    2256         365 :     OGRSpatialReferenceRefCountedPtr poSRS;
    2257         365 :     m_oCacheWKTToSRS.tryGet(pszWKT, poSRS);
    2258         365 :     if (poSRS)
    2259         199 :         return poSRS;
    2260             : 
    2261         166 :     poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
    2262         166 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2263         166 :     if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
    2264             :     {
    2265           0 :         poSRS.reset();
    2266             :     }
    2267         166 :     if (poSRS != nullptr)
    2268             :     {
    2269         166 :         if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
    2270             :         {
    2271         166 :             auto poSRSMatch = poSRS->FindBestMatch(100);
    2272         166 :             if (poSRSMatch)
    2273             :             {
    2274         165 :                 poSRS.reset(poSRSMatch, /* add_ref = */ false);
    2275         165 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2276             :             }
    2277         166 :             m_oCacheWKTToSRS.insert(pszWKT, poSRS);
    2278             :         }
    2279             :         else
    2280             :         {
    2281           0 :             poSRS->AutoIdentifyEPSG();
    2282             :         }
    2283             :     }
    2284         166 :     return poSRS;
    2285             : }

Generated by: LCOV version 1.14