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

Generated by: LCOV version 1.14