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

Generated by: LCOV version 1.14