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

Generated by: LCOV version 1.14