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

Generated by: LCOV version 1.14