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