LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbDatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 381 476 80.0 %
Date: 2024-11-25 13:07:18 Functions: 31 36 86.1 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements FileGDB OGR Datasource.
       5             :  * Author:   Ragi Yaser Burhum, ragi@burhum.com
       6             :  *           Paul Ramsey, pramsey at cleverelephant.ca
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2010, Ragi Yaser Burhum
      10             :  * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
      11             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "ogr_fgdb.h"
      17             : #include "cpl_conv.h"
      18             : #include "cpl_string.h"
      19             : #include "gdal.h"
      20             : #include "FGdbUtils.h"
      21             : #include "cpl_multiproc.h"
      22             : 
      23             : #include "filegdb_fielddomain.h"
      24             : #include "filegdb_relationship.h"
      25             : 
      26             : using std::vector;
      27             : using std::wstring;
      28             : 
      29             : /***********************************************************************/
      30             : /*                         OGRFileGDBGroup                             */
      31             : /***********************************************************************/
      32             : 
      33             : class OGRFileGDBGroup final : public GDALGroup
      34             : {
      35             :   protected:
      36             :     friend class FGdbDataSource;
      37             :     std::vector<std::shared_ptr<GDALGroup>> m_apoSubGroups{};
      38             :     std::vector<OGRLayer *> m_apoLayers{};
      39             : 
      40             :   public:
      41          95 :     OGRFileGDBGroup(const std::string &osParentName, const char *pszName)
      42          95 :         : GDALGroup(osParentName, pszName)
      43             :     {
      44          95 :     }
      45             : 
      46             :     std::vector<std::string>
      47             :     GetGroupNames(CSLConstList papszOptions) const override;
      48             :     std::shared_ptr<GDALGroup>
      49             :     OpenGroup(const std::string &osName,
      50             :               CSLConstList papszOptions) const override;
      51             : 
      52             :     std::vector<std::string>
      53             :     GetVectorLayerNames(CSLConstList papszOptions) const override;
      54             :     OGRLayer *OpenVectorLayer(const std::string &osName,
      55             :                               CSLConstList papszOptions) const override;
      56             : };
      57             : 
      58           2 : std::vector<std::string> OGRFileGDBGroup::GetGroupNames(CSLConstList) const
      59             : {
      60           2 :     std::vector<std::string> ret;
      61           4 :     for (const auto &poSubGroup : m_apoSubGroups)
      62           2 :         ret.emplace_back(poSubGroup->GetName());
      63           2 :     return ret;
      64             : }
      65             : 
      66           3 : std::shared_ptr<GDALGroup> OGRFileGDBGroup::OpenGroup(const std::string &osName,
      67             :                                                       CSLConstList) const
      68             : {
      69           6 :     for (const auto &poSubGroup : m_apoSubGroups)
      70             :     {
      71           5 :         if (poSubGroup->GetName() == osName)
      72           2 :             return poSubGroup;
      73             :     }
      74           1 :     return nullptr;
      75             : }
      76             : 
      77             : std::vector<std::string>
      78           3 : OGRFileGDBGroup::GetVectorLayerNames(CSLConstList) const
      79             : {
      80           3 :     std::vector<std::string> ret;
      81           7 :     for (const auto &poLayer : m_apoLayers)
      82           4 :         ret.emplace_back(poLayer->GetName());
      83           3 :     return ret;
      84             : }
      85             : 
      86           5 : OGRLayer *OGRFileGDBGroup::OpenVectorLayer(const std::string &osName,
      87             :                                            CSLConstList) const
      88             : {
      89           8 :     for (const auto &poLayer : m_apoLayers)
      90             :     {
      91           7 :         if (poLayer->GetName() == osName)
      92           4 :             return poLayer;
      93             :     }
      94           1 :     return nullptr;
      95             : }
      96             : 
      97             : /************************************************************************/
      98             : /*                          FGdbDataSource()                           */
      99             : /************************************************************************/
     100             : 
     101          88 : FGdbDataSource::FGdbDataSource(bool bUseDriverMutex,
     102             :                                FGdbDatabaseConnection *pConnection,
     103          88 :                                bool bUseOpenFileGDB)
     104             :     : m_bUseDriverMutex(bUseDriverMutex), m_pConnection(pConnection),
     105             :       m_pGeodatabase(nullptr), m_bUpdate(false), m_poOpenFileGDBDrv(nullptr),
     106          88 :       m_bUseOpenFileGDB(bUseOpenFileGDB)
     107             : {
     108          88 :     bPerLayerCopyingForTransaction = -1;
     109          88 :     SetDescription(pConnection->m_osName.c_str());
     110          88 :     poDriver = GDALDriver::FromHandle(GDALGetDriverByName("FileGDB"));
     111          88 : }
     112             : 
     113             : /************************************************************************/
     114             : /*                          ~FGdbDataSource()                          */
     115             : /************************************************************************/
     116             : 
     117         176 : FGdbDataSource::~FGdbDataSource()
     118             : {
     119         176 :     CPLMutexHolderOptionalLockD(m_bUseDriverMutex ? FGdbDriver::hMutex
     120             :                                                   : nullptr);
     121             : 
     122          88 :     if (m_pConnection && m_pConnection->IsLocked())
     123           0 :         CommitTransaction();
     124             : 
     125             :     // CloseInternal();
     126          88 :     size_t count = m_layers.size();
     127         499 :     for (size_t i = 0; i < count; ++i)
     128             :     {
     129         411 :         if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
     130             :         {
     131         406 :             poFgdbLayer->CloseGDBObjects();
     132             :         }
     133             :     }
     134             : 
     135          88 :     FixIndexes();
     136             : 
     137          88 :     if (m_bUseDriverMutex)
     138          87 :         FGdbDriver::Release(m_osPublicName);
     139             : 
     140             :     // size_t count = m_layers.size();
     141         499 :     for (size_t i = 0; i < count; ++i)
     142             :     {
     143             :         // only delete layers coming from this driver -- the tables opened by
     144             :         // the OpenFileGDB driver will be deleted when we close the OpenFileGDB
     145             :         // datasource
     146         411 :         if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
     147             :         {
     148         406 :             delete poFgdbLayer;
     149             :         }
     150             :     }
     151         176 : }
     152             : 
     153             : /************************************************************************/
     154             : /*                             FixIndexes()                             */
     155             : /************************************************************************/
     156             : 
     157          89 : int FGdbDataSource::FixIndexes()
     158             : {
     159          89 :     int bRet = TRUE;
     160          89 :     if (m_pConnection && m_pConnection->IsFIDHackInProgress())
     161             :     {
     162           1 :         m_pConnection->CloseGeodatabase();
     163             : 
     164           1 :         const char *const apszDrivers[2] = {"OpenFileGDB", nullptr};
     165             :         const char *pszSystemCatalog =
     166           1 :             CPLFormFilename(m_osFSName, "a00000001.gdbtable", nullptr);
     167             :         auto poOpenFileGDBDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
     168           2 :             pszSystemCatalog, GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr));
     169           2 :         if (poOpenFileGDBDS == nullptr ||
     170           1 :             poOpenFileGDBDS->GetLayer(0) == nullptr)
     171             :         {
     172           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     173             :                      "Cannot open %s with OpenFileGDB driver. "
     174             :                      "Should not happen. Some layers will be corrupted",
     175             :                      pszSystemCatalog);
     176           0 :             bRet = FALSE;
     177             :         }
     178             :         else
     179             :         {
     180           1 :             OGRLayer *poLayer = poOpenFileGDBDS->GetLayer(0);
     181           1 :             size_t count = m_layers.size();
     182           2 :             for (size_t i = 0; i < count; ++i)
     183             :             {
     184           1 :                 FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]);
     185           1 :                 if (!poFgdbLayer)
     186           0 :                     continue;
     187             : 
     188           1 :                 if (poFgdbLayer->m_oMapOGRFIDToFGDBFID.empty())
     189           0 :                     continue;
     190           2 :                 CPLString osFilter = "name = '";
     191           1 :                 osFilter += m_layers[i]->GetName();
     192           1 :                 osFilter += "'";
     193           1 :                 poLayer->SetAttributeFilter(osFilter);
     194           1 :                 poLayer->ResetReading();
     195           1 :                 OGRFeature *poF = poLayer->GetNextFeature();
     196           1 :                 if (poF == nullptr)
     197             :                 {
     198           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     199             :                              "Cannot find filename for layer %s",
     200           0 :                              m_layers[i]->GetName());
     201           0 :                     bRet = FALSE;
     202             :                 }
     203             :                 else
     204             :                 {
     205           1 :                     if (!poFgdbLayer->EditIndexesForFIDHack(CPLFormFilename(
     206           1 :                             m_osFSName, CPLSPrintf("a%08x", (int)poF->GetFID()),
     207             :                             nullptr)))
     208             :                     {
     209           0 :                         bRet = FALSE;
     210             :                     }
     211             :                 }
     212           1 :                 delete poF;
     213             :             }
     214             :         }
     215             : 
     216           1 :         m_pConnection->SetFIDHackInProgress(FALSE);
     217             :     }
     218          89 :     return bRet;
     219             : }
     220             : 
     221             : /************************************************************************/
     222             : /*                                Open()                                */
     223             : /************************************************************************/
     224             : 
     225          88 : int FGdbDataSource::Open(const char *pszNewName, int bUpdate,
     226             :                          const char *pszPublicName)
     227             : {
     228          88 :     m_osFSName = pszNewName;
     229          88 :     m_osPublicName = (pszPublicName) ? pszPublicName : pszNewName;
     230          88 :     m_pGeodatabase = m_pConnection->GetGDB();
     231          88 :     m_bUpdate = CPL_TO_BOOL(bUpdate);
     232          88 :     m_poOpenFileGDBDrv = (GDALDriver *)GDALGetDriverByName("OpenFileGDB");
     233             : 
     234          88 :     return LoadLayers(L"\\");
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                           CloseInternal()                            */
     239             : /************************************************************************/
     240             : 
     241           1 : int FGdbDataSource::CloseInternal(int bCloseGeodatabase)
     242             : {
     243           1 :     size_t count = m_layers.size();
     244           2 :     for (size_t i = 0; i < count; ++i)
     245             :     {
     246           1 :         if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
     247             :         {
     248           1 :             poFgdbLayer->CloseGDBObjects();
     249             :         }
     250             :     }
     251             : 
     252           1 :     int bRet = FixIndexes();
     253           1 :     if (m_pConnection && bCloseGeodatabase)
     254           0 :         m_pConnection->CloseGeodatabase();
     255           1 :     m_pGeodatabase = nullptr;
     256           1 :     return bRet;
     257             : }
     258             : 
     259             : /************************************************************************/
     260             : /*                               ReOpen()                               */
     261             : /************************************************************************/
     262             : 
     263           1 : int FGdbDataSource::ReOpen()
     264             : {
     265           1 :     CPLAssert(m_pGeodatabase == nullptr);
     266             : 
     267           2 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL_REOPEN", ""), "CASE1") ||
     268           1 :         !m_pConnection->OpenGeodatabase(m_osFSName))
     269             :     {
     270           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen %s",
     271             :                  m_osFSName.c_str());
     272           0 :         return FALSE;
     273             :     }
     274             : 
     275             :     FGdbDataSource *pDS =
     276           1 :         new FGdbDataSource(m_bUseDriverMutex, m_pConnection, m_bUseOpenFileGDB);
     277           2 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL_REOPEN", ""), "CASE2") ||
     278           1 :         !pDS->Open(m_osPublicName, TRUE, m_osFSName))
     279             :     {
     280           0 :         pDS->m_bUseDriverMutex = false;
     281           0 :         delete pDS;
     282           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen %s",
     283             :                  m_osFSName.c_str());
     284           0 :         return FALSE;
     285             :     }
     286             : 
     287           1 :     int bRet = TRUE;
     288           1 :     size_t count = m_layers.size();
     289           2 :     for (size_t i = 0; i < count; ++i)
     290             :     {
     291             :         FGdbLayer *pNewLayer =
     292           1 :             (FGdbLayer *)pDS->GetLayerByName(m_layers[i]->GetName());
     293           1 :         FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]);
     294           2 :         if (pNewLayer &&
     295           1 :             !EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL_REOPEN", ""), "CASE3"))
     296             :         {
     297           1 :             if (poFgdbLayer)
     298             :             {
     299           1 :                 poFgdbLayer->m_pTable = pNewLayer->m_pTable;
     300             :             }
     301           1 :             pNewLayer->m_pTable = nullptr;
     302           1 :             if (poFgdbLayer)
     303             :             {
     304           1 :                 poFgdbLayer->m_pEnumRows = pNewLayer->m_pEnumRows;
     305             :             }
     306           1 :             pNewLayer->m_pEnumRows = nullptr;
     307             :         }
     308             :         else
     309             :         {
     310           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot reopen %s",
     311           0 :                      m_layers[i]->GetName());
     312           0 :             bRet = FALSE;
     313             :         }
     314           1 :         if (poFgdbLayer)
     315             :         {
     316           1 :             poFgdbLayer->m_oMapOGRFIDToFGDBFID.clear();
     317           1 :             poFgdbLayer->m_oMapFGDBFIDToOGRFID.clear();
     318             :         }
     319             :     }
     320             : 
     321           1 :     m_pGeodatabase = pDS->m_pGeodatabase;
     322           1 :     pDS->m_pGeodatabase = nullptr;
     323             : 
     324           1 :     pDS->m_bUseDriverMutex = false;
     325           1 :     pDS->m_pConnection = nullptr;
     326           1 :     delete pDS;
     327             : 
     328           1 :     return bRet;
     329             : }
     330             : 
     331             : /************************************************************************/
     332             : /*                          OpenFGDBTables()                            */
     333             : /************************************************************************/
     334             : 
     335          70 : bool FGdbDataSource::OpenFGDBTables(OGRFileGDBGroup *group,
     336             :                                     const std::wstring &type,
     337             :                                     const std::vector<std::wstring> &layers)
     338             : {
     339             : #ifdef DISPLAY_RELATED_DATASETS
     340             :     std::vector<std::wstring> relationshipTypes;
     341             :     m_pGeodatabase->GetDatasetRelationshipTypes(relationshipTypes);
     342             :     std::vector<std::wstring> datasetTypes;
     343             :     m_pGeodatabase->GetDatasetTypes(datasetTypes);
     344             : #endif
     345             : 
     346             :     fgdbError hr;
     347         329 :     for (unsigned int i = 0; i < layers.size(); i++)
     348             :     {
     349         259 :         Table *pTable = new Table;
     350             :         // CPLDebug("FGDB", "Opening %s", WStringToString(layers[i]).c_str());
     351         259 :         if (FAILED(hr = m_pGeodatabase->OpenTable(layers[i], *pTable)))
     352             :         {
     353           1 :             delete pTable;
     354             : 
     355           2 :             std::wstring fgdb_error_desc_w;
     356             :             fgdbError er;
     357           1 :             er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr,
     358             :                                                             fgdb_error_desc_w);
     359           1 :             const char *pszLikelyReason =
     360             :                 "Might be due to unsupported spatial reference system. Using "
     361             :                 "OpenFileGDB driver or FileGDB SDK >= 1.4 should solve it";
     362           1 :             if (er == S_OK)
     363             :             {
     364             :                 std::string fgdb_error_desc =
     365           2 :                     WStringToString(fgdb_error_desc_w);
     366           1 :                 if (fgdb_error_desc == "FileGDB compression is not installed.")
     367             :                 {
     368           0 :                     pszLikelyReason = "Using FileGDB SDK 1.4 or later should "
     369             :                                       "solve this issue.";
     370             :                 }
     371             :             }
     372             : 
     373           1 :             GDBErr(hr, "Error opening " + WStringToString(layers[i]),
     374             :                    CE_Warning,
     375           2 :                    (". Skipping it. " + CPLString(pszLikelyReason)).c_str());
     376           1 :             continue;
     377             :         }
     378             : 
     379             : #ifdef DISPLAY_RELATED_DATASETS
     380             :         CPLDebug("FGDB", "Finding datasets related to %s",
     381             :                  WStringToString(layers[i]).c_str());
     382             :         // Slow !
     383             :         for (const auto &relType : relationshipTypes)
     384             :         {
     385             :             for (const auto &itemType : datasetTypes)
     386             :             {
     387             :                 std::vector<std::wstring> relatedDatasets;
     388             :                 m_pGeodatabase->GetRelatedDatasets(layers[i], relType, itemType,
     389             :                                                    relatedDatasets);
     390             : 
     391             :                 std::vector<std::string> relatedDatasetDefs;
     392             :                 m_pGeodatabase->GetRelatedDatasetDefinitions(
     393             :                     layers[i], relType, itemType, relatedDatasetDefs);
     394             : 
     395             :                 if (!relatedDatasets.empty() || !relatedDatasetDefs.empty())
     396             :                 {
     397             :                     CPLDebug("FGDB", "relationshipType: %s",
     398             :                              WStringToString(relType).c_str());
     399             :                     CPLDebug("FGDB", "itemType: %s",
     400             :                              WStringToString(itemType).c_str());
     401             :                 }
     402             :                 for (const auto &name : relatedDatasets)
     403             :                 {
     404             :                     CPLDebug("FGDB", "Related dataset: %s",
     405             :                              WStringToString(name).c_str());
     406             :                 }
     407             :                 for (const auto &xml : relatedDatasetDefs)
     408             :                 {
     409             :                     CPLDebug("FGDB", "Related dataset def: %s", xml.c_str());
     410             :                 }
     411             :             }
     412             :         }
     413             : #endif
     414             : 
     415         258 :         FGdbLayer *pLayer = new FGdbLayer();
     416         258 :         if (!pLayer->Initialize(this, pTable, layers[i], type))
     417             :         {
     418           0 :             delete pLayer;
     419           0 :             return GDBErr(hr, "Error initializing OGRLayer for " +
     420           0 :                                   WStringToString(layers[i]));
     421             :         }
     422             : 
     423         258 :         m_layers.push_back(pLayer);
     424         258 :         group->m_apoLayers.emplace_back(pLayer);
     425             :     }
     426          70 :     return true;
     427             : }
     428             : 
     429             : /************************************************************************/
     430             : /*                            LoadLayers()                             */
     431             : /************************************************************************/
     432             : 
     433          88 : bool FGdbDataSource::LoadLayers(const std::wstring &root)
     434             : {
     435         176 :     std::vector<wstring> tables;
     436         176 :     std::vector<wstring> featureclasses;
     437         176 :     std::vector<wstring> featuredatasets;
     438             :     fgdbError hr;
     439             : 
     440         176 :     auto poRootGroup = std::make_shared<OGRFileGDBGroup>(std::string(), "");
     441          88 :     m_poRootGroup = poRootGroup;
     442             : 
     443             :     /* Find all the Tables in the root */
     444          88 :     if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Table", tables)))
     445             :     {
     446           0 :         return GDBErr(hr, "Error reading Tables in " + WStringToString(root));
     447             :     }
     448             :     /* Open the tables we found */
     449          88 :     if (!tables.empty() && !OpenFGDBTables(poRootGroup.get(), L"Table", tables))
     450           0 :         return false;
     451             : 
     452             :     /* Find all the Feature Classes in the root */
     453          88 :     if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Class",
     454             :                                                      featureclasses)))
     455             :     {
     456           0 :         return GDBErr(hr, "Error reading Feature Classes in " +
     457           0 :                               WStringToString(root));
     458             :     }
     459             :     /* Open the tables we found */
     460         135 :     if (!featureclasses.empty() &&
     461         135 :         !OpenFGDBTables(poRootGroup.get(), L"Feature Class", featureclasses))
     462           0 :         return false;
     463             : 
     464             :     /* Find all the Feature Datasets in the root */
     465          88 :     if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Dataset",
     466             :                                                      featuredatasets)))
     467             :     {
     468           0 :         return GDBErr(hr, "Error reading Feature Datasets in " +
     469           0 :                               WStringToString(root));
     470             :     }
     471             :     /* Look for Feature Classes inside the Feature Dataset */
     472          95 :     for (unsigned int i = 0; i < featuredatasets.size(); i++)
     473             :     {
     474             :         const std::string featureDatasetPath(
     475           7 :             WStringToString(featuredatasets[i]));
     476           7 :         if (FAILED(hr = m_pGeodatabase->GetChildDatasets(
     477             :                        featuredatasets[i], L"Feature Class", featureclasses)))
     478             :         {
     479           0 :             return GDBErr(hr, "Error reading Feature Classes in " +
     480           0 :                                   featureDatasetPath);
     481             :         }
     482           7 :         std::string featureDatasetName(featureDatasetPath);
     483           7 :         if (!featureDatasetName.empty() && featureDatasetPath[0] == '\\')
     484           7 :             featureDatasetName = featureDatasetName.substr(1);
     485             :         auto poFeatureDatasetGroup = std::make_shared<OGRFileGDBGroup>(
     486           7 :             poRootGroup->GetName(), featureDatasetName.c_str());
     487           7 :         poRootGroup->m_apoSubGroups.emplace_back(poFeatureDatasetGroup);
     488          14 :         if (!featureclasses.empty() &&
     489          14 :             !OpenFGDBTables(poFeatureDatasetGroup.get(), L"Feature Class",
     490             :                             featureclasses))
     491           0 :             return false;
     492             :     }
     493             : 
     494             :     // Look for items which aren't present in the GDB_Items table (see
     495             :     // https://github.com/OSGeo/gdal/issues/4463) We do this by browsing the
     496             :     // catalog table (using the OpenFileGDB driver) and looking for items we
     497             :     // haven't yet found. If we find any, we have no choice but to load these
     498             :     // using the OpenFileGDB driver, as the ESRI SDK refuses to acknowledge that
     499             :     // they exist (despite ArcGIS itself showing them!)
     500          88 :     int iGDBItems = -1;
     501          88 :     const char *const apszDrivers[2] = {"OpenFileGDB", nullptr};
     502             :     const char *pszSystemCatalog =
     503          88 :         CPLFormFilename(m_osFSName, "a00000001", "gdbtable");
     504          88 :     if (m_bUseOpenFileGDB)
     505             :     {
     506          86 :         m_poOpenFileGDBDS.reset(GDALDataset::Open(
     507             :             pszSystemCatalog, GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr));
     508             :     }
     509         114 :     if (m_poOpenFileGDBDS != nullptr &&
     510          26 :         m_poOpenFileGDBDS->GetLayer(0) != nullptr)
     511             :     {
     512          26 :         OGRLayer *poCatalogLayer = m_poOpenFileGDBDS->GetLayer(0);
     513             :         const int iNameIndex =
     514          26 :             poCatalogLayer->GetLayerDefn()->GetFieldIndex("Name");
     515          26 :         int i = -1;
     516         263 :         for (auto &poFeat : poCatalogLayer)
     517             :         {
     518         237 :             i++;
     519             :             const std::string osTableName =
     520         237 :                 poFeat->GetFieldAsString(iNameIndex);
     521             : 
     522         237 :             if (osTableName.compare("GDB_Items") == 0)
     523             :             {
     524          26 :                 iGDBItems = i;
     525             :             }
     526             : 
     527             :             // test if layer is already added
     528         237 :             if (GDALDataset::GetLayerByName(osTableName.c_str()))
     529          23 :                 continue;
     530             : 
     531         428 :             const CPLString osLCTableName(CPLString(osTableName).tolower());
     532         428 :             const bool bIsPrivateLayer = osLCTableName.size() >= 4 &&
     533         428 :                                          osLCTableName.substr(0, 4) == "gdb_";
     534         214 :             if (!bIsPrivateLayer)
     535             :             {
     536           6 :                 if (OGRLayer *poLayer =
     537           6 :                         m_poOpenFileGDBDS->GetLayerByName(osTableName.c_str()))
     538             :                 {
     539           5 :                     m_layers.push_back(poLayer);
     540           5 :                     poRootGroup->m_apoLayers.emplace_back(poLayer);
     541             :                 }
     542             :             }
     543             :         }
     544             :     }
     545             : 
     546             :     // Read relationships. Note that we don't use the SDK method for this, as
     547             :     // it's too slow!
     548          88 :     if (iGDBItems >= 0)
     549             :     {
     550          26 :         const char *pszGDBItems = CPLFormFilename(
     551             :             m_osFSName, CPLSPrintf("a%08x", iGDBItems + 1), "gdbtable");
     552             :         std::unique_ptr<GDALDataset> poGDBItems(GDALDataset::Open(
     553          26 :             pszGDBItems, GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr));
     554          26 :         if (poGDBItems != nullptr && poGDBItems->GetLayer(0) != nullptr)
     555             :         {
     556          26 :             if (OGRLayer *poItemsLayer = poGDBItems->GetLayer(0))
     557             :             {
     558          26 :                 const int iType = poItemsLayer->FindFieldIndex("Type", true);
     559             :                 const int iDefinition =
     560          26 :                     poItemsLayer->FindFieldIndex("Definition", true);
     561          26 :                 if (iType < 0 || iDefinition < 0)
     562             :                 {
     563           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     564             :                              "Wrong structure for GDB_Items table");
     565           0 :                     return FALSE;
     566             :                 }
     567             : 
     568             :                 // Hunt for relationships in GDB_Items table
     569         325 :                 for (const auto &poFeature : poItemsLayer)
     570             :                 {
     571         598 :                     CPLString osType;
     572         299 :                     const char *pszType = poFeature->GetFieldAsString(iType);
     573         299 :                     if (pszType != nullptr &&
     574         299 :                         EQUAL(pszType, pszRelationshipTypeUUID))
     575             :                     {
     576             :                         // relationship item
     577             :                         auto poRelationship = ParseXMLRelationshipDef(
     578          33 :                             poFeature->GetFieldAsString(iDefinition));
     579          11 :                         if (poRelationship)
     580             :                         {
     581             :                             const std::string relationshipName(
     582          22 :                                 poRelationship->GetName());
     583          11 :                             m_osMapRelationships[relationshipName] =
     584          22 :                                 std::move(poRelationship);
     585             :                         }
     586             :                     }
     587             :                 }
     588             :             }
     589             :         }
     590             :     }
     591             : 
     592          88 :     return true;
     593             : }
     594             : 
     595             : /************************************************************************/
     596             : /*                            DeleteLayer()                             */
     597             : /************************************************************************/
     598             : 
     599           2 : OGRErr FGdbDataSource::DeleteLayer(int iLayer)
     600             : {
     601           2 :     if (!m_bUpdate || m_pGeodatabase == nullptr)
     602           0 :         return OGRERR_FAILURE;
     603             : 
     604           2 :     if (iLayer < 0 || iLayer >= static_cast<int>(m_layers.size()))
     605           0 :         return OGRERR_FAILURE;
     606             : 
     607           2 :     FGdbLayer *poBaseLayer = dynamic_cast<FGdbLayer *>(m_layers[iLayer]);
     608           2 :     if (!poBaseLayer)
     609           0 :         return OGRERR_FAILURE;
     610             : 
     611             :     // Fetch FGDBAPI Table before deleting OGR layer object
     612             : 
     613             :     // Table* pTable = poBaseLayer->GetTable();
     614             : 
     615           4 :     std::string name = poBaseLayer->GetLayerDefn()->GetName();
     616           4 :     std::wstring strPath = poBaseLayer->GetTablePath();
     617           4 :     std::wstring strType = poBaseLayer->GetType();
     618             : 
     619             :     // delete OGR layer
     620           2 :     delete m_layers[iLayer];
     621             : 
     622             :     // pTable = NULL; // OGR Layer had ownership of FGDB Table
     623             : 
     624           2 :     m_layers.erase(m_layers.begin() + iLayer);
     625             : 
     626             :     long hr;
     627             : 
     628           2 :     if (FAILED(hr = m_pGeodatabase->Delete(strPath, strType)))
     629             :     {
     630           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     631             :                  "%s was not deleted however it has been closed", name.c_str());
     632           0 :         GDBErr(hr, "Failed deleting dataset");
     633           0 :         return OGRERR_FAILURE;
     634             :     }
     635             : 
     636           2 :     return OGRERR_NONE;
     637             : }
     638             : 
     639             : /************************************************************************/
     640             : /*                           TestCapability()                           */
     641             : /************************************************************************/
     642             : 
     643         126 : int FGdbDataSource::TestCapability(const char *pszCap)
     644             : {
     645         126 :     if (EQUAL(pszCap, ODsCCreateLayer))
     646          10 :         return m_bUpdate;
     647             : 
     648         116 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     649           2 :         return m_bUpdate;
     650         114 :     else if (EQUAL(pszCap, ODsCRandomLayerWrite))
     651           0 :         return m_bUpdate;
     652         114 :     else if (EQUAL(pszCap, ODsCAddFieldDomain) ||
     653         110 :              EQUAL(pszCap, ODsCDeleteFieldDomain) ||
     654         109 :              EQUAL(pszCap, ODsCUpdateFieldDomain))
     655           6 :         return m_bUpdate;
     656         108 :     else if (EQUAL(pszCap, ODsCMeasuredGeometries))
     657          36 :         return TRUE;
     658          72 :     else if (EQUAL(pszCap, ODsCZGeometries))
     659          36 :         return TRUE;
     660             : 
     661          36 :     return FALSE;
     662             : }
     663             : 
     664             : /************************************************************************/
     665             : /*                              GetLayer()                              */
     666             : /************************************************************************/
     667             : 
     668        8034 : OGRLayer *FGdbDataSource::GetLayer(int iLayer)
     669             : {
     670        8034 :     int count = static_cast<int>(m_layers.size());
     671             : 
     672        8034 :     if (iLayer < 0 || iLayer >= count)
     673           4 :         return nullptr;
     674             :     else
     675        8030 :         return m_layers[iLayer];
     676             : }
     677             : 
     678             : /************************************************************************/
     679             : /*                             ICreateLayer()                           */
     680             : /*                                                                      */
     681             : /* See FGdbLayer::Create for creation options                           */
     682             : /************************************************************************/
     683             : 
     684             : OGRLayer *
     685         150 : FGdbDataSource::ICreateLayer(const char *pszLayerName,
     686             :                              const OGRGeomFieldDefn *poSrcGeomFieldDefn,
     687             :                              CSLConstList papszOptions)
     688             : {
     689         150 :     if (!m_bUpdate || m_pGeodatabase == nullptr)
     690           0 :         return nullptr;
     691             : 
     692         150 :     FGdbLayer *pLayer = new FGdbLayer();
     693         150 :     if (!pLayer->Create(this, pszLayerName, poSrcGeomFieldDefn, papszOptions))
     694             :     {
     695           0 :         delete pLayer;
     696           0 :         return nullptr;
     697             :     }
     698             : 
     699         150 :     m_layers.push_back(pLayer);
     700             : 
     701         150 :     return pLayer;
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                   OGRFGdbSingleFeatureLayer                          */
     706             : /************************************************************************/
     707             : 
     708             : class OGRFGdbSingleFeatureLayer final : public OGRLayer
     709             : {
     710             :   private:
     711             :     char *pszVal;
     712             :     OGRFeatureDefn *poFeatureDefn;
     713             :     int iNextShapeId;
     714             : 
     715             :   public:
     716             :     OGRFGdbSingleFeatureLayer(const char *pszLayerName, const char *pszVal);
     717             :     virtual ~OGRFGdbSingleFeatureLayer();
     718             : 
     719           0 :     virtual void ResetReading() override
     720             :     {
     721           0 :         iNextShapeId = 0;
     722           0 :     }
     723             : 
     724             :     virtual OGRFeature *GetNextFeature() override;
     725             : 
     726           0 :     virtual OGRFeatureDefn *GetLayerDefn() override
     727             :     {
     728           0 :         return poFeatureDefn;
     729             :     }
     730             : 
     731           0 :     virtual int TestCapability(const char *) override
     732             :     {
     733           0 :         return FALSE;
     734             :     }
     735             : };
     736             : 
     737             : /************************************************************************/
     738             : /*                    OGRFGdbSingleFeatureLayer()                       */
     739             : /************************************************************************/
     740             : 
     741          35 : OGRFGdbSingleFeatureLayer::OGRFGdbSingleFeatureLayer(const char *pszLayerName,
     742          35 :                                                      const char *pszValIn)
     743             : {
     744          35 :     poFeatureDefn = new OGRFeatureDefn(pszLayerName);
     745          35 :     SetDescription(poFeatureDefn->GetName());
     746          35 :     poFeatureDefn->Reference();
     747          35 :     OGRFieldDefn oField("FIELD_1", OFTString);
     748          35 :     poFeatureDefn->AddFieldDefn(&oField);
     749             : 
     750          35 :     iNextShapeId = 0;
     751          35 :     pszVal = pszValIn ? CPLStrdup(pszValIn) : nullptr;
     752          35 : }
     753             : 
     754             : /************************************************************************/
     755             : /*                   ~OGRFGdbSingleFeatureLayer()                       */
     756             : /************************************************************************/
     757             : 
     758          70 : OGRFGdbSingleFeatureLayer::~OGRFGdbSingleFeatureLayer()
     759             : {
     760          35 :     if (poFeatureDefn != nullptr)
     761          35 :         poFeatureDefn->Release();
     762          35 :     CPLFree(pszVal);
     763          70 : }
     764             : 
     765             : /************************************************************************/
     766             : /*                           GetNextFeature()                           */
     767             : /************************************************************************/
     768             : 
     769          52 : OGRFeature *OGRFGdbSingleFeatureLayer::GetNextFeature()
     770             : {
     771          52 :     if (iNextShapeId != 0)
     772          17 :         return nullptr;
     773             : 
     774          35 :     OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
     775          35 :     if (pszVal)
     776          35 :         poFeature->SetField(0, pszVal);
     777          35 :     poFeature->SetFID(iNextShapeId++);
     778          35 :     return poFeature;
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                              ExecuteSQL()                            */
     783             : /************************************************************************/
     784             : 
     785         132 : OGRLayer *FGdbDataSource::ExecuteSQL(const char *pszSQLCommand,
     786             :                                      OGRGeometry *poSpatialFilter,
     787             :                                      const char *pszDialect)
     788             : 
     789             : {
     790         132 :     if (m_pConnection && m_pConnection->IsFIDHackInProgress())
     791             :     {
     792           1 :         if (CloseInternal())
     793           1 :             ReOpen();
     794             :     }
     795         132 :     if (m_pGeodatabase == nullptr)
     796           0 :         return nullptr;
     797             : 
     798         132 :     size_t count = m_layers.size();
     799        2233 :     for (size_t i = 0; i < count; ++i)
     800             :     {
     801        2101 :         if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
     802        2101 :             poFgdbLayer->EndBulkLoad();
     803             :     }
     804             : 
     805             :     /* -------------------------------------------------------------------- */
     806             :     /*      Use generic implementation for recognized dialects              */
     807             :     /* -------------------------------------------------------------------- */
     808         132 :     if (IsGenericSQLDialect(pszDialect))
     809           0 :         return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
     810           0 :                                        pszDialect);
     811             : 
     812             :     /* -------------------------------------------------------------------- */
     813             :     /*      Special case GetLayerDefinition                                 */
     814             :     /* -------------------------------------------------------------------- */
     815         132 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
     816             :     {
     817          36 :         FGdbLayer *poLayer = (FGdbLayer *)GetLayerByName(
     818          18 :             pszSQLCommand + strlen("GetLayerDefinition "));
     819          18 :         if (poLayer)
     820             :         {
     821          17 :             char *pszVal = nullptr;
     822          17 :             poLayer->GetLayerXML(&pszVal);
     823             :             OGRLayer *poRet =
     824          17 :                 new OGRFGdbSingleFeatureLayer("LayerDefinition", pszVal);
     825          17 :             CPLFree(pszVal);
     826          17 :             return poRet;
     827             :         }
     828             :         else
     829           1 :             return nullptr;
     830             :     }
     831             : 
     832             :     /* -------------------------------------------------------------------- */
     833             :     /*      Special case GetLayerMetadata                                   */
     834             :     /* -------------------------------------------------------------------- */
     835         114 :     if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
     836             :     {
     837          36 :         FGdbLayer *poLayer = (FGdbLayer *)GetLayerByName(
     838          18 :             pszSQLCommand + strlen("GetLayerMetadata "));
     839          18 :         if (poLayer)
     840             :         {
     841          17 :             char *pszVal = nullptr;
     842          17 :             poLayer->GetLayerMetadataXML(&pszVal);
     843             :             OGRLayer *poRet =
     844          17 :                 new OGRFGdbSingleFeatureLayer("LayerMetadata", pszVal);
     845          17 :             CPLFree(pszVal);
     846          17 :             return poRet;
     847             :         }
     848             :         else
     849           1 :             return nullptr;
     850             :     }
     851             : 
     852             :     /* -------------------------------------------------------------------- */
     853             :     /*      Special case REPACK                                             */
     854             :     /* -------------------------------------------------------------------- */
     855          96 :     if (EQUAL(pszSQLCommand, "REPACK"))
     856             :     {
     857           1 :         auto hr = m_pGeodatabase->CompactDatabase();
     858           1 :         if (FAILED(hr))
     859             :         {
     860           0 :             GDBErr(hr, "CompactDatabase failed");
     861           0 :             return new OGRFGdbSingleFeatureLayer("result", "false");
     862             :         }
     863           1 :         return new OGRFGdbSingleFeatureLayer("result", "true");
     864             :     }
     865             : 
     866             :     /* TODO: remove that workaround when the SDK has finally a decent */
     867             :     /* SQL support ! */
     868          95 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT ") && pszDialect == nullptr)
     869             :     {
     870          90 :         CPLDebug("FGDB", "Support for SELECT is known to be partially "
     871             :                          "non-compliant with FileGDB SDK API v1.2.\n"
     872             :                          "So for now, we use default OGR SQL engine. "
     873             :                          "Explicitly specify -dialect FileGDB\n"
     874             :                          "to use the SQL engine from the FileGDB SDK API");
     875             :         OGRLayer *poLayer =
     876          90 :             GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
     877          90 :         if (poLayer)
     878          89 :             m_oSetSelectLayers.insert(poLayer);
     879          90 :         return poLayer;
     880             :     }
     881             : 
     882             :     /* -------------------------------------------------------------------- */
     883             :     /*      Run the SQL                                                     */
     884             :     /* -------------------------------------------------------------------- */
     885           5 :     EnumRows *pEnumRows = new EnumRows;
     886             :     long hr;
     887             :     try
     888             :     {
     889           5 :         hr = m_pGeodatabase->ExecuteSQL(StringToWString(pszSQLCommand), true,
     890             :                                         *pEnumRows);
     891             :     }
     892           0 :     catch (...)
     893             :     {
     894           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     895             :                  "Exception occurred at executing '%s'. Application may "
     896             :                  "become unstable",
     897             :                  pszSQLCommand);
     898           0 :         delete pEnumRows;
     899           0 :         return nullptr;
     900             :     }
     901             : 
     902           5 :     if (FAILED(hr))
     903             :     {
     904           2 :         GDBErr(hr, CPLSPrintf("Failed at executing '%s'", pszSQLCommand));
     905           2 :         delete pEnumRows;
     906           2 :         return nullptr;
     907             :     }
     908             : 
     909           3 :     if (STARTS_WITH_CI(pszSQLCommand, "SELECT "))
     910             :     {
     911           2 :         OGRLayer *poLayer = new FGdbResultLayer(this, pszSQLCommand, pEnumRows);
     912           2 :         m_oSetSelectLayers.insert(poLayer);
     913           2 :         return poLayer;
     914             :     }
     915             :     else
     916             :     {
     917           1 :         delete pEnumRows;
     918           1 :         return nullptr;
     919             :     }
     920             : }
     921             : 
     922             : /************************************************************************/
     923             : /*                           ReleaseResultSet()                         */
     924             : /************************************************************************/
     925             : 
     926         126 : void FGdbDataSource::ReleaseResultSet(OGRLayer *poResultsSet)
     927             : {
     928         126 :     if (poResultsSet)
     929         126 :         m_oSetSelectLayers.erase(poResultsSet);
     930         126 :     delete poResultsSet;
     931         126 : }
     932             : 
     933             : /************************************************************************/
     934             : /*                      HasPerLayerCopyingForTransaction()              */
     935             : /************************************************************************/
     936             : 
     937           0 : int FGdbDataSource::HasPerLayerCopyingForTransaction()
     938             : {
     939           0 :     if (bPerLayerCopyingForTransaction >= 0)
     940           0 :         return bPerLayerCopyingForTransaction;
     941             : #ifdef _WIN32
     942             :     bPerLayerCopyingForTransaction = FALSE;
     943             : #else
     944           0 :     bPerLayerCopyingForTransaction =
     945           0 :         m_poOpenFileGDBDrv != nullptr &&
     946           0 :         CPLTestBool(
     947             :             CPLGetConfigOption("FGDB_PER_LAYER_COPYING_TRANSACTION", "TRUE"));
     948             : #endif
     949           0 :     return bPerLayerCopyingForTransaction;
     950             : }
     951             : 
     952             : /************************************************************************/
     953             : /*                        SetSymlinkFlagOnAllLayers()                    */
     954             : /************************************************************************/
     955             : 
     956           0 : void FGdbDataSource::SetSymlinkFlagOnAllLayers()
     957             : {
     958           0 :     size_t count = m_layers.size();
     959           0 :     for (size_t i = 0; i < count; ++i)
     960             :     {
     961           0 :         if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
     962           0 :             poFgdbLayer->SetSymlinkFlag();
     963             :     }
     964           0 : }
     965             : 
     966             : /************************************************************************/
     967             : /*                        GetFieldDomain()                              */
     968             : /************************************************************************/
     969             : 
     970             : const OGRFieldDomain *
     971          25 : FGdbDataSource::GetFieldDomain(const std::string &name) const
     972             : {
     973          25 :     const auto baseRet = GDALDataset::GetFieldDomain(name);
     974          25 :     if (baseRet)
     975           9 :         return baseRet;
     976             : 
     977          32 :     std::string domainDef;
     978             :     const auto hr =
     979          16 :         m_pGeodatabase->GetDomainDefinition(StringToWString(name), domainDef);
     980          16 :     if (FAILED(hr))
     981             :     {
     982             :         // GDBErr(hr, "Failed in GetDomainDefinition()");
     983          10 :         return nullptr;
     984             :     }
     985             : 
     986          12 :     auto poDomain = ParseXMLFieldDomainDef(domainDef);
     987           6 :     if (!poDomain)
     988           0 :         return nullptr;
     989          12 :     const std::string domainName(poDomain->GetName());
     990           6 :     m_oMapFieldDomains[domainName] = std::move(poDomain);
     991           6 :     return GDALDataset::GetFieldDomain(name);
     992             : }
     993             : 
     994             : /************************************************************************/
     995             : /*                        GetFieldDomainNames()                         */
     996             : /************************************************************************/
     997             : 
     998           2 : std::vector<std::string> FGdbDataSource::GetFieldDomainNames(CSLConstList) const
     999             : {
    1000           4 :     std::vector<std::wstring> oDomainNamesWList;
    1001           2 :     const auto hr = m_pGeodatabase->GetDomains(oDomainNamesWList);
    1002           2 :     if (FAILED(hr))
    1003             :     {
    1004           0 :         GDBErr(hr, "Failed in GetDomains()");
    1005           0 :         return std::vector<std::string>();
    1006             :     }
    1007             : 
    1008           4 :     std::vector<std::string> oDomainNamesList;
    1009           2 :     oDomainNamesList.reserve(oDomainNamesWList.size());
    1010           8 :     for (const auto &osNameW : oDomainNamesWList)
    1011             :     {
    1012           6 :         oDomainNamesList.emplace_back(WStringToString(osNameW));
    1013             :     }
    1014           2 :     return oDomainNamesList;
    1015             : }
    1016             : 
    1017             : /************************************************************************/
    1018             : /*                          AddFieldDomain()                            */
    1019             : /************************************************************************/
    1020             : 
    1021           4 : bool FGdbDataSource::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
    1022             :                                     std::string &failureReason)
    1023             : {
    1024           8 :     const std::string domainName(domain->GetName());
    1025           4 :     if (!m_bUpdate)
    1026             :     {
    1027           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1028             :                  "AddFieldDomain() not supported on read-only dataset");
    1029           0 :         return false;
    1030             :     }
    1031             : 
    1032           4 :     if (GetFieldDomain(domainName) != nullptr)
    1033             :     {
    1034           0 :         failureReason = "A domain of identical name already exists";
    1035           0 :         return false;
    1036             :     }
    1037             : 
    1038             :     std::string osXML =
    1039           8 :         BuildXMLFieldDomainDef(domain.get(), true, failureReason);
    1040           4 :     if (osXML.empty())
    1041             :     {
    1042           0 :         return false;
    1043             :     }
    1044             : 
    1045           4 :     const auto hr = m_pGeodatabase->CreateDomain(osXML);
    1046           4 :     if (FAILED(hr))
    1047             :     {
    1048           0 :         GDBErr(hr, "Failed in CreateDomain()");
    1049           0 :         CPLDebug("FileGDB", "XML of domain: %s", osXML.c_str());
    1050           0 :         return false;
    1051             :     }
    1052             : 
    1053           4 :     m_oMapFieldDomains[domainName] = std::move(domain);
    1054             : 
    1055           4 :     return true;
    1056             : }
    1057             : 
    1058             : /************************************************************************/
    1059             : /*                         DeleteFieldDomain()                          */
    1060             : /************************************************************************/
    1061             : 
    1062           2 : bool FGdbDataSource::DeleteFieldDomain(const std::string &name,
    1063             :                                        std::string & /*failureReason*/)
    1064             : {
    1065           2 :     if (!m_bUpdate)
    1066             :     {
    1067           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1068             :                  "DeleteFieldDomain() not supported on read-only dataset");
    1069           0 :         return false;
    1070             :     }
    1071             : 
    1072           2 :     const auto hr = m_pGeodatabase->DeleteDomain(StringToWString(name));
    1073           2 :     if (FAILED(hr))
    1074             :     {
    1075           1 :         GDBErr(hr, "Failed in DeleteDomain()");
    1076           1 :         return false;
    1077             :     }
    1078             : 
    1079           1 :     m_oMapFieldDomains.erase(name);
    1080             : 
    1081           1 :     return true;
    1082             : }
    1083             : 
    1084             : /************************************************************************/
    1085             : /*                        UpdateFieldDomain()                           */
    1086             : /************************************************************************/
    1087             : 
    1088           1 : bool FGdbDataSource::UpdateFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
    1089             :                                        std::string &failureReason)
    1090             : {
    1091           2 :     const std::string domainName(domain->GetName());
    1092           1 :     if (!m_bUpdate)
    1093             :     {
    1094           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1095             :                  "AddFieldDomain() not supported on read-only dataset");
    1096           0 :         return false;
    1097             :     }
    1098             : 
    1099           1 :     if (GetFieldDomain(domainName) == nullptr)
    1100             :     {
    1101           0 :         failureReason = "The domain should already exist to be updated";
    1102           0 :         return false;
    1103             :     }
    1104             : 
    1105             :     std::string osXML =
    1106           2 :         BuildXMLFieldDomainDef(domain.get(), true, failureReason);
    1107           1 :     if (osXML.empty())
    1108             :     {
    1109           0 :         return false;
    1110             :     }
    1111             : 
    1112           1 :     const auto hr = m_pGeodatabase->AlterDomain(osXML);
    1113           1 :     if (FAILED(hr))
    1114             :     {
    1115           0 :         GDBErr(hr, "Failed in AlterDomain()");
    1116           0 :         CPLDebug("FileGDB", "XML of domain: %s", osXML.c_str());
    1117           0 :         return false;
    1118             :     }
    1119             : 
    1120           1 :     m_oMapFieldDomains[domainName] = std::move(domain);
    1121             : 
    1122           1 :     return true;
    1123             : }
    1124             : 
    1125             : /************************************************************************/
    1126             : /*                        GetRelationshipNames()                        */
    1127             : /************************************************************************/
    1128             : 
    1129             : std::vector<std::string>
    1130           2 : FGdbDataSource::GetRelationshipNames(CPL_UNUSED CSLConstList papszOptions) const
    1131             : 
    1132             : {
    1133           2 :     std::vector<std::string> oasNames;
    1134           2 :     oasNames.reserve(m_osMapRelationships.size());
    1135          13 :     for (auto it = m_osMapRelationships.begin();
    1136          24 :          it != m_osMapRelationships.end(); ++it)
    1137             :     {
    1138          11 :         oasNames.emplace_back(it->first);
    1139             :     }
    1140           2 :     return oasNames;
    1141             : }
    1142             : 
    1143             : /************************************************************************/
    1144             : /*                        GetRelationship()                             */
    1145             : /************************************************************************/
    1146             : 
    1147             : const GDALRelationship *
    1148           8 : FGdbDataSource::GetRelationship(const std::string &name) const
    1149             : 
    1150             : {
    1151           8 :     auto it = m_osMapRelationships.find(name);
    1152           8 :     if (it == m_osMapRelationships.end())
    1153           1 :         return nullptr;
    1154             : 
    1155           7 :     return it->second.get();
    1156             : }

Generated by: LCOV version 1.14