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 :
29 : /************************************************************************/
30 : /* OGRFileGDBDriverUnload() */
31 : /************************************************************************/
32 :
33 11 : static void OGRFileGDBDriverUnload(GDALDriver *)
34 : {
35 11 : if (poMapConnections && !poMapConnections->empty())
36 0 : CPLDebug("FileGDB", "Remaining %d connections. Bug?",
37 0 : (int)poMapConnections->size());
38 11 : if (FGdbDriver::hMutex != nullptr)
39 1 : CPLDestroyMutex(FGdbDriver::hMutex);
40 11 : FGdbDriver::hMutex = nullptr;
41 11 : delete poMapConnections;
42 11 : poMapConnections = nullptr;
43 11 : }
44 :
45 : /************************************************************************/
46 : /* OGRFileGDBDriverOpen() */
47 : /************************************************************************/
48 :
49 39 : static GDALDataset *OGRFileGDBDriverOpen(GDALOpenInfo *poOpenInfo)
50 : {
51 39 : if (poOpenInfo->eAccess == GA_Update)
52 1 : return nullptr;
53 :
54 38 : 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 38 : const bool bUseOpenFileGDB = CPLTestBool(CSLFetchNameValueDef(
62 38 : poOpenInfo->papszOpenOptions, "@MAY_USE_OPENFILEGDB", "YES"));
63 :
64 38 : if (bUseOpenFileGDB)
65 : {
66 36 : 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 36 : const char *const apszOpenFileGDBDriver[] = {"OpenFileGDB", nullptr};
73 : auto poOpenFileGDBDS = std::unique_ptr<GDALDataset>(
74 : GDALDataset::Open(pszFilename, GDAL_OF_RASTER,
75 36 : apszOpenFileGDBDriver, nullptr, nullptr));
76 36 : 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 : long hr;
88 :
89 76 : CPLMutexHolderD(&FGdbDriver::hMutex);
90 38 : if (poMapConnections == nullptr)
91 2 : poMapConnections = new std::map<CPLString, FGdbDatabaseConnection *>();
92 :
93 38 : FGdbDatabaseConnection *pConnection = (*poMapConnections)[pszFilename];
94 38 : if (pConnection != nullptr)
95 : {
96 4 : pConnection->m_nRefCount++;
97 4 : CPLDebug("FileGDB", "ref_count of %s = %d now", pszFilename,
98 : pConnection->m_nRefCount);
99 : }
100 : else
101 : {
102 34 : Geodatabase *pGeoDatabase = new Geodatabase;
103 34 : hr = ::OpenGeodatabase(StringToWString(pszFilename), *pGeoDatabase);
104 :
105 34 : if (FAILED(hr))
106 : {
107 2 : delete pGeoDatabase;
108 :
109 2 : if (OGRGetDriverByName("OpenFileGDB") != nullptr)
110 : {
111 2 : std::wstring fgdb_error_desc_w;
112 2 : std::string fgdb_error_desc("Unknown error");
113 : fgdbError er;
114 1 : er = FileGDBAPI::ErrorInfo::GetErrorDescription(
115 : static_cast<fgdbError>(hr), fgdb_error_desc_w);
116 1 : if (er == S_OK)
117 : {
118 1 : fgdb_error_desc = WStringToString(fgdb_error_desc_w);
119 : }
120 1 : CPLDebug("FileGDB",
121 : "Cannot open %s with FileGDB driver: %s. Failing "
122 : "silently so OpenFileGDB can be tried",
123 : pszFilename, fgdb_error_desc.c_str());
124 : }
125 : else
126 : {
127 1 : GDBErr(hr, "Failed to open Geodatabase");
128 : }
129 2 : poMapConnections->erase(pszFilename);
130 2 : return nullptr;
131 : }
132 :
133 32 : CPLDebug("FileGDB", "Really opening %s", pszFilename);
134 32 : pConnection = new FGdbDatabaseConnection(pszFilename, pGeoDatabase);
135 32 : (*poMapConnections)[pszFilename] = pConnection;
136 : }
137 :
138 : FGdbDataSource *pDS;
139 :
140 36 : pDS = new FGdbDataSource(true, pConnection, bUseOpenFileGDB);
141 :
142 36 : if (!pDS->Open(pszFilename, /*bUpdate = */ FALSE, nullptr))
143 : {
144 0 : delete pDS;
145 0 : return nullptr;
146 : }
147 : else
148 : {
149 : auto poMutexedDS =
150 36 : new OGRMutexedDataSource(pDS, TRUE, FGdbDriver::hMutex, TRUE);
151 36 : return poMutexedDS;
152 : }
153 : }
154 :
155 : /***********************************************************************/
156 : /* OGRFileGDBDriverCreate() */
157 : /***********************************************************************/
158 :
159 : static GDALDataset *
160 6 : OGRFileGDBDriverCreate(const char *pszName, CPL_UNUSED int nBands,
161 : CPL_UNUSED int nXSize, CPL_UNUSED int nYSize,
162 : CPL_UNUSED GDALDataType eDT, char **papszOptions)
163 : {
164 : auto poOpenFileGDBDriver =
165 6 : GetGDALDriverManager()->GetDriverByName("OpenFileGDB");
166 6 : if (poOpenFileGDBDriver)
167 : {
168 6 : CPLError(
169 : CE_Warning, CPLE_AppDefined,
170 : "FileGDB driver has no longer any direct creation capabilities. "
171 : "Forwarding to OpenFileGDB driver");
172 6 : return poOpenFileGDBDriver->Create(pszName, 0, 0, 0, GDT_Unknown,
173 6 : papszOptions);
174 : }
175 0 : return nullptr;
176 : }
177 :
178 : /***********************************************************************/
179 : /* Release() */
180 : /***********************************************************************/
181 :
182 36 : void FGdbDriver::Release(const char *pszName)
183 : {
184 72 : CPLMutexHolderOptionalLockD(FGdbDriver::hMutex);
185 :
186 36 : if (poMapConnections == nullptr)
187 0 : poMapConnections = new std::map<CPLString, FGdbDatabaseConnection *>();
188 :
189 36 : FGdbDatabaseConnection *pConnection = (*poMapConnections)[pszName];
190 36 : if (pConnection != nullptr)
191 : {
192 36 : pConnection->m_nRefCount--;
193 36 : CPLDebug("FileGDB", "ref_count of %s = %d now", pszName,
194 : pConnection->m_nRefCount);
195 36 : if (pConnection->m_nRefCount == 0)
196 : {
197 32 : pConnection->CloseGeodatabase();
198 32 : delete pConnection;
199 32 : poMapConnections->erase(pszName);
200 : }
201 : }
202 36 : }
203 :
204 : /***********************************************************************/
205 : /* CloseGeodatabase() */
206 : /***********************************************************************/
207 :
208 32 : void FGdbDatabaseConnection::CloseGeodatabase()
209 : {
210 32 : if (m_pGeodatabase != nullptr)
211 : {
212 32 : CPLDebug("FileGDB", "Really closing %s now", m_osName.c_str());
213 32 : ::CloseGeodatabase(*m_pGeodatabase);
214 32 : delete m_pGeodatabase;
215 32 : m_pGeodatabase = nullptr;
216 : }
217 32 : }
218 :
219 : /***********************************************************************/
220 : /* OpenGeodatabase() */
221 : /***********************************************************************/
222 :
223 0 : int FGdbDatabaseConnection::OpenGeodatabase(const char *pszFSName)
224 : {
225 0 : m_pGeodatabase = new Geodatabase;
226 0 : long hr = ::OpenGeodatabase(StringToWString(CPLString(pszFSName)),
227 0 : *m_pGeodatabase);
228 0 : if (FAILED(hr))
229 : {
230 0 : delete m_pGeodatabase;
231 0 : m_pGeodatabase = nullptr;
232 0 : return FALSE;
233 : }
234 0 : return TRUE;
235 : }
236 :
237 : /************************************************************************/
238 : /* OGRFileGDBDeleteDataSource() */
239 : /************************************************************************/
240 :
241 1 : static CPLErr OGRFileGDBDeleteDataSource(const char *pszDataSource)
242 : {
243 2 : CPLMutexHolderD(&FGdbDriver::hMutex);
244 :
245 3 : std::wstring wstr = StringToWString(pszDataSource);
246 :
247 1 : long hr = 0;
248 :
249 1 : if (S_OK != (hr = ::DeleteGeodatabase(wstr)))
250 : {
251 0 : GDBErr(hr, "Failed to delete Geodatabase");
252 0 : return CE_Failure;
253 : }
254 :
255 1 : return CE_None;
256 : }
257 :
258 : /***********************************************************************/
259 : /* RegisterOGRFileGDB() */
260 : /***********************************************************************/
261 :
262 15 : void RegisterOGRFileGDB()
263 :
264 : {
265 15 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
266 0 : return;
267 :
268 15 : if (!GDAL_CHECK_VERSION("OGR FGDB"))
269 0 : return;
270 :
271 15 : GDALDriver *poDriver = new GDALDriver();
272 15 : OGRFileGDBDriverSetCommonMetadata(poDriver);
273 :
274 15 : poDriver->pfnOpen = OGRFileGDBDriverOpen;
275 15 : poDriver->pfnCreate = OGRFileGDBDriverCreate;
276 15 : poDriver->pfnDelete = OGRFileGDBDeleteDataSource;
277 15 : poDriver->pfnUnloadDriver = OGRFileGDBDriverUnload;
278 :
279 15 : GetGDALDriverManager()->RegisterDriver(poDriver);
280 : }
|