LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - ogropenfilegdbdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 948 1052 90.1 %
Date: 2024-11-21 22:18:42 Functions: 45 45 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB OGR driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_openfilegdb.h"
      15             : 
      16             : #include <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         566 :     OGROpenFileGDBGroup(const std::string &osParentName, const char *pszName)
      55         566 :         : GDALGroup(osParentName, pszName)
      56             :     {
      57         566 :     }
      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         843 : OGROpenFileGDBDataSource::OGROpenFileGDBDataSource()
      85         843 :     : m_papszFiles(nullptr), bLastSQLUsedOptimizedImplementation(false)
      86             : {
      87         843 : }
      88             : 
      89             : /************************************************************************/
      90             : /*                     ~OGROpenFileGDBDataSource()                      */
      91             : /************************************************************************/
      92        1686 : OGROpenFileGDBDataSource::~OGROpenFileGDBDataSource()
      93             : 
      94             : {
      95         843 :     OGROpenFileGDBDataSource::Close();
      96        1686 : }
      97             : 
      98             : /************************************************************************/
      99             : /*                              Close()                                 */
     100             : /************************************************************************/
     101             : 
     102        1557 : CPLErr OGROpenFileGDBDataSource::Close()
     103             : {
     104        1557 :     CPLErr eErr = CE_None;
     105        1557 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     106             :     {
     107         843 :         if (m_bInTransaction)
     108           2 :             OGROpenFileGDBDataSource::RollbackTransaction();
     109             : 
     110         843 :         if (OGROpenFileGDBDataSource::FlushCache(true) != CE_None)
     111           0 :             eErr = CE_Failure;
     112             : 
     113         843 :         m_apoLayers.clear();
     114         843 :         m_apoHiddenLayers.clear();
     115         843 :         CSLDestroy(m_papszFiles);
     116             : 
     117         843 :         if (GDALDataset::Close() != CE_None)
     118           0 :             eErr = CE_Failure;
     119             :     }
     120        1557 :     return eErr;
     121             : }
     122             : 
     123             : /************************************************************************/
     124             : /*                             FileExists()                             */
     125             : /************************************************************************/
     126             : 
     127       17447 : int OGROpenFileGDBDataSource::FileExists(const char *pszFilename)
     128             : {
     129       17447 :     if (m_papszFiles)
     130       17438 :         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         612 : bool OGROpenFileGDBDataSource::Open(const GDALOpenInfo *poOpenInfo,
     142             :                                     bool &bRetryFileGDBOut)
     143             : {
     144         612 :     bRetryFileGDBOut = false;
     145             : 
     146         612 :     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         612 :     m_osDirName = poOpenInfo->pszFilename;
     154             : 
     155        1224 :     std::string osRasterLayerName;
     156         612 :     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        1224 :     FileGDBTable oTable;
     180             : 
     181             :     // Whether to open directly a given .gdbtable file (mostly for debugging
     182             :     // purposes)
     183         612 :     int nInterestTable = 0;
     184         612 :     unsigned int unInterestTable = 0;
     185         612 :     const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
     186        1337 :     if (poOpenInfo->nHeaderBytes > 0 &&
     187         113 :         strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
     188          57 :         pszFilenameWithoutPath[0] == 'a' &&
     189         781 :         EQUAL(pszFilenameWithoutPath + strlen("a00000000"), ".gdbtable") &&
     190          56 :         sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
     191             :     {
     192          56 :         nInterestTable = static_cast<int>(unInterestTable);
     193          56 :         m_osDirName = CPLGetPath(m_osDirName);
     194             :     }
     195             : 
     196         702 :     if (EQUAL(CPLGetExtension(m_osDirName), "zip") &&
     197          90 :         !STARTS_WITH(m_osDirName, "/vsizip/"))
     198             :     {
     199          84 :         m_osDirName = "/vsizip/" + m_osDirName;
     200             :     }
     201         528 :     else if (EQUAL(CPLGetExtension(m_osDirName), "tar") &&
     202           0 :              !STARTS_WITH(m_osDirName, "/vsitar/"))
     203             :     {
     204           0 :         m_osDirName = "/vsitar/" + m_osDirName;
     205             :     }
     206             : 
     207        1131 :     if (STARTS_WITH(m_osDirName, "/vsizip/") ||
     208         519 :         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(CPLGetExtension(papszDir[i]), "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 =
     239        1224 :         CPLFormFilename(m_osDirName.c_str(), ".ogrtransaction_backup", nullptr);
     240             :     VSIStatBufL sStat;
     241         612 :     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         611 :     m_papszFiles = VSIReadDir(m_osDirName);
     269             : 
     270             :     /* Explore catalog table */
     271             :     m_osGDBSystemCatalogFilename =
     272         611 :         CPLFormFilename(m_osDirName, "a00000001", "gdbtable");
     273        1212 :     if (!FileExists(m_osGDBSystemCatalogFilename.c_str()) ||
     274         601 :         !oTable.Open(m_osGDBSystemCatalogFilename.c_str(),
     275         601 :                      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 char *pszTablX =
     284           1 :                 CPLResetExtension(poOpenInfo->pszFilename, "gdbtablx");
     285           1 :             if ((!FileExists(pszTablX) &&
     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         575 :     const int idxName = oTable.GetFieldIdx("Name");
     299         575 :     const int idxFileformat = oTable.GetFieldIdx("FileFormat");
     300        2294 :     if (!(idxName >= 0 && idxFileformat >= 0 &&
     301         573 :           oTable.GetField(idxName)->GetType() == FGFT_STRING &&
     302        1146 :           (oTable.GetField(idxFileformat)->GetType() == FGFT_INT16 ||
     303         573 :            oTable.GetField(idxFileformat)->GetType() == FGFT_INT32)))
     304             :     {
     305           2 :         return false;
     306             :     }
     307             : 
     308         573 :     int iGDBItems = -1;          /* V10 */
     309         573 :     int iGDBFeatureClasses = -1; /* V9.X */
     310         573 :     int iGDBObjectClasses = -1;  /* V9.X */
     311             : 
     312        1146 :     std::vector<std::string> aosTableNames;
     313             :     try
     314             :     {
     315       20561 :         for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     316             :         {
     317       19988 :             if (!oTable.SelectRow(i))
     318             :             {
     319        4986 :                 if (oTable.HasGotError())
     320           0 :                     break;
     321        4986 :                 aosTableNames.push_back("");
     322        4986 :                 continue;
     323             :             }
     324             : 
     325       15002 :             const OGRField *psField = oTable.GetFieldValue(idxName);
     326       15002 :             if (psField != nullptr)
     327             :             {
     328       14912 :                 aosTableNames.push_back(psField->String);
     329       14912 :                 if (strcmp(psField->String, "GDB_SpatialRefs") == 0)
     330             :                 {
     331             :                     // Normally a00000003
     332             :                     m_osGDBSpatialRefsFilename = CPLFormFilename(
     333             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     334         571 :                         nullptr);
     335             :                 }
     336       14341 :                 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             :                     m_osGDBItemsFilename = CPLFormFilename(
     341             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     342         546 :                         nullptr);
     343         546 :                     iGDBItems = i;
     344             :                 }
     345       13795 :                 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             :                     m_osGDBItemRelationshipsFilename = CPLFormFilename(
     350             :                         m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
     351         548 :                         nullptr);
     352             :                 }
     353       13247 :                 else if (strcmp(psField->String, "GDB_FeatureClasses") == 0)
     354             :                 {
     355             :                     // FileGDB v9
     356          23 :                     iGDBFeatureClasses = i;
     357             :                 }
     358       13224 :                 else if (strcmp(psField->String, "GDB_ObjectClasses") == 0)
     359             :                 {
     360             :                     // FileGDB v9
     361          23 :                     iGDBObjectClasses = i;
     362             :                 }
     363       14912 :                 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         573 :     oTable.Close();
     377             : 
     378        1146 :     std::set<int> oSetIgnoredRasterLayerTableNum;
     379         573 :     if (iGDBItems >= 0)
     380             :     {
     381         546 :         eAccess = poOpenInfo->eAccess;
     382             : 
     383         546 :         const bool bRet = OpenFileGDBv10(
     384             :             iGDBItems, nInterestTable, poOpenInfo, osRasterLayerName,
     385             :             oSetIgnoredRasterLayerTableNum, bRetryFileGDBOut);
     386         546 :         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         567 :     if (m_apoLayers.empty() && nInterestTable > 0)
     412             :     {
     413          54 :         if (FileExists(poOpenInfo->pszFilename))
     414             :         {
     415          54 :             const char *pszLyrName = nullptr;
     416         108 :             if (nInterestTable <= static_cast<int>(aosTableNames.size()) &&
     417          54 :                 !aosTableNames[nInterestTable - 1].empty())
     418          54 :                 pszLyrName = aosTableNames[nInterestTable - 1].c_str();
     419             :             else
     420           0 :                 pszLyrName = CPLSPrintf("a%08x", nInterestTable);
     421          54 :             m_apoLayers.push_back(std::make_unique<OGROpenFileGDBLayer>(
     422          54 :                 this, poOpenInfo->pszFilename, pszLyrName, "", "",
     423         108 :                 eAccess == GA_Update));
     424             :         }
     425             :         else
     426             :         {
     427           0 :             return false;
     428             :         }
     429             :     }
     430             : 
     431         567 :     if (nInterestTable == 0)
     432             :     {
     433         512 :         const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
     434         512 :             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       11124 :         for (const auto &oIter : m_osMapNameToIdx)
     439             :         {
     440             :             // test if layer is already added
     441       10612 :             if (GDALDataset::GetLayerByName(oIter.first.c_str()))
     442           0 :                 continue;
     443             : 
     444       10612 :             if (bListAllTables || !IsPrivateLayerName(oIter.first))
     445             :             {
     446        5905 :                 const int idx = oIter.second;
     447             :                 CPLString osFilename(CPLFormFilename(
     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         567 :     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         538 :     if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
     478          50 :         (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
     479             :     {
     480          24 :         if (m_aosSubdatasets.empty())
     481             :         {
     482          16 :             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         514 :     else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     493         488 :              (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
     494             :     {
     495         485 :         if (m_apoLayers.empty() && poOpenInfo->eAccess == GA_ReadOnly)
     496             :         {
     497           2 :             return false;
     498             :         }
     499             :     }
     500             : 
     501         513 :     return true;
     502             : }
     503             : 
     504             : /***********************************************************************/
     505             : /*                             AddLayer()                              */
     506             : /***********************************************************************/
     507             : 
     508        3760 : 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        3760 :     const auto oIter = m_osMapNameToIdx.find(osName);
     515        3760 :     int idx = 0;
     516        3760 :     if (oIter != m_osMapNameToIdx.end())
     517        3746 :         idx = oIter->second;
     518        3760 :     if (idx > 0 && (nInterestTable <= 0 || nInterestTable == idx))
     519             :     {
     520        3660 :         m_osMapNameToIdx.erase(oIter);
     521             : 
     522             :         CPLString osFilename =
     523        3660 :             CPLFormFilename(m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable");
     524        3660 :         if (FileExists(osFilename))
     525             :         {
     526        3647 :             nCandidateLayers++;
     527             : 
     528        3647 :             if (m_papszFiles != nullptr)
     529             :             {
     530             :                 const CPLString osSDC =
     531        3647 :                     CPLResetExtension(osFilename, "gdbtable.sdc");
     532             :                 const CPLString osCDF =
     533        3647 :                     CPLResetExtension(osFilename, "gdbtable.cdf");
     534        3647 :                 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        3644 :                 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        3644 :             m_apoLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
     603             :                 this, osFilename, osName, osDefinition, osDocumentation,
     604        3644 :                 eAccess == GA_Update, eGeomType, osParentDefinition));
     605        3644 :             return m_apoLayers.back().get();
     606             :         }
     607             :     }
     608         113 :     return nullptr;
     609             : }
     610             : 
     611          13 : std::vector<std::string> OGROpenFileGDBGroup::GetGroupNames(CSLConstList) const
     612             : {
     613          13 :     std::vector<std::string> ret;
     614          20 :     for (const auto &poSubGroup : m_apoSubGroups)
     615           7 :         ret.emplace_back(poSubGroup->GetName());
     616          13 :     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          13 : OGROpenFileGDBGroup::GetVectorLayerNames(CSLConstList) const
     632             : {
     633          13 :     std::vector<std::string> ret;
     634          53 :     for (const auto &poLayer : m_apoLayers)
     635          40 :         ret.emplace_back(poLayer->GetName());
     636          13 :     return ret;
     637             : }
     638             : 
     639          24 : OGRLayer *OGROpenFileGDBGroup::OpenVectorLayer(const std::string &osName,
     640             :                                                CSLConstList) const
     641             : {
     642         106 :     for (const auto &poLayer : m_apoLayers)
     643             :     {
     644         105 :         if (poLayer->GetName() == osName)
     645          23 :             return poLayer;
     646             :     }
     647           1 :     return nullptr;
     648             : }
     649             : 
     650             : /***********************************************************************/
     651             : /*                         OpenFileGDBv10()                            */
     652             : /***********************************************************************/
     653             : 
     654         546 : 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         546 :     CPLDebug("OpenFileGDB", "FileGDB v10 or later");
     661             : 
     662        1092 :     FileGDBTable oTable;
     663             : 
     664             :     CPLString osFilename(CPLFormFilename(
     665        1092 :         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         546 :     const bool bOpenInUpdateMode =
     671         546 :         (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0;
     672         546 :     if (!oTable.Open(osFilename, bOpenInUpdateMode))
     673           0 :         return false;
     674             : 
     675         546 :     const int iUUID = oTable.GetFieldIdx("UUID");
     676         546 :     const int iType = oTable.GetFieldIdx("Type");
     677         546 :     const int iName = oTable.GetFieldIdx("Name");
     678         546 :     const int iPath = oTable.GetFieldIdx("Path");
     679         546 :     const int iDefinition = oTable.GetFieldIdx("Definition");
     680         546 :     const int iDocumentation = oTable.GetFieldIdx("Documentation");
     681         546 :     if (iUUID < 0 || iType < 0 || iName < 0 || iPath < 0 || iDefinition < 0 ||
     682         546 :         iDocumentation < 0 ||
     683        1092 :         oTable.GetField(iUUID)->GetType() != FGFT_GLOBALID ||
     684        1092 :         oTable.GetField(iType)->GetType() != FGFT_GUID ||
     685        1092 :         oTable.GetField(iName)->GetType() != FGFT_STRING ||
     686        1092 :         oTable.GetField(iPath)->GetType() != FGFT_STRING ||
     687        1638 :         oTable.GetField(iDefinition)->GetType() != FGFT_XML ||
     688         546 :         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        1092 :     auto poRootGroup = std::make_shared<OGROpenFileGDBGroup>(std::string(), "");
     696         546 :     m_poRootGroup = poRootGroup;
     697             :     std::map<std::string, std::shared_ptr<OGROpenFileGDBGroup>>
     698        1092 :         oMapPathToFeatureDataset;
     699             : 
     700             :     // First pass to collect FeatureDatasets
     701        9795 :     for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     702             :     {
     703        9249 :         if (!oTable.SelectRow(i))
     704             :         {
     705        2097 :             if (oTable.HasGotError())
     706           0 :                 break;
     707        2097 :             continue;
     708             :         }
     709             : 
     710       14304 :         CPLString osType;
     711        7152 :         const OGRField *psTypeField = oTable.GetFieldValue(iType);
     712        7152 :         if (psTypeField != nullptr)
     713             :         {
     714        7152 :             const char *pszType = psTypeField->String;
     715        7152 :             if (pszType)
     716        7152 :                 osType = pszType;
     717             :         }
     718             : 
     719       14304 :         std::string osPath;
     720        7152 :         const OGRField *psField = oTable.GetFieldValue(iPath);
     721        7152 :         if (psField != nullptr)
     722             :         {
     723        7152 :             const char *pszPath = psField->String;
     724        7152 :             if (pszPath)
     725        7152 :                 osPath = pszPath;
     726             :         }
     727             : 
     728        7152 :         if (osPath == "\\")
     729             :         {
     730         546 :             psField = oTable.GetFieldValue(iUUID);
     731         546 :             if (psField != nullptr)
     732             :             {
     733         546 :                 const char *pszUUID = psField->String;
     734         546 :                 if (pszUUID)
     735         546 :                     m_osRootGUID = pszUUID;
     736             :             }
     737             :         }
     738             : 
     739        7152 :         psField = oTable.GetFieldValue(iDefinition);
     740        7152 :         if (psField != nullptr &&
     741        6606 :             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       13718 :         else if (psField != nullptr &&
     764        6586 :                  osType.tolower().compare(pszRelationshipTypeUUID) == 0)
     765             :         {
     766             :             // relationship item
     767         657 :             auto poRelationship = ParseXMLRelationshipDef(psField->String);
     768         219 :             if (poRelationship)
     769             :             {
     770         219 :                 const auto &relationshipName = poRelationship->GetName();
     771         438 :                 m_osMapRelationships.insert(std::pair(
     772         657 :                     std::string(relationshipName), std::move(poRelationship)));
     773             :             }
     774             :         }
     775             :     }
     776             : 
     777             :     // Now collect layers
     778         546 :     int nCandidateLayers = 0;
     779         546 :     int nLayersCDF = 0;
     780         546 :     bool bRet = true;
     781        9795 :     for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
     782             :     {
     783        9249 :         if (!oTable.SelectRow(i))
     784             :         {
     785        2097 :             if (oTable.HasGotError())
     786           0 :                 break;
     787        2097 :             continue;
     788             :         }
     789             : 
     790        7152 :         const OGRField *psField = oTable.GetFieldValue(iDefinition);
     791        7152 :         if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
     792        5290 :             (strstr(psField->String, "DEFeatureClassInfo") != nullptr ||
     793        2441 :              strstr(psField->String, "DETableInfo") != nullptr))
     794             :         {
     795        6762 :             CPLString osDefinition(psField->String);
     796             : 
     797        3381 :             psField = oTable.GetFieldValue(iDocumentation);
     798             :             CPLString osDocumentation(psField != nullptr ? psField->String
     799        6762 :                                                          : "");
     800             : 
     801        3381 :             std::shared_ptr<OGROpenFileGDBGroup> poParent;
     802        3381 :             psField = oTable.GetFieldValue(iPath);
     803        3381 :             if (psField != nullptr)
     804             :             {
     805        3381 :                 const char *pszPath = psField->String;
     806        3381 :                 if (pszPath)
     807             :                 {
     808        6762 :                     std::string osPath(pszPath);
     809        3381 :                     const auto nPos = osPath.rfind('\\');
     810        3381 :                     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        3381 :             psField = oTable.GetFieldValue(iName);
     831        3381 :             if (psField != nullptr)
     832             :             {
     833        3381 :                 OGRLayer *poLayer = AddLayer(
     834        3381 :                     psField->String, nInterestTable, nCandidateLayers,
     835             :                     nLayersCDF, osDefinition, osDocumentation, wkbUnknown,
     836        6762 :                     poParent ? poParent->GetDefinition() : std::string());
     837             : 
     838        3381 :                 if (poLayer)
     839             :                 {
     840        3265 :                     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        3246 :                         poRootGroup->m_apoLayers.emplace_back(poLayer);
     849             :                     }
     850             :                 }
     851        3381 :             }
     852             :         }
     853        3771 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
     854        1909 :                  (strstr(psField->String, "GPCodedValueDomain2") != nullptr ||
     855         833 :                   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        2676 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
     866        1433 :                  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        1652 :         else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
     933         697 :                  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         546 :     if (nLayersCDF > 0 && GDALGetDriverByName("FileGDB") != nullptr)
     963             :     {
     964           2 :         bRetryFileGDBOut = true;
     965           2 :         return false;
     966             :     }
     967             : 
     968         544 :     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             :     CPLString osFilename(CPLFormFilename(
     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             :     osFilename = CPLFormFilename(
    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         665 : int OGROpenFileGDBDataSource::TestCapability(const char *pszCap)
    1169             : {
    1170         665 :     if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
    1171         595 :         EQUAL(pszCap, ODsCAddFieldDomain) ||
    1172         591 :         EQUAL(pszCap, ODsCDeleteFieldDomain) ||
    1173         590 :         EQUAL(pszCap, ODsCUpdateFieldDomain) ||
    1174         589 :         EQUAL(pszCap, GDsCAddRelationship) ||
    1175         589 :         EQUAL(pszCap, GDsCDeleteRelationship) ||
    1176         589 :         EQUAL(pszCap, GDsCUpdateRelationship) ||
    1177         589 :         EQUAL(pszCap, ODsCEmulatedTransactions))
    1178             :     {
    1179          79 :         return eAccess == GA_Update;
    1180             :     }
    1181             : 
    1182         586 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
    1183         181 :         return TRUE;
    1184         405 :     else if (EQUAL(pszCap, ODsCZGeometries))
    1185         181 :         return TRUE;
    1186         224 :     else if (EQUAL(pszCap, ODsCCurveGeometries))
    1187         198 :         return TRUE;
    1188             : 
    1189          26 :     return FALSE;
    1190             : }
    1191             : 
    1192             : /***********************************************************************/
    1193             : /*                            GetLayer()                               */
    1194             : /***********************************************************************/
    1195             : 
    1196     1194650 : OGRLayer *OGROpenFileGDBDataSource::GetLayer(int iIndex)
    1197             : {
    1198     1194650 :     if (iIndex < 0 || iIndex >= static_cast<int>(m_apoLayers.size()))
    1199          16 :         return nullptr;
    1200     1194630 :     return m_apoLayers[iIndex].get();
    1201             : }
    1202             : 
    1203             : /***********************************************************************/
    1204             : /*                      BuildLayerFromName()                           */
    1205             : /***********************************************************************/
    1206             : 
    1207             : std::unique_ptr<OGROpenFileGDBLayer>
    1208         764 : OGROpenFileGDBDataSource::BuildLayerFromName(const char *pszName)
    1209             : {
    1210             : 
    1211         764 :     const auto oIter = m_osMapNameToIdx.find(pszName);
    1212         764 :     if (oIter != m_osMapNameToIdx.end())
    1213             :     {
    1214         164 :         const int idx = oIter->second;
    1215             :         CPLString osFilename(
    1216         164 :             CPLFormFilename(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         601 :     return nullptr;
    1224             : }
    1225             : 
    1226             : /***********************************************************************/
    1227             : /*                          GetLayerByName()                           */
    1228             : /***********************************************************************/
    1229             : 
    1230             : OGROpenFileGDBLayer *
    1231        3288 : OGROpenFileGDBDataSource::GetLayerByName(const char *pszName)
    1232             : {
    1233       34100 :     for (auto &poLayer : m_apoLayers)
    1234             :     {
    1235       33396 :         if (EQUAL(poLayer->GetName(), pszName))
    1236        2584 :             return poLayer.get();
    1237             :     }
    1238             : 
    1239        4485 :     for (auto &poLayer : m_apoHiddenLayers)
    1240             :     {
    1241        3782 :         if (EQUAL(poLayer->GetName(), pszName))
    1242           1 :             return poLayer.get();
    1243             :     }
    1244             : 
    1245        1406 :     auto poLayer = BuildLayerFromName(pszName);
    1246         703 :     if (poLayer)
    1247             :     {
    1248         103 :         m_apoHiddenLayers.emplace_back(std::move(poLayer));
    1249         103 :         return m_apoHiddenLayers.back().get();
    1250             :     }
    1251         600 :     return nullptr;
    1252             : }
    1253             : 
    1254             : /***********************************************************************/
    1255             : /*                          IsPrivateLayerName()                       */
    1256             : /***********************************************************************/
    1257             : 
    1258       10647 : bool OGROpenFileGDBDataSource::IsPrivateLayerName(const CPLString &osName)
    1259             : {
    1260       10647 :     const CPLString osLCTableName(CPLString(osName).tolower());
    1261             : 
    1262             :     // tables beginning with "GDB_" are private tables
    1263             :     // Also consider "VAT_" tables as private ones.
    1264       27215 :     return osLCTableName.size() >= 4 && (osLCTableName.substr(0, 4) == "gdb_" ||
    1265       27215 :                                          osLCTableName.substr(0, 4) == "vat_");
    1266             : }
    1267             : 
    1268             : /***********************************************************************/
    1269             : /*                          IsLayerPrivate()                           */
    1270             : /***********************************************************************/
    1271             : 
    1272          90 : bool OGROpenFileGDBDataSource::IsLayerPrivate(int iLayer) const
    1273             : {
    1274          90 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
    1275           0 :         return false;
    1276             : 
    1277          90 :     const std::string osName(m_apoLayers[iLayer]->GetName());
    1278          90 :     return IsPrivateLayerName(osName);
    1279             : }
    1280             : 
    1281             : /***********************************************************************/
    1282             : /*                           GetMetadata()                             */
    1283             : /***********************************************************************/
    1284             : 
    1285          27 : char **OGROpenFileGDBDataSource::GetMetadata(const char *pszDomain)
    1286             : {
    1287          27 :     if (pszDomain && EQUAL(pszDomain, "SUBDATASETS"))
    1288           7 :         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         158 :     virtual OGRFeatureDefn *GetLayerDefn() override
    1361             :     {
    1362         158 :         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 GetExtent(OGREnvelope *psExtent, int bForce) override
    1373             :     {
    1374          12 :         return poBaseLayer->GetExtent(psExtent, bForce);
    1375             :     }
    1376             : 
    1377          12 :     virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent,
    1378             :                              int bForce) override
    1379             :     {
    1380          12 :         return OGRLayer::GetExtent(iGeomField, psExtent, bForce);
    1381             :     }
    1382             : 
    1383             :     virtual GIntBig GetFeatureCount(int bForce) override;
    1384             : };
    1385             : 
    1386             : /***********************************************************************/
    1387             : /*                    OGROpenFileGDBSimpleSQLLayer()                   */
    1388             : /***********************************************************************/
    1389             : 
    1390          16 : OGROpenFileGDBSimpleSQLLayer::OGROpenFileGDBSimpleSQLLayer(
    1391             :     OGRLayer *poBaseLayerIn, FileGDBIterator *poIterIn, int nColumns,
    1392          16 :     const swq_col_def *pasColDefs, GIntBig nOffset, GIntBig nLimit)
    1393             :     : poBaseLayer(poBaseLayerIn), poIter(poIterIn), poFeatureDefn(nullptr),
    1394          16 :       m_nOffset(nOffset), m_nLimit(nLimit)
    1395             : {
    1396          16 :     if (nColumns == 1 && strcmp(pasColDefs[0].field_name, "*") == 0)
    1397             :     {
    1398          14 :         poFeatureDefn = poBaseLayer->GetLayerDefn();
    1399          14 :         poFeatureDefn->Reference();
    1400             :     }
    1401             :     else
    1402             :     {
    1403           2 :         poFeatureDefn = new OGRFeatureDefn(poBaseLayer->GetName());
    1404           2 :         poFeatureDefn->SetGeomType(poBaseLayer->GetGeomType());
    1405           2 :         poFeatureDefn->Reference();
    1406           2 :         if (poBaseLayer->GetGeomType() != wkbNone)
    1407             :         {
    1408           4 :             poFeatureDefn->GetGeomFieldDefn(0)->SetName(
    1409           2 :                 poBaseLayer->GetGeometryColumn());
    1410           2 :             poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
    1411           2 :                 poBaseLayer->GetSpatialRef());
    1412             :         }
    1413           6 :         for (int i = 0; i < nColumns; i++)
    1414             :         {
    1415           4 :             if (strcmp(pasColDefs[i].field_name, "*") == 0)
    1416             :             {
    1417           0 :                 for (int j = 0;
    1418           0 :                      j < poBaseLayer->GetLayerDefn()->GetFieldCount(); j++)
    1419           0 :                     poFeatureDefn->AddFieldDefn(
    1420           0 :                         poBaseLayer->GetLayerDefn()->GetFieldDefn(j));
    1421             :             }
    1422             :             else
    1423             :             {
    1424             :                 OGRFieldDefn *poFieldDefn =
    1425           8 :                     poBaseLayer->GetLayerDefn()->GetFieldDefn(
    1426           8 :                         poBaseLayer->GetLayerDefn()->GetFieldIndex(
    1427           4 :                             pasColDefs[i].field_name));
    1428           4 :                 CPLAssert(poFieldDefn != nullptr); /* already checked before */
    1429           4 :                 poFeatureDefn->AddFieldDefn(poFieldDefn);
    1430             :             }
    1431             :         }
    1432             :     }
    1433          16 :     SetDescription(poFeatureDefn->GetName());
    1434          16 :     OGROpenFileGDBSimpleSQLLayer::ResetReading();
    1435          16 : }
    1436             : 
    1437             : /***********************************************************************/
    1438             : /*                   ~OGROpenFileGDBSimpleSQLLayer()                   */
    1439             : /***********************************************************************/
    1440             : 
    1441          32 : OGROpenFileGDBSimpleSQLLayer::~OGROpenFileGDBSimpleSQLLayer()
    1442             : {
    1443          16 :     if (poFeatureDefn)
    1444             :     {
    1445          16 :         poFeatureDefn->Release();
    1446             :     }
    1447          16 :     delete poIter;
    1448          32 : }
    1449             : 
    1450             : /***********************************************************************/
    1451             : /*                          ResetReading()                             */
    1452             : /***********************************************************************/
    1453             : 
    1454         204 : void OGROpenFileGDBSimpleSQLLayer::ResetReading()
    1455             : {
    1456         204 :     poIter->Reset();
    1457         204 :     m_nSkipped = 0;
    1458         204 :     m_nIterated = 0;
    1459         204 : }
    1460             : 
    1461             : /***********************************************************************/
    1462             : /*                          GetFeature()                               */
    1463             : /***********************************************************************/
    1464             : 
    1465         682 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetFeature(GIntBig nFeatureId)
    1466             : {
    1467         682 :     OGRFeature *poSrcFeature = poBaseLayer->GetFeature(nFeatureId);
    1468         682 :     if (poSrcFeature == nullptr)
    1469           9 :         return nullptr;
    1470             : 
    1471         673 :     if (poFeatureDefn == poBaseLayer->GetLayerDefn())
    1472         528 :         return poSrcFeature;
    1473             :     else
    1474             :     {
    1475         145 :         OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
    1476         145 :         poFeature->SetFrom(poSrcFeature);
    1477         145 :         poFeature->SetFID(poSrcFeature->GetFID());
    1478         145 :         delete poSrcFeature;
    1479         145 :         return poFeature;
    1480             :     }
    1481             : }
    1482             : 
    1483             : /***********************************************************************/
    1484             : /*                         GetNextFeature()                            */
    1485             : /***********************************************************************/
    1486             : 
    1487         735 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetNextFeature()
    1488             : {
    1489             :     while (true)
    1490             :     {
    1491         735 :         if (m_nLimit >= 0 && m_nIterated == m_nLimit)
    1492           1 :             return nullptr;
    1493             : 
    1494         734 :         const int64_t nRow = poIter->GetNextRowSortedByValue();
    1495         734 :         if (nRow < 0)
    1496          73 :             return nullptr;
    1497         661 :         OGRFeature *poFeature = GetFeature(nRow + 1);
    1498         661 :         if (poFeature == nullptr)
    1499           0 :             return nullptr;
    1500         661 :         if (m_nOffset >= 0 && m_nSkipped < m_nOffset)
    1501             :         {
    1502         343 :             delete poFeature;
    1503         343 :             m_nSkipped++;
    1504         343 :             continue;
    1505             :         }
    1506         318 :         m_nIterated++;
    1507             : 
    1508         746 :         if ((m_poFilterGeom == nullptr ||
    1509         603 :              FilterGeometry(poFeature->GetGeometryRef())) &&
    1510         285 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
    1511             :         {
    1512         235 :             return poFeature;
    1513             :         }
    1514             : 
    1515          83 :         delete poFeature;
    1516         426 :     }
    1517             : }
    1518             : 
    1519             : /***********************************************************************/
    1520             : /*                         GetFeatureCount()                           */
    1521             : /***********************************************************************/
    1522             : 
    1523          51 : GIntBig OGROpenFileGDBSimpleSQLLayer::GetFeatureCount(int bForce)
    1524             : {
    1525             : 
    1526             :     /* No filter */
    1527          51 :     if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
    1528             :     {
    1529          33 :         GIntBig nRowCount = poIter->GetRowCount();
    1530          33 :         if (m_nOffset > 0)
    1531             :         {
    1532           3 :             if (m_nOffset <= nRowCount)
    1533           2 :                 nRowCount -= m_nOffset;
    1534             :             else
    1535           1 :                 nRowCount = 0;
    1536             :         }
    1537          33 :         if (m_nLimit >= 0 && nRowCount > m_nLimit)
    1538           5 :             nRowCount = m_nLimit;
    1539          33 :         return nRowCount;
    1540             :     }
    1541             : 
    1542          18 :     return OGRLayer::GetFeatureCount(bForce);
    1543             : }
    1544             : 
    1545             : /***********************************************************************/
    1546             : /*                         TestCapability()                            */
    1547             : /***********************************************************************/
    1548             : 
    1549          96 : int OGROpenFileGDBSimpleSQLLayer::TestCapability(const char *pszCap)
    1550             : {
    1551             : 
    1552          96 :     if (EQUAL(pszCap, OLCFastFeatureCount))
    1553             :     {
    1554           0 :         return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
    1555             :     }
    1556          96 :     else if (EQUAL(pszCap, OLCFastGetExtent))
    1557             :     {
    1558           6 :         return TRUE;
    1559             :     }
    1560          90 :     else if (EQUAL(pszCap, OLCRandomRead))
    1561             :     {
    1562           0 :         return TRUE;
    1563             :     }
    1564          90 :     else if (EQUAL(pszCap, OLCStringsAsUTF8))
    1565             :     {
    1566          27 :         return TRUE; /* ? */
    1567             :     }
    1568             : 
    1569          63 :     return FALSE;
    1570             : }
    1571             : 
    1572             : /***********************************************************************/
    1573             : /*                            ExecuteSQL()                             */
    1574             : /***********************************************************************/
    1575             : 
    1576         793 : OGRLayer *OGROpenFileGDBDataSource::ExecuteSQL(const char *pszSQLCommand,
    1577             :                                                OGRGeometry *poSpatialFilter,
    1578             :                                                const char *pszDialect)
    1579             : {
    1580             : 
    1581             :     /* -------------------------------------------------------------------- */
    1582             :     /*      Special case GetLayerDefinition                                 */
    1583             :     /* -------------------------------------------------------------------- */
    1584         793 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
    1585             :     {
    1586          32 :         const char *pszLayerName =
    1587             :             pszSQLCommand + strlen("GetLayerDefinition ");
    1588          32 :         auto poLayer = GetLayerByName(pszLayerName);
    1589          32 :         if (poLayer)
    1590             :         {
    1591             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1592          31 :                 "LayerDefinition", poLayer->GetXMLDefinition().c_str());
    1593          31 :             return poRet;
    1594             :         }
    1595           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1596             :                  pszLayerName);
    1597           1 :         return nullptr;
    1598             :     }
    1599             : 
    1600             :     /* -------------------------------------------------------------------- */
    1601             :     /*      Special case GetLayerMetadata                                   */
    1602             :     /* -------------------------------------------------------------------- */
    1603         761 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
    1604             :     {
    1605          21 :         const char *pszLayerName = pszSQLCommand + strlen("GetLayerMetadata ");
    1606          21 :         auto poLayer = GetLayerByName(pszLayerName);
    1607          21 :         if (poLayer)
    1608             :         {
    1609             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1610          20 :                 "LayerMetadata", poLayer->GetXMLDocumentation().c_str());
    1611          20 :             return poRet;
    1612             :         }
    1613           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1614             :                  pszLayerName);
    1615           1 :         return nullptr;
    1616             :     }
    1617             : 
    1618             :     /* -------------------------------------------------------------------- */
    1619             :     /*      Special case GetLayerAttrIndexUse (only for debugging purposes) */
    1620             :     /* -------------------------------------------------------------------- */
    1621         740 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerAttrIndexUse "))
    1622             :     {
    1623             :         auto poLayer =
    1624         141 :             GetLayerByName(pszSQLCommand + strlen("GetLayerAttrIndexUse "));
    1625         141 :         if (poLayer)
    1626             :         {
    1627             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1628             :                 "LayerAttrIndexUse",
    1629         141 :                 CPLSPrintf("%d", poLayer->GetAttrIndexUse()));
    1630         141 :             return poRet;
    1631             :         }
    1632             : 
    1633           0 :         return nullptr;
    1634             :     }
    1635             : 
    1636             :     /* -------------------------------------------------------------------- */
    1637             :     /*      Special case GetLayerSpatialIndexState (only for debugging purposes)
    1638             :      */
    1639             :     /* -------------------------------------------------------------------- */
    1640         599 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerSpatialIndexState "))
    1641             :     {
    1642          12 :         auto poLayer = GetLayerByName(pszSQLCommand +
    1643             :                                       strlen("GetLayerSpatialIndexState "));
    1644          12 :         if (poLayer)
    1645             :         {
    1646             :             OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1647             :                 "LayerSpatialIndexState",
    1648          12 :                 CPLSPrintf("%d", poLayer->GetSpatialIndexState()));
    1649          12 :             return poRet;
    1650             :         }
    1651             : 
    1652           0 :         return nullptr;
    1653             :     }
    1654             : 
    1655             :     /* -------------------------------------------------------------------- */
    1656             :     /*      Special case GetLastSQLUsedOptimizedImplementation (only for
    1657             :      * debugging purposes) */
    1658             :     /* -------------------------------------------------------------------- */
    1659         587 :     if (EQUAL(pszSQLCommand, "GetLastSQLUsedOptimizedImplementation"))
    1660             :     {
    1661             :         OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
    1662             :             "GetLastSQLUsedOptimizedImplementation",
    1663          46 :             CPLSPrintf("%d",
    1664          23 :                        static_cast<int>(bLastSQLUsedOptimizedImplementation)));
    1665          23 :         return poRet;
    1666             :     }
    1667             : 
    1668             :     /* -------------------------------------------------------------------- */
    1669             :     /*      Special case for "CREATE SPATIAL INDEX ON "                     */
    1670             :     /* -------------------------------------------------------------------- */
    1671         564 :     if (STARTS_WITH_CI(pszSQLCommand, "CREATE SPATIAL INDEX ON "))
    1672             :     {
    1673           0 :         const char *pszLayerName =
    1674             :             pszSQLCommand + strlen("CREATE SPATIAL INDEX ON ");
    1675           0 :         auto poLayer = GetLayerByName(pszLayerName);
    1676           0 :         if (poLayer)
    1677             :         {
    1678           0 :             poLayer->CreateSpatialIndex();
    1679             :         }
    1680           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1681             :                  pszLayerName);
    1682           0 :         return nullptr;
    1683             :     }
    1684             : 
    1685             :     /* -------------------------------------------------------------------- */
    1686             :     /*      Special case for "CREATE INDEX idx_name ON table_name(col_name) */
    1687             :     /* -------------------------------------------------------------------- */
    1688         564 :     if (STARTS_WITH_CI(pszSQLCommand, "CREATE INDEX "))
    1689             :     {
    1690          42 :         CPLString osSQL(pszSQLCommand);
    1691          21 :         const auto nPosON = osSQL.ifind(" ON ");
    1692          21 :         if (nPosON != std::string::npos)
    1693             :         {
    1694             :             const std::string osIdxName = osSQL.substr(
    1695          21 :                 strlen("CREATE INDEX "), nPosON - strlen("CREATE INDEX "));
    1696          21 :             const std::string afterOn = osSQL.substr(nPosON + strlen(" ON "));
    1697          21 :             const auto nOpenParPos = afterOn.find('(');
    1698          21 :             if (nOpenParPos != std::string::npos && afterOn.back() == ')')
    1699             :             {
    1700          42 :                 const std::string osLayerName = afterOn.substr(0, nOpenParPos);
    1701             :                 const std::string osExpression = afterOn.substr(
    1702          42 :                     nOpenParPos + 1, afterOn.size() - nOpenParPos - 2);
    1703          21 :                 if (osExpression.find(',') != std::string::npos)
    1704             :                 {
    1705           1 :                     CPLError(
    1706             :                         CE_Failure, CPLE_NotSupported,
    1707             :                         "Creation of multiple-column indices is not supported");
    1708           1 :                     return nullptr;
    1709             :                 }
    1710          20 :                 auto poLayer = GetLayerByName(osLayerName.c_str());
    1711          20 :                 if (poLayer)
    1712             :                 {
    1713          19 :                     poLayer->CreateIndex(osIdxName, osExpression);
    1714          19 :                     return nullptr;
    1715             :                 }
    1716           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1717             :                          osLayerName.c_str());
    1718           1 :                 return nullptr;
    1719             :             }
    1720             :         }
    1721           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1722             :                  "Bad syntax. Expected CREATE INDEX idx_name ON "
    1723             :                  "table_name(col_name)");
    1724           0 :         return nullptr;
    1725             :     }
    1726             : 
    1727             :     /* -------------------------------------------------------------------- */
    1728             :     /*      Special case for "RECOMPUTE EXTENT ON "                         */
    1729             :     /* -------------------------------------------------------------------- */
    1730         543 :     if (STARTS_WITH_CI(pszSQLCommand, "RECOMPUTE EXTENT ON "))
    1731             :     {
    1732           3 :         const char *pszLayerName =
    1733             :             pszSQLCommand + strlen("RECOMPUTE EXTENT ON ");
    1734           3 :         auto poLayer = GetLayerByName(pszLayerName);
    1735           3 :         if (poLayer)
    1736             :         {
    1737           2 :             poLayer->RecomputeExtent();
    1738             :         }
    1739             :         else
    1740             :         {
    1741           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1742             :                      pszLayerName);
    1743             :         }
    1744           3 :         return nullptr;
    1745             :     }
    1746             : 
    1747             :     /* -------------------------------------------------------------------- */
    1748             :     /*      Special case for "DELLAYER:"                                    */
    1749             :     /* -------------------------------------------------------------------- */
    1750         540 :     if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
    1751             :     {
    1752           3 :         const char *pszLayerName = pszSQLCommand + strlen("DELLAYER:");
    1753           5 :         for (int i = 0; i < static_cast<int>(m_apoLayers.size()); ++i)
    1754             :         {
    1755           3 :             if (strcmp(pszLayerName, m_apoLayers[i]->GetName()) == 0)
    1756             :             {
    1757           1 :                 DeleteLayer(i);
    1758           1 :                 return nullptr;
    1759             :             }
    1760             :         }
    1761           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1762             :                  pszLayerName);
    1763           2 :         return nullptr;
    1764             :     }
    1765             : 
    1766             :     /* -------------------------------------------------------------------- */
    1767             :     /*      Special case for "CHECK_FREELIST_CONSISTENCY:"                  */
    1768             :     /* -------------------------------------------------------------------- */
    1769         537 :     if (STARTS_WITH_CI(pszSQLCommand, "CHECK_FREELIST_CONSISTENCY:"))
    1770             :     {
    1771           8 :         const char *pszLayerName =
    1772             :             pszSQLCommand + strlen("CHECK_FREELIST_CONSISTENCY:");
    1773           8 :         auto poLayer = GetLayerByName(pszLayerName);
    1774           8 :         if (poLayer)
    1775             :         {
    1776             :             return new OGROpenFileGDBSingleFeatureLayer(
    1777             :                 "result",
    1778          16 :                 CPLSPrintf("%d", static_cast<int>(
    1779           8 :                                      poLayer->CheckFreeListConsistency())));
    1780             :         }
    1781           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1782             :                  pszLayerName);
    1783           0 :         return nullptr;
    1784             :     }
    1785             : 
    1786             :     /* -------------------------------------------------------------------- */
    1787             :     /*      Special case for "REPACK"                                       */
    1788             :     /* -------------------------------------------------------------------- */
    1789         529 :     if (EQUAL(pszSQLCommand, "REPACK"))
    1790             :     {
    1791           3 :         bool bSuccess = true;
    1792           6 :         for (auto &poLayer : m_apoLayers)
    1793             :         {
    1794           3 :             if (!poLayer->Repack())
    1795           0 :                 bSuccess = false;
    1796             :         }
    1797             :         return new OGROpenFileGDBSingleFeatureLayer(
    1798           3 :             "result", bSuccess ? "true" : "false");
    1799             :     }
    1800         526 :     else if (STARTS_WITH(pszSQLCommand, "REPACK "))
    1801             :     {
    1802           2 :         const char *pszLayerName = pszSQLCommand + strlen("REPACK ");
    1803           2 :         auto poLayer = GetLayerByName(pszLayerName);
    1804           2 :         if (poLayer)
    1805             :         {
    1806           1 :             const bool bSuccess = poLayer->Repack();
    1807             :             return new OGROpenFileGDBSingleFeatureLayer(
    1808           1 :                 "result", bSuccess ? "true" : "false");
    1809             :         }
    1810           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
    1811             :                  pszLayerName);
    1812           1 :         return nullptr;
    1813             :     }
    1814             : 
    1815         524 :     bLastSQLUsedOptimizedImplementation = false;
    1816             : 
    1817             :     /* -------------------------------------------------------------------- */
    1818             :     /*      Special cases for SQL optimizations                             */
    1819             :     /* -------------------------------------------------------------------- */
    1820         516 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT ") &&
    1821          39 :         (pszDialect == nullptr || EQUAL(pszDialect, "") ||
    1822        1040 :          EQUAL(pszDialect, "OGRSQL")) &&
    1823         516 :         CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
    1824             :     {
    1825         516 :         swq_select oSelect;
    1826         516 :         if (oSelect.preparse(pszSQLCommand) != CE_None)
    1827           1 :             return nullptr;
    1828             : 
    1829             :         /* --------------------------------------------------------------------
    1830             :          */
    1831             :         /*      MIN/MAX/SUM/AVG/COUNT optimization */
    1832             :         /* --------------------------------------------------------------------
    1833             :          */
    1834         515 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1835         515 :             oSelect.table_count == 1 && oSelect.order_specs == 0 &&
    1836         488 :             oSelect.query_mode != SWQM_DISTINCT_LIST &&
    1837         488 :             oSelect.where_expr == nullptr)
    1838             :         {
    1839             :             OGROpenFileGDBLayer *poLayer =
    1840         618 :                 reinterpret_cast<OGROpenFileGDBLayer *>(
    1841         309 :                     GetLayerByName(oSelect.table_defs[0].table_name));
    1842         309 :             if (poLayer)
    1843             :             {
    1844         308 :                 OGRMemLayer *poMemLayer = nullptr;
    1845             : 
    1846         308 :                 int i = 0;  // Used after for.
    1847         366 :                 for (; i < oSelect.result_columns(); i++)
    1848             :                 {
    1849         330 :                     swq_col_func col_func = oSelect.column_defs[i].col_func;
    1850         509 :                     if (!(col_func == SWQCF_MIN || col_func == SWQCF_MAX ||
    1851         185 :                           col_func == SWQCF_COUNT || col_func == SWQCF_AVG ||
    1852             :                           col_func == SWQCF_SUM))
    1853         272 :                         break;
    1854             : 
    1855         152 :                     if (oSelect.column_defs[i].field_name == nullptr)
    1856           0 :                         break;
    1857         152 :                     if (oSelect.column_defs[i].distinct_flag)
    1858           0 :                         break;
    1859         152 :                     if (oSelect.column_defs[i].target_type != SWQ_OTHER)
    1860           0 :                         break;
    1861             : 
    1862         152 :                     int idx = poLayer->GetLayerDefn()->GetFieldIndex(
    1863         152 :                         oSelect.column_defs[i].field_name);
    1864         152 :                     if (idx < 0)
    1865          89 :                         break;
    1866             : 
    1867             :                     OGRFieldDefn *poFieldDefn =
    1868          63 :                         poLayer->GetLayerDefn()->GetFieldDefn(idx);
    1869             : 
    1870          64 :                     if (col_func == SWQCF_SUM &&
    1871           1 :                         poFieldDefn->GetType() == OFTDateTime)
    1872           0 :                         break;
    1873             : 
    1874          63 :                     int eOutOGRType = -1;
    1875          63 :                     const OGRField *psField = nullptr;
    1876             :                     OGRField sField;
    1877          63 :                     if (col_func == SWQCF_MIN || col_func == SWQCF_MAX)
    1878             :                     {
    1879          55 :                         psField = poLayer->GetMinMaxValue(
    1880             :                             poFieldDefn, col_func == SWQCF_MIN, eOutOGRType);
    1881          55 :                         if (eOutOGRType < 0)
    1882           5 :                             break;
    1883             :                     }
    1884             :                     else
    1885             :                     {
    1886           8 :                         double dfMin = 0.0;
    1887           8 :                         double dfMax = 0.0;
    1888           8 :                         int nCount = 0;
    1889           8 :                         double dfSum = 0.0;
    1890             : 
    1891           8 :                         if (!poLayer->GetMinMaxSumCount(poFieldDefn, dfMin,
    1892             :                                                         dfMax, dfSum, nCount))
    1893           0 :                             break;
    1894           8 :                         psField = &sField;
    1895           8 :                         if (col_func == SWQCF_AVG)
    1896             :                         {
    1897           6 :                             if (nCount == 0)
    1898             :                             {
    1899           1 :                                 eOutOGRType = OFTReal;
    1900           1 :                                 psField = nullptr;
    1901             :                             }
    1902             :                             else
    1903             :                             {
    1904           5 :                                 if (poFieldDefn->GetType() == OFTDateTime)
    1905             :                                 {
    1906           1 :                                     eOutOGRType = OFTDateTime;
    1907           1 :                                     FileGDBDoubleDateToOGRDate(dfSum / nCount,
    1908             :                                                                false, &sField);
    1909             :                                 }
    1910             :                                 else
    1911             :                                 {
    1912           4 :                                     eOutOGRType = OFTReal;
    1913           4 :                                     sField.Real = dfSum / nCount;
    1914             :                                 }
    1915             :                             }
    1916             :                         }
    1917           2 :                         else if (col_func == SWQCF_COUNT)
    1918             :                         {
    1919           1 :                             sField.Integer = nCount;
    1920           1 :                             eOutOGRType = OFTInteger;
    1921             :                         }
    1922             :                         else
    1923             :                         {
    1924           1 :                             sField.Real = dfSum;
    1925           1 :                             eOutOGRType = OFTReal;
    1926             :                         }
    1927             :                     }
    1928             : 
    1929          58 :                     if (poMemLayer == nullptr)
    1930             :                     {
    1931          36 :                         poMemLayer =
    1932          36 :                             new OGRMemLayer("SELECT", nullptr, wkbNone);
    1933             :                         OGRFeature *poFeature =
    1934          36 :                             new OGRFeature(poMemLayer->GetLayerDefn());
    1935          36 :                         CPL_IGNORE_RET_VAL(
    1936          36 :                             poMemLayer->CreateFeature(poFeature));
    1937          36 :                         delete poFeature;
    1938             :                     }
    1939             : 
    1940             :                     const char *pszMinMaxFieldName =
    1941         102 :                         CPLSPrintf("%s_%s",
    1942             :                                    (col_func == SWQCF_MIN)   ? "MIN"
    1943          52 :                                    : (col_func == SWQCF_MAX) ? "MAX"
    1944          10 :                                    : (col_func == SWQCF_AVG) ? "AVG"
    1945           2 :                                    : (col_func == SWQCF_SUM) ? "SUM"
    1946             :                                                              : "COUNT",
    1947          58 :                                    oSelect.column_defs[i].field_name);
    1948             :                     OGRFieldDefn oFieldDefn(
    1949             :                         pszMinMaxFieldName,
    1950         116 :                         static_cast<OGRFieldType>(eOutOGRType));
    1951          58 :                     poMemLayer->CreateField(&oFieldDefn);
    1952          58 :                     if (psField != nullptr)
    1953             :                     {
    1954          55 :                         OGRFeature *poFeature = poMemLayer->GetFeature(0);
    1955          55 :                         poFeature->SetField(oFieldDefn.GetNameRef(), psField);
    1956          55 :                         CPL_IGNORE_RET_VAL(poMemLayer->SetFeature(poFeature));
    1957          55 :                         delete poFeature;
    1958             :                     }
    1959             :                 }
    1960         308 :                 if (i != oSelect.result_columns())
    1961             :                 {
    1962         272 :                     delete poMemLayer;
    1963             :                 }
    1964             :                 else
    1965             :                 {
    1966          36 :                     CPLDebug(
    1967             :                         "OpenFileGDB",
    1968             :                         "Using optimized MIN/MAX/SUM/AVG/COUNT implementation");
    1969          36 :                     bLastSQLUsedOptimizedImplementation = true;
    1970          36 :                     return poMemLayer;
    1971             :                 }
    1972             :             }
    1973             :         }
    1974             : 
    1975             :         /* --------------------------------------------------------------------
    1976             :          */
    1977             :         /*      ORDER BY optimization */
    1978             :         /* --------------------------------------------------------------------
    1979             :          */
    1980         479 :         if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
    1981         479 :             oSelect.table_count == 1 && oSelect.order_specs == 1 &&
    1982          26 :             oSelect.query_mode != SWQM_DISTINCT_LIST)
    1983             :         {
    1984             :             OGROpenFileGDBLayer *poLayer =
    1985          50 :                 reinterpret_cast<OGROpenFileGDBLayer *>(
    1986          25 :                     GetLayerByName(oSelect.table_defs[0].table_name));
    1987          50 :             if (poLayer != nullptr &&
    1988          25 :                 poLayer->HasIndexForField(oSelect.order_defs[0].field_name))
    1989             :             {
    1990          23 :                 OGRErr eErr = OGRERR_NONE;
    1991          23 :                 if (oSelect.where_expr != nullptr)
    1992             :                 {
    1993             :                     /* The where must be a simple comparison on the column */
    1994             :                     /* that is used for ordering */
    1995          12 :                     if (oSelect.where_expr->eNodeType == SNT_OPERATION &&
    1996           4 :                         OGROpenFileGDBIsComparisonOp(
    1997           4 :                             oSelect.where_expr->nOperation) &&
    1998           3 :                         oSelect.where_expr->nOperation != SWQ_NE &&
    1999           3 :                         oSelect.where_expr->nSubExprCount == 2 &&
    2000           3 :                         (oSelect.where_expr->papoSubExpr[0]->eNodeType ==
    2001           0 :                              SNT_COLUMN ||
    2002           0 :                          oSelect.where_expr->papoSubExpr[0]->eNodeType ==
    2003           3 :                              SNT_CONSTANT) &&
    2004           3 :                         oSelect.where_expr->papoSubExpr[0]->field_type ==
    2005           3 :                             SWQ_STRING &&
    2006           3 :                         EQUAL(oSelect.where_expr->papoSubExpr[0]->string_value,
    2007           8 :                               oSelect.order_defs[0].field_name) &&
    2008           2 :                         oSelect.where_expr->papoSubExpr[1]->eNodeType ==
    2009             :                             SNT_CONSTANT)
    2010             :                     {
    2011             :                         /* ok */
    2012             :                     }
    2013             :                     else
    2014           2 :                         eErr = OGRERR_FAILURE;
    2015             :                 }
    2016          23 :                 if (eErr == OGRERR_NONE)
    2017             :                 {
    2018          21 :                     int i = 0;  // Used after for.
    2019          40 :                     for (; i < oSelect.result_columns(); i++)
    2020             :                     {
    2021          23 :                         if (oSelect.column_defs[i].col_func != SWQCF_NONE)
    2022           1 :                             break;
    2023          22 :                         if (oSelect.column_defs[i].field_name == nullptr)
    2024           0 :                             break;
    2025          22 :                         if (oSelect.column_defs[i].distinct_flag)
    2026           0 :                             break;
    2027          22 :                         if (oSelect.column_defs[i].target_type != SWQ_OTHER)
    2028           1 :                             break;
    2029          21 :                         if (strcmp(oSelect.column_defs[i].field_name, "*") !=
    2030          27 :                                 0 &&
    2031          12 :                             poLayer->GetLayerDefn()->GetFieldIndex(
    2032           6 :                                 oSelect.column_defs[i].field_name) < 0)
    2033           2 :                             break;
    2034             :                     }
    2035          21 :                     if (i != oSelect.result_columns())
    2036           4 :                         eErr = OGRERR_FAILURE;
    2037             :                 }
    2038          23 :                 if (eErr == OGRERR_NONE)
    2039             :                 {
    2040          17 :                     int op = -1;
    2041          17 :                     swq_expr_node *poValue = nullptr;
    2042          17 :                     if (oSelect.where_expr != nullptr)
    2043             :                     {
    2044           2 :                         op = oSelect.where_expr->nOperation;
    2045           2 :                         poValue = oSelect.where_expr->papoSubExpr[1];
    2046             :                     }
    2047             : 
    2048          34 :                     FileGDBIterator *poIter = poLayer->BuildIndex(
    2049          17 :                         oSelect.order_defs[0].field_name,
    2050          17 :                         oSelect.order_defs[0].ascending_flag, op, poValue);
    2051             : 
    2052             :                     /* Check that they are no NULL values */
    2053          32 :                     if (oSelect.where_expr == nullptr && poIter != nullptr &&
    2054          15 :                         poIter->GetRowCount() !=
    2055          15 :                             poLayer->GetFeatureCount(FALSE))
    2056             :                     {
    2057           1 :                         delete poIter;
    2058           1 :                         poIter = nullptr;
    2059             :                     }
    2060             : 
    2061          17 :                     if (poIter != nullptr)
    2062             :                     {
    2063          16 :                         CPLDebug("OpenFileGDB",
    2064             :                                  "Using OGROpenFileGDBSimpleSQLLayer");
    2065          16 :                         bLastSQLUsedOptimizedImplementation = true;
    2066             :                         return new OGROpenFileGDBSimpleSQLLayer(
    2067          16 :                             poLayer, poIter, oSelect.result_columns(),
    2068          16 :                             oSelect.column_defs.data(), oSelect.offset,
    2069          16 :                             oSelect.limit);
    2070             :                     }
    2071             :                 }
    2072             :             }
    2073             :         }
    2074             :     }
    2075             : 
    2076         471 :     return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
    2077             : }
    2078             : 
    2079             : /***********************************************************************/
    2080             : /*                           ReleaseResultSet()                        */
    2081             : /***********************************************************************/
    2082             : 
    2083         743 : void OGROpenFileGDBDataSource::ReleaseResultSet(OGRLayer *poResultsSet)
    2084             : {
    2085         743 :     delete poResultsSet;
    2086         743 : }
    2087             : 
    2088             : /***********************************************************************/
    2089             : /*                           GetFileList()                             */
    2090             : /***********************************************************************/
    2091             : 
    2092           8 : char **OGROpenFileGDBDataSource::GetFileList()
    2093             : {
    2094           8 :     int nInterestTable = -1;
    2095           8 :     const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
    2096          16 :     CPLString osFilenameRadix;
    2097           8 :     unsigned int unInterestTable = 0;
    2098          16 :     if (strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
    2099           8 :         pszFilenameWithoutPath[0] == 'a' &&
    2100           0 :         sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
    2101             :     {
    2102           0 :         nInterestTable = static_cast<int>(unInterestTable);
    2103           0 :         osFilenameRadix = CPLSPrintf("a%08x.", nInterestTable);
    2104             :     }
    2105             : 
    2106           8 :     char **papszFiles = VSIReadDir(m_osDirName);
    2107          16 :     CPLStringList osStringList;
    2108           8 :     char **papszIter = papszFiles;
    2109        1148 :     for (; papszIter != nullptr && *papszIter != nullptr; papszIter++)
    2110             :     {
    2111        1140 :         if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
    2112           4 :             continue;
    2113        1136 :         if (osFilenameRadix.empty() ||
    2114           0 :             strncmp(*papszIter, osFilenameRadix, osFilenameRadix.size()) == 0)
    2115             :         {
    2116             :             osStringList.AddString(
    2117        1136 :                 CPLFormFilename(m_osDirName, *papszIter, nullptr));
    2118             :         }
    2119             :     }
    2120           8 :     CSLDestroy(papszFiles);
    2121          16 :     return osStringList.StealList();
    2122             : }
    2123             : 
    2124             : /************************************************************************/
    2125             : /*                           BuildSRS()                                 */
    2126             : /************************************************************************/
    2127             : 
    2128             : OGRSpatialReference *
    2129        2794 : OGROpenFileGDBDataSource::BuildSRS(const CPLXMLNode *psInfo)
    2130             : {
    2131             :     const char *pszWKT =
    2132        2794 :         CPLGetXMLValue(psInfo, "SpatialReference.WKT", nullptr);
    2133             :     const int nWKID =
    2134        2794 :         atoi(CPLGetXMLValue(psInfo, "SpatialReference.WKID", "0"));
    2135             :     // The concept of LatestWKID is explained in
    2136             :     // https://support.esri.com/en/technical-article/000013950
    2137             :     int nLatestWKID =
    2138        2794 :         atoi(CPLGetXMLValue(psInfo, "SpatialReference.LatestWKID", "0"));
    2139             : 
    2140        2794 :     std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser> poSRS;
    2141        2794 :     if (nWKID > 0 || nLatestWKID > 0)
    2142             :     {
    2143             :         const auto ImportFromCode =
    2144        2384 :             [](OGRSpatialReference &oSRS, int nLatestCode, int nCode)
    2145             :         {
    2146        2384 :             bool bSuccess = false;
    2147        2384 :             CPLErrorStateBackuper oQuietError(CPLQuietErrorHandler);
    2148             : 
    2149             :             // Try first with nLatestWKID as there is a higher chance it is a
    2150             :             // EPSG code and not an ESRI one.
    2151        2384 :             if (nLatestCode > 0)
    2152             :             {
    2153         122 :                 if (nLatestCode > 32767)
    2154             :                 {
    2155           1 :                     if (oSRS.SetFromUserInput(
    2156           1 :                             CPLSPrintf("ESRI:%d", nLatestCode)) == OGRERR_NONE)
    2157             :                     {
    2158           0 :                         bSuccess = true;
    2159             :                     }
    2160             :                 }
    2161         121 :                 else if (oSRS.importFromEPSG(nLatestCode) == OGRERR_NONE)
    2162             :                 {
    2163         121 :                     bSuccess = true;
    2164             :                 }
    2165         122 :                 if (!bSuccess)
    2166             :                 {
    2167           1 :                     CPLDebug("OpenFileGDB", "Cannot import SRID %d",
    2168             :                              nLatestCode);
    2169             :                 }
    2170             :             }
    2171        2384 :             if (!bSuccess && nCode > 0)
    2172             :             {
    2173        2263 :                 if (nCode > 32767)
    2174             :                 {
    2175           1 :                     if (oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nCode)) ==
    2176             :                         OGRERR_NONE)
    2177             :                     {
    2178           0 :                         bSuccess = true;
    2179             :                     }
    2180             :                 }
    2181        2262 :                 else if (oSRS.importFromEPSG(nCode) == OGRERR_NONE)
    2182             :                 {
    2183        2262 :                     bSuccess = true;
    2184             :                 }
    2185        2263 :                 if (!bSuccess)
    2186             :                 {
    2187           1 :                     CPLDebug("OpenFileGDB", "Cannot import SRID %d", nCode);
    2188             :                 }
    2189             :             }
    2190             : 
    2191        4768 :             return bSuccess;
    2192             :         };
    2193             : 
    2194             :         poSRS =
    2195        4766 :             std::unique_ptr<OGRSpatialReference, OGRSpatialReferenceReleaser>(
    2196        4766 :                 new OGRSpatialReference());
    2197        2383 :         poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2198        2383 :         if (!ImportFromCode(*poSRS.get(), nLatestWKID, nWKID))
    2199             :         {
    2200           0 :             poSRS.reset();
    2201             :         }
    2202             :         else
    2203             :         {
    2204        2383 :             const int nLatestVCSWKID = atoi(
    2205             :                 CPLGetXMLValue(psInfo, "SpatialReference.LatestVCSWKID", "0"));
    2206             :             const int nVCSWKID =
    2207        2383 :                 atoi(CPLGetXMLValue(psInfo, "SpatialReference.VCSWKID", "0"));
    2208        2383 :             if (nVCSWKID > 0 || nLatestVCSWKID > 0)
    2209             :             {
    2210             :                 auto poVertSRS = std::unique_ptr<OGRSpatialReference,
    2211             :                                                  OGRSpatialReferenceReleaser>(
    2212           2 :                     new OGRSpatialReference());
    2213           1 :                 if (ImportFromCode(*poVertSRS.get(), nLatestVCSWKID, nVCSWKID))
    2214             :                 {
    2215             :                     auto poCompoundSRS =
    2216             :                         std::unique_ptr<OGRSpatialReference,
    2217             :                                         OGRSpatialReferenceReleaser>(
    2218           0 :                             new OGRSpatialReference());
    2219           0 :                     if (poCompoundSRS->SetCompoundCS(
    2220           0 :                             std::string(poSRS->GetName())
    2221           0 :                                 .append(" + ")
    2222           0 :                                 .append(poVertSRS->GetName())
    2223             :                                 .c_str(),
    2224           0 :                             poSRS.get(), poVertSRS.get()) == OGRERR_NONE)
    2225             :                     {
    2226           0 :                         poCompoundSRS->SetAxisMappingStrategy(
    2227             :                             OAMS_TRADITIONAL_GIS_ORDER);
    2228           0 :                         poSRS = std::move(poCompoundSRS);
    2229             :                     }
    2230             :                 }
    2231           2 :                 if (!poSRS->IsCompound() &&
    2232           1 :                     !(pszWKT != nullptr && pszWKT[0] != '{'))
    2233             :                 {
    2234           0 :                     poSRS.reset();
    2235             :                 }
    2236             :             }
    2237             :         }
    2238             :     }
    2239        7560 :     if (pszWKT != nullptr && pszWKT[0] != '{' &&
    2240        2384 :         (poSRS == nullptr ||
    2241        2382 :          (strstr(pszWKT, "VERTCS") && !poSRS->IsCompound())))
    2242             :     {
    2243           3 :         poSRS.reset(BuildSRS(pszWKT));
    2244             :     }
    2245        5588 :     return poSRS.release();
    2246             : }
    2247             : 
    2248             : /************************************************************************/
    2249             : /*                           BuildSRS()                                 */
    2250             : /************************************************************************/
    2251             : 
    2252         385 : OGRSpatialReference *OGROpenFileGDBDataSource::BuildSRS(const char *pszWKT)
    2253             : {
    2254         385 :     std::shared_ptr<OGRSpatialReference> poSharedObj;
    2255         385 :     m_oCacheWKTToSRS.tryGet(pszWKT, poSharedObj);
    2256         385 :     if (poSharedObj)
    2257         199 :         return poSharedObj->Clone();
    2258             : 
    2259         186 :     OGRSpatialReference *poSRS = new OGRSpatialReference();
    2260         186 :     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2261         186 :     if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
    2262             :     {
    2263           0 :         delete poSRS;
    2264           0 :         poSRS = nullptr;
    2265             :     }
    2266         186 :     if (poSRS != nullptr)
    2267             :     {
    2268         186 :         if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
    2269             :         {
    2270         186 :             auto poSRSMatch = poSRS->FindBestMatch(100);
    2271         186 :             if (poSRSMatch)
    2272             :             {
    2273         185 :                 poSRS->Release();
    2274         185 :                 poSRS = poSRSMatch;
    2275         185 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2276             :             }
    2277             :             m_oCacheWKTToSRS.insert(
    2278         186 :                 pszWKT, std::shared_ptr<OGRSpatialReference>(poSRS->Clone()));
    2279             :         }
    2280             :         else
    2281             :         {
    2282           0 :             poSRS->AutoIdentifyEPSG();
    2283             :         }
    2284             :     }
    2285         186 :     return poSRS;
    2286             : }

Generated by: LCOV version 1.14