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