LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbDriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 130 388 33.5 %
Date: 2024-05-06 18:28:20 Functions: 9 12 75.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements FileGDB OGR driver.
       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 "FGdbUtils.h"
      35             : #include "cpl_multiproc.h"
      36             : #include "ogrmutexeddatasource.h"
      37             : #include "FGdbDriverCore.h"
      38             : 
      39             : extern "C" void RegisterOGRFileGDB();
      40             : 
      41             : static std::map<CPLString, FGdbDatabaseConnection *> *poMapConnections =
      42             :     nullptr;
      43             : CPLMutex *FGdbDriver::hMutex = nullptr;
      44             : FGdbTransactionManager *FGdbDriver::m_poTransactionManager = nullptr;
      45             : 
      46             : /************************************************************************/
      47             : /*                     OGRFileGDBDriverUnload()                         */
      48             : /************************************************************************/
      49             : 
      50          10 : static void OGRFileGDBDriverUnload(GDALDriver *)
      51             : {
      52          10 :     if (poMapConnections && !poMapConnections->empty())
      53           0 :         CPLDebug("FileGDB", "Remaining %d connections. Bug?",
      54           0 :                  (int)poMapConnections->size());
      55          10 :     if (FGdbDriver::hMutex != nullptr)
      56           6 :         CPLDestroyMutex(FGdbDriver::hMutex);
      57          10 :     FGdbDriver::hMutex = nullptr;
      58          10 :     delete FGdbDriver::m_poTransactionManager;
      59          10 :     FGdbDriver::m_poTransactionManager = nullptr;
      60          10 :     delete poMapConnections;
      61          10 :     poMapConnections = nullptr;
      62          10 : }
      63             : 
      64             : /************************************************************************/
      65             : /*                      OGRFileGDBDriverOpen()                          */
      66             : /************************************************************************/
      67             : 
      68          55 : static GDALDataset *OGRFileGDBDriverOpen(GDALOpenInfo *poOpenInfo)
      69             : {
      70          55 :     const char *pszFilename = poOpenInfo->pszFilename;
      71             :     // @MAY_USE_OPENFILEGDB may be set to NO by the OpenFileGDB driver in its
      72             :     // Open() method when it detects that a dataset includes compressed tables
      73             :     // (.cdf), and thus calls the FileGDB driver to make it handle such
      74             :     // datasets. As the FileGDB driver would call, by default, OpenFileGDB for
      75             :     // assistance to get some information, OpenFileGDB needs to instruct it not
      76             :     // to do so to avoid a OpenFileGDB -> FileGDB -> OpenFileGDB cycle.
      77          55 :     const bool bUseOpenFileGDB = CPLTestBool(CSLFetchNameValueDef(
      78          55 :         poOpenInfo->papszOpenOptions, "@MAY_USE_OPENFILEGDB", "YES"));
      79             : 
      80          55 :     if (bUseOpenFileGDB)
      81             :     {
      82          53 :         if (OGRFileGDBDriverIdentifyInternal(poOpenInfo, pszFilename) ==
      83             :             GDAL_IDENTIFY_FALSE)
      84           0 :             return nullptr;
      85             : 
      86             :         // If this is a raster-only GDB, do not try to open it, to be consistent
      87             :         // with OpenFileGDB behavior.
      88          53 :         const char *const apszOpenFileGDBDriver[] = {"OpenFileGDB", nullptr};
      89             :         auto poOpenFileGDBDS = std::unique_ptr<GDALDataset>(
      90             :             GDALDataset::Open(pszFilename, GDAL_OF_RASTER,
      91          53 :                               apszOpenFileGDBDriver, nullptr, nullptr));
      92          53 :         if (poOpenFileGDBDS)
      93             :         {
      94           0 :             poOpenFileGDBDS.reset();
      95           0 :             poOpenFileGDBDS = std::unique_ptr<GDALDataset>(
      96             :                 GDALDataset::Open(pszFilename, GDAL_OF_VECTOR,
      97           0 :                                   apszOpenFileGDBDriver, nullptr, nullptr));
      98           0 :             if (!poOpenFileGDBDS)
      99           0 :                 return nullptr;
     100             :         }
     101             :     }
     102             : 
     103          55 :     const bool bUpdate = poOpenInfo->eAccess == GA_Update;
     104             :     long hr;
     105             : 
     106         110 :     CPLMutexHolderD(&FGdbDriver::hMutex);
     107          55 :     if (poMapConnections == nullptr)
     108           2 :         poMapConnections = new std::map<CPLString, FGdbDatabaseConnection *>();
     109             : 
     110          55 :     FGdbDatabaseConnection *pConnection = (*poMapConnections)[pszFilename];
     111          55 :     if (pConnection != nullptr)
     112             :     {
     113          11 :         if (pConnection->IsFIDHackInProgress())
     114             :         {
     115           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     116             :                      "Cannot open geodatabase at the moment since it is in "
     117             :                      "'FID hack mode'");
     118           0 :             return nullptr;
     119             :         }
     120             : 
     121          11 :         pConnection->m_nRefCount++;
     122          11 :         CPLDebug("FileGDB", "ref_count of %s = %d now", pszFilename,
     123             :                  pConnection->m_nRefCount);
     124             :     }
     125             :     else
     126             :     {
     127          44 :         Geodatabase *pGeoDatabase = new Geodatabase;
     128          44 :         hr = ::OpenGeodatabase(StringToWString(pszFilename), *pGeoDatabase);
     129             : 
     130          44 :         if (FAILED(hr))
     131             :         {
     132           2 :             delete pGeoDatabase;
     133             : 
     134           3 :             if (OGRGetDriverByName("OpenFileGDB") != nullptr &&
     135           1 :                 bUpdate == FALSE)
     136             :             {
     137           2 :                 std::wstring fgdb_error_desc_w;
     138           2 :                 std::string fgdb_error_desc("Unknown error");
     139             :                 fgdbError er;
     140           1 :                 er = FileGDBAPI::ErrorInfo::GetErrorDescription(
     141             :                     static_cast<fgdbError>(hr), fgdb_error_desc_w);
     142           1 :                 if (er == S_OK)
     143             :                 {
     144           1 :                     fgdb_error_desc = WStringToString(fgdb_error_desc_w);
     145             :                 }
     146           1 :                 CPLDebug("FileGDB",
     147             :                          "Cannot open %s with FileGDB driver: %s. Failing "
     148             :                          "silently so OpenFileGDB can be tried",
     149             :                          pszFilename, fgdb_error_desc.c_str());
     150             :             }
     151             :             else
     152             :             {
     153           1 :                 GDBErr(hr, "Failed to open Geodatabase");
     154             :             }
     155           2 :             poMapConnections->erase(pszFilename);
     156           2 :             return nullptr;
     157             :         }
     158             : 
     159          42 :         CPLDebug("FileGDB", "Really opening %s", pszFilename);
     160          42 :         pConnection = new FGdbDatabaseConnection(pszFilename, pGeoDatabase);
     161          42 :         (*poMapConnections)[pszFilename] = pConnection;
     162             :     }
     163             : 
     164             :     FGdbDataSource *pDS;
     165             : 
     166          53 :     pDS = new FGdbDataSource(true, pConnection, bUseOpenFileGDB);
     167             : 
     168          53 :     if (!pDS->Open(pszFilename, bUpdate, nullptr))
     169             :     {
     170           0 :         delete pDS;
     171           0 :         return nullptr;
     172             :     }
     173             :     else
     174             :     {
     175             :         OGRMutexedDataSource *poMutexedDS =
     176          53 :             new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE);
     177          53 :         if (bUpdate)
     178          16 :             return OGRCreateEmulatedTransactionDataSourceWrapper(
     179           8 :                 poMutexedDS, FGdbDriver::GetTransactionManager(), TRUE, FALSE);
     180             :         else
     181          45 :             return poMutexedDS;
     182             :     }
     183             : }
     184             : 
     185             : /***********************************************************************/
     186             : /*                    OGRFileGDBDriverCreate()                         */
     187             : /***********************************************************************/
     188             : 
     189             : static GDALDataset *
     190          35 : OGRFileGDBDriverCreate(const char *pszName, CPL_UNUSED int nBands,
     191             :                        CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
     192             :                        CPL_UNUSED GDALDataType eDT, char **papszOptions)
     193             : {
     194             :     long hr;
     195             :     Geodatabase *pGeodatabase;
     196         105 :     std::wstring wconn = StringToWString(pszName);
     197          35 :     int bUpdate = TRUE;  // If we're creating, we must be writing.
     198             :     VSIStatBuf stat;
     199             : 
     200          70 :     CPLMutexHolderD(&FGdbDriver::hMutex);
     201             : 
     202             :     /* We don't support options yet, so warn if they send us some */
     203             :     if (papszOptions)
     204             :     {
     205             :         /* TODO: warning, ignoring options */
     206             :     }
     207             : 
     208             :     /* Only accept names of form "filename.gdb" and */
     209             :     /* also .gdb.zip to be able to return FGDB with MapServer OGR output (#4199)
     210             :      */
     211          35 :     const char *pszExt = CPLGetExtension(pszName);
     212          35 :     if (!(EQUAL(pszExt, "gdb") || EQUAL(pszExt, "zip")))
     213             :     {
     214           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     215             :                  "FGDB data source name must use 'gdb' extension.\n");
     216           1 :         return nullptr;
     217             :     }
     218             : 
     219             :     /* Don't try to create on top of something already there */
     220          34 :     if (CPLStat(pszName, &stat) == 0)
     221             :     {
     222           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s already exists.\n", pszName);
     223           1 :         return nullptr;
     224             :     }
     225             : 
     226             :     /* Try to create the geodatabase */
     227          33 :     pGeodatabase =
     228          33 :         new Geodatabase;  // Create on heap so we can store it in the Datasource
     229          33 :     hr = CreateGeodatabase(wconn, *pGeodatabase);
     230             : 
     231             :     /* Handle creation errors */
     232          33 :     if (S_OK != hr)
     233             :     {
     234           1 :         const char *errstr = "Error creating geodatabase (%s).\n";
     235           1 :         if (hr == -2147220653)
     236           0 :             errstr = "File already exists (%s).\n";
     237           1 :         delete pGeodatabase;
     238           1 :         CPLError(CE_Failure, CPLE_AppDefined, errstr, pszName);
     239           1 :         return nullptr;
     240             :     }
     241             : 
     242             :     FGdbDatabaseConnection *pConnection =
     243          32 :         new FGdbDatabaseConnection(pszName, pGeodatabase);
     244             : 
     245          32 :     if (poMapConnections == nullptr)
     246           5 :         poMapConnections = new std::map<CPLString, FGdbDatabaseConnection *>();
     247          32 :     (*poMapConnections)[pszName] = pConnection;
     248             : 
     249             :     /* Ready to embed the Geodatabase in an OGR Datasource */
     250          32 :     FGdbDataSource *pDS = new FGdbDataSource(true, pConnection, true);
     251          32 :     if (!pDS->Open(pszName, bUpdate, nullptr))
     252             :     {
     253           0 :         delete pDS;
     254           0 :         return nullptr;
     255             :     }
     256             :     else
     257          32 :         return OGRCreateEmulatedTransactionDataSourceWrapper(
     258          32 :             new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE),
     259          64 :             FGdbDriver::GetTransactionManager(), TRUE, FALSE);
     260             : }
     261             : 
     262             : /************************************************************************/
     263             : /*                           StartTransaction()                         */
     264             : /************************************************************************/
     265             : 
     266           0 : OGRErr FGdbTransactionManager::StartTransaction(OGRDataSource *&poDSInOut,
     267             :                                                 int &bOutHasReopenedDS)
     268             : {
     269           0 :     CPLMutexHolderOptionalLockD(FGdbDriver::hMutex);
     270             : 
     271           0 :     bOutHasReopenedDS = FALSE;
     272             : 
     273           0 :     OGRMutexedDataSource *poMutexedDS = (OGRMutexedDataSource *)poDSInOut;
     274           0 :     FGdbDataSource *poDS = (FGdbDataSource *)poMutexedDS->GetBaseDataSource();
     275           0 :     if (!poDS->GetUpdate())
     276           0 :         return OGRERR_FAILURE;
     277           0 :     FGdbDatabaseConnection *pConnection = poDS->GetConnection();
     278           0 :     if (pConnection->GetRefCount() != 1)
     279             :     {
     280           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     281             :                  "Cannot start transaction as database is opened in another "
     282             :                  "connection");
     283           0 :         return OGRERR_FAILURE;
     284             :     }
     285           0 :     if (pConnection->IsLocked())
     286             :     {
     287           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     288             :                  "Transaction is already in progress");
     289           0 :         return OGRERR_FAILURE;
     290             :     }
     291             : 
     292           0 :     bOutHasReopenedDS = TRUE;
     293             : 
     294           0 :     CPLString osName(poMutexedDS->GetName());
     295           0 :     CPLString osNameOri(osName);
     296           0 :     if (osName.back() == '/' || osName.back() == '\\')
     297           0 :         osName.resize(osName.size() - 1);
     298             : 
     299             : #ifndef _WIN32
     300             :     int bPerLayerCopyingForTransaction =
     301           0 :         poDS->HasPerLayerCopyingForTransaction();
     302             : #endif
     303             : 
     304           0 :     pConnection->m_nRefCount++;
     305           0 :     delete poDSInOut;
     306           0 :     poDSInOut = nullptr;
     307           0 :     poMutexedDS = nullptr;
     308           0 :     poDS = nullptr;
     309             : 
     310           0 :     pConnection->CloseGeodatabase();
     311             : 
     312           0 :     const CPLString osEditedName(CPLString(osName).append(".ogredited"));
     313             : 
     314           0 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     315           0 :     CPL_IGNORE_RET_VAL(CPLUnlinkTree(osEditedName));
     316           0 :     CPLPopErrorHandler();
     317             : 
     318           0 :     OGRErr eErr = OGRERR_NONE;
     319             : 
     320           0 :     CPLString osDatabaseToReopen;
     321             : #ifndef _WIN32
     322           0 :     if (bPerLayerCopyingForTransaction)
     323             :     {
     324           0 :         int bError = FALSE;
     325             : 
     326           0 :         if (VSIMkdir(osEditedName, 0755) != 0)
     327             :         {
     328           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     329             :                      "Cannot create directory '%s'.", osEditedName.c_str());
     330           0 :             bError = TRUE;
     331             :         }
     332             : 
     333             :         // Only copy a0000000X.Y files with X >= 1 && X <= 8, gdb and timestamps
     334             :         // and symlink others
     335           0 :         char **papszFiles = VSIReadDir(osName);
     336           0 :         for (char **papszIter = papszFiles; !bError && *papszIter; ++papszIter)
     337             :         {
     338           0 :             if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
     339           0 :                 continue;
     340           0 :             if (((*papszIter)[0] == 'a' && atoi((*papszIter) + 1) >= 1 &&
     341           0 :                  atoi((*papszIter) + 1) <= 8) ||
     342           0 :                 EQUAL(*papszIter, "gdb") || EQUAL(*papszIter, "timestamps"))
     343             :             {
     344           0 :                 if (CPLCopyFile(
     345             :                         CPLFormFilename(osEditedName, *papszIter, nullptr),
     346           0 :                         CPLFormFilename(osName, *papszIter, nullptr)) != 0)
     347             :                 {
     348           0 :                     bError = TRUE;
     349           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s",
     350             :                              *papszIter);
     351             :                 }
     352             :             }
     353             :             else
     354             :             {
     355           0 :                 CPLString osSourceFile;
     356           0 :                 if (CPLIsFilenameRelative(osName))
     357             :                     osSourceFile = CPLFormFilename(
     358             :                         CPLSPrintf("../%s", CPLGetFilename(osName.c_str())),
     359           0 :                         *papszIter, nullptr);
     360             :                 else
     361           0 :                     osSourceFile = osName;
     362           0 :                 if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
     363           0 :                     CPLSymlink(osSourceFile,
     364             :                                CPLFormFilename(osEditedName.c_str(), *papszIter,
     365             :                                                nullptr),
     366             :                                nullptr) != 0)
     367             :                 {
     368           0 :                     bError = TRUE;
     369           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "Cannot symlink %s",
     370             :                              *papszIter);
     371             :                 }
     372             :             }
     373             :         }
     374           0 :         CSLDestroy(papszFiles);
     375             : 
     376           0 :         if (bError)
     377             :         {
     378           0 :             eErr = OGRERR_FAILURE;
     379           0 :             osDatabaseToReopen = osName;
     380             :         }
     381             :         else
     382           0 :             osDatabaseToReopen = osEditedName;
     383             :     }
     384             :     else
     385             : #endif
     386             :     {
     387           0 :         if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
     388           0 :             CPLCopyTree(osEditedName, osName) != 0)
     389             :         {
     390           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot backup geodatabase");
     391           0 :             eErr = OGRERR_FAILURE;
     392           0 :             osDatabaseToReopen = osName;
     393             :         }
     394             :         else
     395           0 :             osDatabaseToReopen = osEditedName;
     396             :     }
     397             : 
     398           0 :     pConnection->m_pGeodatabase = new Geodatabase;
     399           0 :     long hr = ::OpenGeodatabase(StringToWString(osDatabaseToReopen),
     400           0 :                                 *(pConnection->m_pGeodatabase));
     401           0 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || FAILED(hr))
     402             :     {
     403           0 :         delete pConnection->m_pGeodatabase;
     404           0 :         pConnection->m_pGeodatabase = nullptr;
     405           0 :         FGdbDriver::Release(osName);
     406           0 :         GDBErr(hr, CPLSPrintf("Failed to open %s. Dataset should be closed",
     407             :                               osDatabaseToReopen.c_str()));
     408             : 
     409           0 :         return OGRERR_FAILURE;
     410             :     }
     411             : 
     412           0 :     FGdbDataSource *pDS = new FGdbDataSource(true, pConnection, true);
     413           0 :     pDS->Open(osDatabaseToReopen, TRUE, osNameOri);
     414             : 
     415             : #ifndef _WIN32
     416           0 :     if (eErr == OGRERR_NONE && bPerLayerCopyingForTransaction)
     417             :     {
     418           0 :         pDS->SetPerLayerCopyingForTransaction(bPerLayerCopyingForTransaction);
     419           0 :         pDS->SetSymlinkFlagOnAllLayers();
     420             :     }
     421             : #endif
     422             : 
     423           0 :     poDSInOut = new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE);
     424             : 
     425           0 :     if (eErr == OGRERR_NONE)
     426           0 :         pConnection->SetLocked(TRUE);
     427           0 :     return eErr;
     428             : }
     429             : 
     430             : /************************************************************************/
     431             : /*                           CommitTransaction()                        */
     432             : /************************************************************************/
     433             : 
     434           0 : OGRErr FGdbTransactionManager::CommitTransaction(OGRDataSource *&poDSInOut,
     435             :                                                  int &bOutHasReopenedDS)
     436             : {
     437           0 :     CPLMutexHolderOptionalLockD(FGdbDriver::hMutex);
     438             : 
     439           0 :     bOutHasReopenedDS = FALSE;
     440             : 
     441           0 :     OGRMutexedDataSource *poMutexedDS = (OGRMutexedDataSource *)poDSInOut;
     442           0 :     FGdbDataSource *poDS = (FGdbDataSource *)poMutexedDS->GetBaseDataSource();
     443           0 :     FGdbDatabaseConnection *pConnection = poDS->GetConnection();
     444           0 :     if (!pConnection->IsLocked())
     445             :     {
     446           0 :         CPLError(CE_Failure, CPLE_NotSupported, "No transaction in progress");
     447           0 :         return OGRERR_FAILURE;
     448             :     }
     449             : 
     450           0 :     bOutHasReopenedDS = TRUE;
     451             : 
     452           0 :     CPLString osName(poMutexedDS->GetName());
     453           0 :     CPLString osNameOri(osName);
     454           0 :     if (osName.back() == '/' || osName.back() == '\\')
     455           0 :         osName.resize(osName.size() - 1);
     456             : 
     457             : #ifndef _WIN32
     458             :     int bPerLayerCopyingForTransaction =
     459           0 :         poDS->HasPerLayerCopyingForTransaction();
     460             : #endif
     461             : 
     462           0 :     pConnection->m_nRefCount++;
     463           0 :     delete poDSInOut;
     464           0 :     poDSInOut = nullptr;
     465           0 :     poMutexedDS = nullptr;
     466           0 :     poDS = nullptr;
     467             : 
     468           0 :     pConnection->CloseGeodatabase();
     469             : 
     470           0 :     CPLString osEditedName(osName);
     471           0 :     osEditedName += ".ogredited";
     472             : 
     473             : #ifndef _WIN32
     474           0 :     if (bPerLayerCopyingForTransaction)
     475             :     {
     476           0 :         int bError = FALSE;
     477             :         char **papszFiles;
     478           0 :         std::vector<CPLString> aosTmpFilesToClean;
     479             : 
     480             :         // Check for files present in original copy that are not in edited copy
     481             :         // That is to say deleted layers
     482           0 :         papszFiles = VSIReadDir(osName);
     483           0 :         for (char **papszIter = papszFiles; !bError && *papszIter; ++papszIter)
     484             :         {
     485           0 :             if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
     486           0 :                 continue;
     487             :             VSIStatBufL sStat;
     488           0 :             if ((*papszIter)[0] == 'a' &&
     489           0 :                 VSIStatL(CPLFormFilename(osEditedName, *papszIter, nullptr),
     490             :                          &sStat) != 0)
     491             :             {
     492           0 :                 if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
     493           0 :                     VSIRename(CPLFormFilename(osName, *papszIter, nullptr),
     494             :                               CPLFormFilename(osName, *papszIter, "tmp")) != 0)
     495             :                 {
     496           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     497             :                              "Cannot rename %s to %s",
     498             :                              CPLFormFilename(osName, *papszIter, nullptr),
     499             :                              CPLFormFilename(osName, *papszIter, "tmp"));
     500           0 :                     bError = TRUE;
     501             :                 }
     502             :                 else
     503           0 :                     aosTmpFilesToClean.push_back(
     504             :                         CPLFormFilename(osName, *papszIter, "tmp"));
     505             :             }
     506             :         }
     507           0 :         CSLDestroy(papszFiles);
     508             : 
     509             :         // Move modified files from edited directory to main directory
     510           0 :         papszFiles = VSIReadDir(osEditedName);
     511           0 :         for (char **papszIter = papszFiles; !bError && *papszIter; ++papszIter)
     512             :         {
     513           0 :             if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
     514           0 :                 continue;
     515             :             struct stat sStat;
     516           0 :             if (lstat(CPLFormFilename(osEditedName, *papszIter, nullptr),
     517           0 :                       &sStat) != 0)
     518             :             {
     519           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Cannot stat %s",
     520             :                          CPLFormFilename(osEditedName, *papszIter, nullptr));
     521           0 :                 bError = TRUE;
     522             :             }
     523           0 :             else if (!S_ISLNK(sStat.st_mode))
     524             :             {
     525             :                 // If there was such a file in original directory, first rename
     526             :                 // it as a temporary file
     527           0 :                 if (lstat(CPLFormFilename(osName, *papszIter, nullptr),
     528           0 :                           &sStat) == 0)
     529             :                 {
     530           0 :                     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""),
     531           0 :                               "CASE2") ||
     532           0 :                         VSIRename(CPLFormFilename(osName, *papszIter, nullptr),
     533             :                                   CPLFormFilename(osName, *papszIter, "tmp")) !=
     534             :                             0)
     535             :                     {
     536           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     537             :                                  "Cannot rename %s to %s",
     538             :                                  CPLFormFilename(osName, *papszIter, nullptr),
     539             :                                  CPLFormFilename(osName, *papszIter, "tmp"));
     540           0 :                         bError = TRUE;
     541             :                     }
     542             :                     else
     543           0 :                         aosTmpFilesToClean.push_back(
     544             :                             CPLFormFilename(osName, *papszIter, "tmp"));
     545             :                 }
     546           0 :                 if (!bError)
     547             :                 {
     548           0 :                     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""),
     549           0 :                               "CASE3") ||
     550           0 :                         CPLMoveFile(
     551             :                             CPLFormFilename(osName, *papszIter, nullptr),
     552             :                             CPLFormFilename(osEditedName, *papszIter,
     553             :                                             nullptr)) != 0)
     554             :                     {
     555           0 :                         CPLError(
     556             :                             CE_Failure, CPLE_AppDefined, "Cannot move %s to %s",
     557             :                             CPLFormFilename(osEditedName, *papszIter, nullptr),
     558             :                             CPLFormFilename(osName, *papszIter, nullptr));
     559           0 :                         bError = TRUE;
     560             :                     }
     561             :                     else
     562           0 :                         CPLDebug(
     563             :                             "FileGDB", "Move %s to %s",
     564             :                             CPLFormFilename(osEditedName, *papszIter, nullptr),
     565             :                             CPLFormFilename(osName, *papszIter, nullptr));
     566             :                 }
     567             :             }
     568             :         }
     569           0 :         CSLDestroy(papszFiles);
     570             : 
     571           0 :         if (!bError)
     572             :         {
     573           0 :             for (size_t i = 0; i < aosTmpFilesToClean.size(); i++)
     574             :             {
     575           0 :                 if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE4") ||
     576           0 :                     VSIUnlink(aosTmpFilesToClean[i]) != 0)
     577             :                 {
     578           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
     579             :                              "Cannot remove %s. Manual cleanup required",
     580           0 :                              aosTmpFilesToClean[i].c_str());
     581             :                 }
     582             :             }
     583             :         }
     584             : 
     585           0 :         if (bError)
     586             :         {
     587           0 :             CPLError(
     588             :                 CE_Failure, CPLE_AppDefined,
     589             :                 "An error occurred while moving files from %s back to %s. "
     590             :                 "Manual cleaning must be done and dataset should be closed",
     591             :                 osEditedName.c_str(), osName.c_str());
     592           0 :             pConnection->SetLocked(FALSE);
     593           0 :             FGdbDriver::Release(osName);
     594           0 :             return OGRERR_FAILURE;
     595             :         }
     596           0 :         else if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE5") ||
     597           0 :                  CPLUnlinkTree(osEditedName) != 0)
     598             :         {
     599           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     600             :                      "Cannot remove %s. Manual cleanup required",
     601             :                      osEditedName.c_str());
     602             :         }
     603             :     }
     604             :     else
     605             : #endif
     606             :     {
     607           0 :         CPLString osTmpName(osName);
     608           0 :         osTmpName += ".ogrtmp";
     609             : 
     610             :         /* Install the backup copy as the main database in 3 steps : */
     611             :         /* first rename the main directory  in .tmp */
     612             :         /* then rename the edited copy under regular name */
     613             :         /* and finally dispose the .tmp directory */
     614             :         /* That way there's no risk definitely losing data */
     615           0 :         if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
     616           0 :             VSIRename(osName, osTmpName) != 0)
     617             :         {
     618           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     619             :                      "Cannot rename %s to %s. Edited database during "
     620             :                      "transaction is in %s"
     621             :                      "Dataset should be closed",
     622             :                      osName.c_str(), osTmpName.c_str(), osEditedName.c_str());
     623           0 :             pConnection->SetLocked(FALSE);
     624           0 :             FGdbDriver::Release(osName);
     625           0 :             return OGRERR_FAILURE;
     626             :         }
     627             : 
     628           0 :         if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") ||
     629           0 :             VSIRename(osEditedName, osName) != 0)
     630             :         {
     631           0 :             CPLError(
     632             :                 CE_Failure, CPLE_AppDefined,
     633             :                 "Cannot rename %s to %s. The original geodatabase is in '%s'. "
     634             :                 "Dataset should be closed",
     635             :                 osEditedName.c_str(), osName.c_str(), osTmpName.c_str());
     636           0 :             pConnection->SetLocked(FALSE);
     637           0 :             FGdbDriver::Release(osName);
     638           0 :             return OGRERR_FAILURE;
     639             :         }
     640             : 
     641           0 :         if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE3") ||
     642           0 :             CPLUnlinkTree(osTmpName) != 0)
     643             :         {
     644           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     645             :                      "Cannot remove %s. Manual cleanup required",
     646             :                      osTmpName.c_str());
     647             :         }
     648             :     }
     649             : 
     650           0 :     pConnection->m_pGeodatabase = new Geodatabase;
     651           0 :     long hr = ::OpenGeodatabase(StringToWString(osName),
     652           0 :                                 *(pConnection->m_pGeodatabase));
     653           0 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE_REOPEN") ||
     654           0 :         FAILED(hr))
     655             :     {
     656           0 :         delete pConnection->m_pGeodatabase;
     657           0 :         pConnection->m_pGeodatabase = nullptr;
     658           0 :         pConnection->SetLocked(FALSE);
     659           0 :         FGdbDriver::Release(osName);
     660           0 :         GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed");
     661           0 :         return OGRERR_FAILURE;
     662             :     }
     663             : 
     664           0 :     FGdbDataSource *pDS = new FGdbDataSource(true, pConnection, true);
     665           0 :     pDS->Open(osNameOri, TRUE, nullptr);
     666             :     // pDS->SetPerLayerCopyingForTransaction(bPerLayerCopyingForTransaction);
     667           0 :     poDSInOut = new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE);
     668             : 
     669           0 :     pConnection->SetLocked(FALSE);
     670             : 
     671           0 :     return OGRERR_NONE;
     672             : }
     673             : 
     674             : /************************************************************************/
     675             : /*                           RollbackTransaction()                      */
     676             : /************************************************************************/
     677             : 
     678           0 : OGRErr FGdbTransactionManager::RollbackTransaction(OGRDataSource *&poDSInOut,
     679             :                                                    int &bOutHasReopenedDS)
     680             : {
     681           0 :     CPLMutexHolderOptionalLockD(FGdbDriver::hMutex);
     682             : 
     683           0 :     bOutHasReopenedDS = FALSE;
     684             : 
     685           0 :     OGRMutexedDataSource *poMutexedDS = (OGRMutexedDataSource *)poDSInOut;
     686           0 :     FGdbDataSource *poDS = (FGdbDataSource *)poMutexedDS->GetBaseDataSource();
     687           0 :     FGdbDatabaseConnection *pConnection = poDS->GetConnection();
     688           0 :     if (!pConnection->IsLocked())
     689             :     {
     690           0 :         CPLError(CE_Failure, CPLE_NotSupported, "No transaction in progress");
     691           0 :         return OGRERR_FAILURE;
     692             :     }
     693             : 
     694           0 :     bOutHasReopenedDS = TRUE;
     695             : 
     696           0 :     CPLString osName(poMutexedDS->GetName());
     697           0 :     CPLString osNameOri(osName);
     698           0 :     if (osName.back() == '/' || osName.back() == '\\')
     699           0 :         osName.resize(osName.size() - 1);
     700             : 
     701             :     // int bPerLayerCopyingForTransaction =
     702             :     // poDS->HasPerLayerCopyingForTransaction();
     703             : 
     704           0 :     pConnection->m_nRefCount++;
     705           0 :     delete poDSInOut;
     706           0 :     poDSInOut = nullptr;
     707           0 :     poMutexedDS = nullptr;
     708           0 :     poDS = nullptr;
     709             : 
     710           0 :     pConnection->CloseGeodatabase();
     711             : 
     712           0 :     CPLString osEditedName(osName);
     713           0 :     osEditedName += ".ogredited";
     714             : 
     715           0 :     OGRErr eErr = OGRERR_NONE;
     716           0 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE1") ||
     717           0 :         CPLUnlinkTree(osEditedName) != 0)
     718             :     {
     719           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     720             :                  "Cannot remove %s. Manual cleanup required",
     721             :                  osEditedName.c_str());
     722           0 :         eErr = OGRERR_FAILURE;
     723             :     }
     724             : 
     725           0 :     pConnection->m_pGeodatabase = new Geodatabase;
     726           0 :     long hr = ::OpenGeodatabase(StringToWString(osName),
     727           0 :                                 *(pConnection->m_pGeodatabase));
     728           0 :     if (EQUAL(CPLGetConfigOption("FGDB_SIMUL_FAIL", ""), "CASE2") || FAILED(hr))
     729             :     {
     730           0 :         delete pConnection->m_pGeodatabase;
     731           0 :         pConnection->m_pGeodatabase = nullptr;
     732           0 :         pConnection->SetLocked(FALSE);
     733           0 :         FGdbDriver::Release(osName);
     734           0 :         GDBErr(hr, "Failed to re-open Geodatabase. Dataset should be closed");
     735           0 :         return OGRERR_FAILURE;
     736             :     }
     737             : 
     738           0 :     FGdbDataSource *pDS = new FGdbDataSource(true, pConnection, true);
     739           0 :     pDS->Open(osNameOri, TRUE, nullptr);
     740             :     // pDS->SetPerLayerCopyingForTransaction(bPerLayerCopyingForTransaction);
     741           0 :     poDSInOut = new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE);
     742             : 
     743           0 :     pConnection->SetLocked(FALSE);
     744             : 
     745           0 :     return eErr;
     746             : }
     747             : 
     748             : /***********************************************************************/
     749             : /*                            Release()                                */
     750             : /***********************************************************************/
     751             : 
     752          85 : void FGdbDriver::Release(const char *pszName)
     753             : {
     754         170 :     CPLMutexHolderOptionalLockD(FGdbDriver::hMutex);
     755             : 
     756          85 :     if (poMapConnections == nullptr)
     757           0 :         poMapConnections = new std::map<CPLString, FGdbDatabaseConnection *>();
     758             : 
     759          85 :     FGdbDatabaseConnection *pConnection = (*poMapConnections)[pszName];
     760          85 :     if (pConnection != nullptr)
     761             :     {
     762          85 :         pConnection->m_nRefCount--;
     763          85 :         CPLDebug("FileGDB", "ref_count of %s = %d now", pszName,
     764             :                  pConnection->m_nRefCount);
     765          85 :         if (pConnection->m_nRefCount == 0)
     766             :         {
     767          74 :             pConnection->CloseGeodatabase();
     768          74 :             delete pConnection;
     769          74 :             poMapConnections->erase(pszName);
     770             :         }
     771             :     }
     772          85 : }
     773             : 
     774             : /***********************************************************************/
     775             : /*                       GetTransactionManager()                       */
     776             : /***********************************************************************/
     777             : 
     778          40 : FGdbTransactionManager *FGdbDriver::GetTransactionManager()
     779             : {
     780          40 :     CPLMutexHolderD(&FGdbDriver::hMutex);
     781          40 :     if (m_poTransactionManager == nullptr)
     782           6 :         m_poTransactionManager = new FGdbTransactionManager();
     783          80 :     return m_poTransactionManager;
     784             : }
     785             : 
     786             : /***********************************************************************/
     787             : /*                         CloseGeodatabase()                          */
     788             : /***********************************************************************/
     789             : 
     790          75 : void FGdbDatabaseConnection::CloseGeodatabase()
     791             : {
     792          75 :     if (m_pGeodatabase != nullptr)
     793             :     {
     794          75 :         CPLDebug("FileGDB", "Really closing %s now", m_osName.c_str());
     795          75 :         ::CloseGeodatabase(*m_pGeodatabase);
     796          75 :         delete m_pGeodatabase;
     797          75 :         m_pGeodatabase = nullptr;
     798             :     }
     799          75 : }
     800             : 
     801             : /***********************************************************************/
     802             : /*                         OpenGeodatabase()                           */
     803             : /***********************************************************************/
     804             : 
     805           1 : int FGdbDatabaseConnection::OpenGeodatabase(const char *pszFSName)
     806             : {
     807           1 :     m_pGeodatabase = new Geodatabase;
     808           2 :     long hr = ::OpenGeodatabase(StringToWString(CPLString(pszFSName)),
     809           2 :                                 *m_pGeodatabase);
     810           1 :     if (FAILED(hr))
     811             :     {
     812           0 :         delete m_pGeodatabase;
     813           0 :         m_pGeodatabase = nullptr;
     814           0 :         return FALSE;
     815             :     }
     816           1 :     return TRUE;
     817             : }
     818             : 
     819             : /************************************************************************/
     820             : /*                     OGRFileGDBDeleteDataSource()                     */
     821             : /************************************************************************/
     822             : 
     823           2 : static CPLErr OGRFileGDBDeleteDataSource(const char *pszDataSource)
     824             : {
     825           4 :     CPLMutexHolderD(&FGdbDriver::hMutex);
     826             : 
     827           6 :     std::wstring wstr = StringToWString(pszDataSource);
     828             : 
     829           2 :     long hr = 0;
     830             : 
     831           2 :     if (S_OK != (hr = ::DeleteGeodatabase(wstr)))
     832             :     {
     833           1 :         GDBErr(hr, "Failed to delete Geodatabase");
     834           1 :         return CE_Failure;
     835             :     }
     836             : 
     837           1 :     return CE_None;
     838             : }
     839             : 
     840             : /***********************************************************************/
     841             : /*                       RegisterOGRFileGDB()                          */
     842             : /***********************************************************************/
     843             : 
     844          13 : void RegisterOGRFileGDB()
     845             : 
     846             : {
     847          13 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
     848           0 :         return;
     849             : 
     850          13 :     if (!GDAL_CHECK_VERSION("OGR FGDB"))
     851           0 :         return;
     852             : 
     853          13 :     GDALDriver *poDriver = new GDALDriver();
     854          13 :     OGRFileGDBDriverSetCommonMetadata(poDriver);
     855             : 
     856          13 :     poDriver->pfnOpen = OGRFileGDBDriverOpen;
     857          13 :     poDriver->pfnCreate = OGRFileGDBDriverCreate;
     858          13 :     poDriver->pfnDelete = OGRFileGDBDeleteDataSource;
     859          13 :     poDriver->pfnUnloadDriver = OGRFileGDBDriverUnload;
     860             : 
     861          13 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     862             : }

Generated by: LCOV version 1.14