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

Generated by: LCOV version 1.14