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 41 : OGRFileGDBGroup(const std::string &osParentName, const char *pszName)
42 41 : : GDALGroup(osParentName, pszName)
43 : {
44 41 : }
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 36 : FGdbDataSource::FGdbDataSource(bool bUseDriverMutex,
102 : FGdbDatabaseConnection *pConnection,
103 36 : bool bUseOpenFileGDB)
104 : : m_bUseDriverMutex(bUseDriverMutex), m_pConnection(pConnection),
105 : m_pGeodatabase(nullptr), m_poOpenFileGDBDrv(nullptr),
106 36 : m_bUseOpenFileGDB(bUseOpenFileGDB)
107 : {
108 36 : SetDescription(pConnection->m_osName.c_str());
109 36 : poDriver = GDALDriver::FromHandle(GDALGetDriverByName("FileGDB"));
110 36 : }
111 :
112 : /************************************************************************/
113 : /* ~FGdbDataSource() */
114 : /************************************************************************/
115 :
116 72 : FGdbDataSource::~FGdbDataSource()
117 : {
118 72 : CPLMutexHolderOptionalLockD(m_bUseDriverMutex ? FGdbDriver::hMutex
119 : : nullptr);
120 :
121 : // CloseInternal();
122 36 : size_t count = m_layers.size();
123 173 : for (size_t i = 0; i < count; ++i)
124 : {
125 137 : if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
126 : {
127 132 : poFgdbLayer->CloseGDBObjects();
128 : }
129 : }
130 :
131 36 : if (m_bUseDriverMutex)
132 36 : FGdbDriver::Release(m_osPublicName);
133 :
134 : // size_t count = m_layers.size();
135 173 : for (size_t i = 0; i < count; ++i)
136 : {
137 : // only delete layers coming from this driver -- the tables opened by
138 : // the OpenFileGDB driver will be deleted when we close the OpenFileGDB
139 : // datasource
140 137 : if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
141 : {
142 132 : delete poFgdbLayer;
143 : }
144 : }
145 72 : }
146 :
147 : /************************************************************************/
148 : /* Open() */
149 : /************************************************************************/
150 :
151 36 : int FGdbDataSource::Open(const char *pszNewName, int /* bUpdate */,
152 : const char *pszPublicName)
153 : {
154 36 : m_osFSName = pszNewName;
155 36 : m_osPublicName = (pszPublicName) ? pszPublicName : pszNewName;
156 36 : m_pGeodatabase = m_pConnection->GetGDB();
157 36 : m_poOpenFileGDBDrv = (GDALDriver *)GDALGetDriverByName("OpenFileGDB");
158 :
159 36 : return LoadLayers(L"\\");
160 : }
161 :
162 : /************************************************************************/
163 : /* CloseInternal() */
164 : /************************************************************************/
165 :
166 0 : int FGdbDataSource::CloseInternal(int bCloseGeodatabase)
167 : {
168 0 : size_t count = m_layers.size();
169 0 : for (size_t i = 0; i < count; ++i)
170 : {
171 0 : if (FGdbLayer *poFgdbLayer = dynamic_cast<FGdbLayer *>(m_layers[i]))
172 : {
173 0 : poFgdbLayer->CloseGDBObjects();
174 : }
175 : }
176 :
177 0 : if (m_pConnection && bCloseGeodatabase)
178 0 : m_pConnection->CloseGeodatabase();
179 0 : m_pGeodatabase = nullptr;
180 0 : return true;
181 : }
182 :
183 : /************************************************************************/
184 : /* OpenFGDBTables() */
185 : /************************************************************************/
186 :
187 44 : bool FGdbDataSource::OpenFGDBTables(OGRFileGDBGroup *group,
188 : const std::wstring &type,
189 : const std::vector<std::wstring> &layers)
190 : {
191 : #ifdef DISPLAY_RELATED_DATASETS
192 : std::vector<std::wstring> relationshipTypes;
193 : m_pGeodatabase->GetDatasetRelationshipTypes(relationshipTypes);
194 : std::vector<std::wstring> datasetTypes;
195 : m_pGeodatabase->GetDatasetTypes(datasetTypes);
196 : #endif
197 :
198 : fgdbError hr;
199 177 : for (unsigned int i = 0; i < layers.size(); i++)
200 : {
201 133 : Table *pTable = new Table;
202 : // CPLDebug("FGDB", "Opening %s", WStringToString(layers[i]).c_str());
203 133 : if (FAILED(hr = m_pGeodatabase->OpenTable(layers[i], *pTable)))
204 : {
205 1 : delete pTable;
206 :
207 2 : std::wstring fgdb_error_desc_w;
208 : fgdbError er;
209 1 : er = FileGDBAPI::ErrorInfo::GetErrorDescription(hr,
210 : fgdb_error_desc_w);
211 1 : const char *pszLikelyReason =
212 : "Might be due to unsupported spatial reference system. Using "
213 : "OpenFileGDB driver or FileGDB SDK >= 1.4 should solve it";
214 1 : if (er == S_OK)
215 : {
216 : std::string fgdb_error_desc =
217 2 : WStringToString(fgdb_error_desc_w);
218 1 : if (fgdb_error_desc == "FileGDB compression is not installed.")
219 : {
220 0 : pszLikelyReason = "Using FileGDB SDK 1.4 or later should "
221 : "solve this issue.";
222 : }
223 : }
224 :
225 1 : GDBErr(hr, "Error opening " + WStringToString(layers[i]),
226 : CE_Warning,
227 2 : (". Skipping it. " + CPLString(pszLikelyReason)).c_str());
228 1 : continue;
229 : }
230 :
231 : #ifdef DISPLAY_RELATED_DATASETS
232 : CPLDebug("FGDB", "Finding datasets related to %s",
233 : WStringToString(layers[i]).c_str());
234 : // Slow !
235 : for (const auto &relType : relationshipTypes)
236 : {
237 : for (const auto &itemType : datasetTypes)
238 : {
239 : std::vector<std::wstring> relatedDatasets;
240 : m_pGeodatabase->GetRelatedDatasets(layers[i], relType, itemType,
241 : relatedDatasets);
242 :
243 : std::vector<std::string> relatedDatasetDefs;
244 : m_pGeodatabase->GetRelatedDatasetDefinitions(
245 : layers[i], relType, itemType, relatedDatasetDefs);
246 :
247 : if (!relatedDatasets.empty() || !relatedDatasetDefs.empty())
248 : {
249 : CPLDebug("FGDB", "relationshipType: %s",
250 : WStringToString(relType).c_str());
251 : CPLDebug("FGDB", "itemType: %s",
252 : WStringToString(itemType).c_str());
253 : }
254 : for (const auto &name : relatedDatasets)
255 : {
256 : CPLDebug("FGDB", "Related dataset: %s",
257 : WStringToString(name).c_str());
258 : }
259 : for (const auto &xml : relatedDatasetDefs)
260 : {
261 : CPLDebug("FGDB", "Related dataset def: %s", xml.c_str());
262 : }
263 : }
264 : }
265 : #endif
266 :
267 132 : FGdbLayer *pLayer = new FGdbLayer();
268 132 : if (!pLayer->Initialize(this, pTable, layers[i], type))
269 : {
270 0 : delete pLayer;
271 0 : return GDBErr(hr, "Error initializing OGRLayer for " +
272 0 : WStringToString(layers[i]));
273 : }
274 :
275 132 : m_layers.push_back(pLayer);
276 132 : group->m_apoLayers.emplace_back(pLayer);
277 : }
278 44 : return true;
279 : }
280 :
281 : /************************************************************************/
282 : /* LoadLayers() */
283 : /************************************************************************/
284 :
285 36 : bool FGdbDataSource::LoadLayers(const std::wstring &root)
286 : {
287 72 : std::vector<wstring> tables;
288 72 : std::vector<wstring> featureclasses;
289 72 : std::vector<wstring> featuredatasets;
290 : fgdbError hr;
291 :
292 72 : auto poRootGroup = std::make_shared<OGRFileGDBGroup>(std::string(), "");
293 36 : m_poRootGroup = poRootGroup;
294 :
295 : /* Find all the Tables in the root */
296 36 : if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Table", tables)))
297 : {
298 0 : return GDBErr(hr, "Error reading Tables in " + WStringToString(root));
299 : }
300 : /* Open the tables we found */
301 36 : if (!tables.empty() && !OpenFGDBTables(poRootGroup.get(), L"Table", tables))
302 0 : return false;
303 :
304 : /* Find all the Feature Classes in the root */
305 36 : if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Class",
306 : featureclasses)))
307 : {
308 0 : return GDBErr(hr, "Error reading Feature Classes in " +
309 0 : WStringToString(root));
310 : }
311 : /* Open the tables we found */
312 69 : if (!featureclasses.empty() &&
313 69 : !OpenFGDBTables(poRootGroup.get(), L"Feature Class", featureclasses))
314 0 : return false;
315 :
316 : /* Find all the Feature Datasets in the root */
317 36 : if (FAILED(hr = m_pGeodatabase->GetChildDatasets(root, L"Feature Dataset",
318 : featuredatasets)))
319 : {
320 0 : return GDBErr(hr, "Error reading Feature Datasets in " +
321 0 : WStringToString(root));
322 : }
323 : /* Look for Feature Classes inside the Feature Dataset */
324 41 : for (unsigned int i = 0; i < featuredatasets.size(); i++)
325 : {
326 : const std::string featureDatasetPath(
327 5 : WStringToString(featuredatasets[i]));
328 5 : if (FAILED(hr = m_pGeodatabase->GetChildDatasets(
329 : featuredatasets[i], L"Feature Class", featureclasses)))
330 : {
331 0 : return GDBErr(hr, "Error reading Feature Classes in " +
332 0 : featureDatasetPath);
333 : }
334 5 : std::string featureDatasetName(featureDatasetPath);
335 5 : if (!featureDatasetName.empty() && featureDatasetPath[0] == '\\')
336 5 : featureDatasetName = featureDatasetName.substr(1);
337 : auto poFeatureDatasetGroup = std::make_shared<OGRFileGDBGroup>(
338 5 : poRootGroup->GetName(), featureDatasetName.c_str());
339 5 : poRootGroup->m_apoSubGroups.emplace_back(poFeatureDatasetGroup);
340 10 : if (!featureclasses.empty() &&
341 10 : !OpenFGDBTables(poFeatureDatasetGroup.get(), L"Feature Class",
342 : featureclasses))
343 0 : return false;
344 : }
345 :
346 : // Look for items which aren't present in the GDB_Items table (see
347 : // https://github.com/OSGeo/gdal/issues/4463) We do this by browsing the
348 : // catalog table (using the OpenFileGDB driver) and looking for items we
349 : // haven't yet found. If we find any, we have no choice but to load these
350 : // using the OpenFileGDB driver, as the ESRI SDK refuses to acknowledge that
351 : // they exist (despite ArcGIS itself showing them!)
352 36 : int iGDBItems = -1;
353 36 : const char *const apszDrivers[2] = {"OpenFileGDB", nullptr};
354 : const std::string osSystemCatalog =
355 72 : CPLFormFilenameSafe(m_osFSName, "a00000001", "gdbtable");
356 36 : if (m_bUseOpenFileGDB)
357 : {
358 34 : m_poOpenFileGDBDS.reset(GDALDataset::Open(osSystemCatalog.c_str(),
359 : GDAL_OF_VECTOR, apszDrivers,
360 : nullptr, nullptr));
361 : }
362 45 : if (m_poOpenFileGDBDS != nullptr &&
363 9 : m_poOpenFileGDBDS->GetLayer(0) != nullptr)
364 : {
365 9 : OGRLayer *poCatalogLayer = m_poOpenFileGDBDS->GetLayer(0);
366 : const int iNameIndex =
367 9 : poCatalogLayer->GetLayerDefn()->GetFieldIndex("Name");
368 9 : int i = -1;
369 106 : for (auto &poFeat : poCatalogLayer)
370 : {
371 97 : i++;
372 : const std::string osTableName =
373 97 : poFeat->GetFieldAsString(iNameIndex);
374 :
375 97 : if (osTableName.compare("GDB_Items") == 0)
376 : {
377 9 : iGDBItems = i;
378 : }
379 :
380 : // test if layer is already added
381 97 : if (GDALDataset::GetLayerByName(osTableName.c_str()))
382 19 : continue;
383 :
384 156 : const CPLString osLCTableName(CPLString(osTableName).tolower());
385 156 : const bool bIsPrivateLayer = osLCTableName.size() >= 4 &&
386 156 : osLCTableName.substr(0, 4) == "gdb_";
387 78 : if (!bIsPrivateLayer)
388 : {
389 6 : if (OGRLayer *poLayer =
390 6 : m_poOpenFileGDBDS->GetLayerByName(osTableName.c_str()))
391 : {
392 5 : m_layers.push_back(poLayer);
393 5 : poRootGroup->m_apoLayers.emplace_back(poLayer);
394 : }
395 : }
396 : }
397 : }
398 :
399 : // Read relationships. Note that we don't use the SDK method for this, as
400 : // it's too slow!
401 36 : if (iGDBItems >= 0)
402 : {
403 : const std::string osGDBItems = CPLFormFilenameSafe(
404 9 : m_osFSName, CPLSPrintf("a%08x", iGDBItems + 1), "gdbtable");
405 : std::unique_ptr<GDALDataset> poGDBItems(GDALDataset::Open(
406 9 : osGDBItems.c_str(), GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr));
407 9 : if (poGDBItems != nullptr && poGDBItems->GetLayer(0) != nullptr)
408 : {
409 9 : if (OGRLayer *poItemsLayer = poGDBItems->GetLayer(0))
410 : {
411 9 : const int iType = poItemsLayer->FindFieldIndex("Type", true);
412 : const int iDefinition =
413 9 : poItemsLayer->FindFieldIndex("Definition", true);
414 9 : if (iType < 0 || iDefinition < 0)
415 : {
416 0 : CPLError(CE_Failure, CPLE_AppDefined,
417 : "Wrong structure for GDB_Items table");
418 0 : return FALSE;
419 : }
420 :
421 : // Hunt for relationships in GDB_Items table
422 270 : for (const auto &poFeature : poItemsLayer)
423 : {
424 522 : CPLString osType;
425 261 : const char *pszType = poFeature->GetFieldAsString(iType);
426 261 : if (pszType != nullptr &&
427 261 : EQUAL(pszType, pszRelationshipTypeUUID))
428 : {
429 : // relationship item
430 : auto poRelationship = ParseXMLRelationshipDef(
431 33 : poFeature->GetFieldAsString(iDefinition));
432 11 : if (poRelationship)
433 : {
434 : const std::string relationshipName(
435 22 : poRelationship->GetName());
436 11 : m_osMapRelationships[relationshipName] =
437 22 : std::move(poRelationship);
438 : }
439 : }
440 : }
441 : }
442 : }
443 : }
444 :
445 36 : return true;
446 : }
447 :
448 : /************************************************************************/
449 : /* TestCapability() */
450 : /************************************************************************/
451 :
452 89 : int FGdbDataSource::TestCapability(const char *pszCap)
453 : {
454 89 : if (EQUAL(pszCap, ODsCMeasuredGeometries))
455 34 : return TRUE;
456 55 : else if (EQUAL(pszCap, ODsCZGeometries))
457 34 : return TRUE;
458 :
459 21 : return FALSE;
460 : }
461 :
462 : /************************************************************************/
463 : /* GetLayer() */
464 : /************************************************************************/
465 :
466 4134 : OGRLayer *FGdbDataSource::GetLayer(int iLayer)
467 : {
468 4134 : int count = static_cast<int>(m_layers.size());
469 :
470 4134 : if (iLayer < 0 || iLayer >= count)
471 2 : return nullptr;
472 : else
473 4132 : return m_layers[iLayer];
474 : }
475 :
476 : /************************************************************************/
477 : /* OGRFGdbSingleFeatureLayer */
478 : /************************************************************************/
479 :
480 : class OGRFGdbSingleFeatureLayer final : public OGRLayer
481 : {
482 : private:
483 : char *pszVal;
484 : OGRFeatureDefn *poFeatureDefn;
485 : int iNextShapeId;
486 :
487 : public:
488 : OGRFGdbSingleFeatureLayer(const char *pszLayerName, const char *pszVal);
489 : virtual ~OGRFGdbSingleFeatureLayer();
490 :
491 0 : virtual void ResetReading() override
492 : {
493 0 : iNextShapeId = 0;
494 0 : }
495 :
496 : virtual OGRFeature *GetNextFeature() override;
497 :
498 0 : virtual OGRFeatureDefn *GetLayerDefn() override
499 : {
500 0 : return poFeatureDefn;
501 : }
502 :
503 0 : virtual int TestCapability(const char *) override
504 : {
505 0 : return FALSE;
506 : }
507 : };
508 :
509 : /************************************************************************/
510 : /* OGRFGdbSingleFeatureLayer() */
511 : /************************************************************************/
512 :
513 34 : OGRFGdbSingleFeatureLayer::OGRFGdbSingleFeatureLayer(const char *pszLayerName,
514 34 : const char *pszValIn)
515 : {
516 34 : poFeatureDefn = new OGRFeatureDefn(pszLayerName);
517 34 : SetDescription(poFeatureDefn->GetName());
518 34 : poFeatureDefn->Reference();
519 34 : OGRFieldDefn oField("FIELD_1", OFTString);
520 34 : poFeatureDefn->AddFieldDefn(&oField);
521 :
522 34 : iNextShapeId = 0;
523 34 : pszVal = pszValIn ? CPLStrdup(pszValIn) : nullptr;
524 34 : }
525 :
526 : /************************************************************************/
527 : /* ~OGRFGdbSingleFeatureLayer() */
528 : /************************************************************************/
529 :
530 68 : OGRFGdbSingleFeatureLayer::~OGRFGdbSingleFeatureLayer()
531 : {
532 34 : if (poFeatureDefn != nullptr)
533 34 : poFeatureDefn->Release();
534 34 : CPLFree(pszVal);
535 68 : }
536 :
537 : /************************************************************************/
538 : /* GetNextFeature() */
539 : /************************************************************************/
540 :
541 51 : OGRFeature *OGRFGdbSingleFeatureLayer::GetNextFeature()
542 : {
543 51 : if (iNextShapeId != 0)
544 17 : return nullptr;
545 :
546 34 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
547 34 : if (pszVal)
548 34 : poFeature->SetField(0, pszVal);
549 34 : poFeature->SetFID(iNextShapeId++);
550 34 : return poFeature;
551 : }
552 :
553 : /************************************************************************/
554 : /* ExecuteSQL() */
555 : /************************************************************************/
556 :
557 125 : OGRLayer *FGdbDataSource::ExecuteSQL(const char *pszSQLCommand,
558 : OGRGeometry *poSpatialFilter,
559 : const char *pszDialect)
560 :
561 : {
562 125 : if (m_pGeodatabase == nullptr)
563 0 : return nullptr;
564 :
565 : /* -------------------------------------------------------------------- */
566 : /* Use generic implementation for recognized dialects */
567 : /* -------------------------------------------------------------------- */
568 125 : if (IsGenericSQLDialect(pszDialect))
569 0 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter,
570 0 : pszDialect);
571 :
572 : /* -------------------------------------------------------------------- */
573 : /* Special case GetLayerDefinition */
574 : /* -------------------------------------------------------------------- */
575 125 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
576 : {
577 36 : FGdbLayer *poLayer = (FGdbLayer *)GetLayerByName(
578 18 : pszSQLCommand + strlen("GetLayerDefinition "));
579 18 : if (poLayer)
580 : {
581 17 : char *pszVal = nullptr;
582 17 : poLayer->GetLayerXML(&pszVal);
583 : OGRLayer *poRet =
584 17 : new OGRFGdbSingleFeatureLayer("LayerDefinition", pszVal);
585 17 : CPLFree(pszVal);
586 17 : return poRet;
587 : }
588 : else
589 1 : return nullptr;
590 : }
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Special case GetLayerMetadata */
594 : /* -------------------------------------------------------------------- */
595 107 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
596 : {
597 36 : FGdbLayer *poLayer = (FGdbLayer *)GetLayerByName(
598 18 : pszSQLCommand + strlen("GetLayerMetadata "));
599 18 : if (poLayer)
600 : {
601 17 : char *pszVal = nullptr;
602 17 : poLayer->GetLayerMetadataXML(&pszVal);
603 : OGRLayer *poRet =
604 17 : new OGRFGdbSingleFeatureLayer("LayerMetadata", pszVal);
605 17 : CPLFree(pszVal);
606 17 : return poRet;
607 : }
608 : else
609 1 : return nullptr;
610 : }
611 :
612 : /* -------------------------------------------------------------------- */
613 : /* Special case REPACK */
614 : /* -------------------------------------------------------------------- */
615 89 : if (EQUAL(pszSQLCommand, "REPACK"))
616 : {
617 0 : auto hr = m_pGeodatabase->CompactDatabase();
618 0 : if (FAILED(hr))
619 : {
620 0 : GDBErr(hr, "CompactDatabase failed");
621 0 : return new OGRFGdbSingleFeatureLayer("result", "false");
622 : }
623 0 : return new OGRFGdbSingleFeatureLayer("result", "true");
624 : }
625 :
626 : /* TODO: remove that workaround when the SDK has finally a decent */
627 : /* SQL support ! */
628 89 : if (STARTS_WITH_CI(pszSQLCommand, "SELECT ") && pszDialect == nullptr)
629 : {
630 85 : CPLDebug("FGDB", "Support for SELECT is known to be partially "
631 : "non-compliant with FileGDB SDK API v1.2.\n"
632 : "So for now, we use default OGR SQL engine. "
633 : "Explicitly specify -dialect FileGDB\n"
634 : "to use the SQL engine from the FileGDB SDK API");
635 : OGRLayer *poLayer =
636 85 : GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
637 85 : if (poLayer)
638 84 : m_oSetSelectLayers.insert(poLayer);
639 85 : return poLayer;
640 : }
641 :
642 : /* -------------------------------------------------------------------- */
643 : /* Run the SQL */
644 : /* -------------------------------------------------------------------- */
645 4 : EnumRows *pEnumRows = new EnumRows;
646 : long hr;
647 : try
648 : {
649 4 : hr = m_pGeodatabase->ExecuteSQL(StringToWString(pszSQLCommand), true,
650 : *pEnumRows);
651 : }
652 0 : catch (...)
653 : {
654 0 : CPLError(CE_Failure, CPLE_AppDefined,
655 : "Exception occurred at executing '%s'. Application may "
656 : "become unstable",
657 : pszSQLCommand);
658 0 : delete pEnumRows;
659 0 : return nullptr;
660 : }
661 :
662 4 : if (FAILED(hr))
663 : {
664 1 : GDBErr(hr, CPLSPrintf("Failed at executing '%s'", pszSQLCommand));
665 1 : delete pEnumRows;
666 1 : return nullptr;
667 : }
668 :
669 3 : if (STARTS_WITH_CI(pszSQLCommand, "SELECT "))
670 : {
671 2 : OGRLayer *poLayer = new FGdbResultLayer(this, pszSQLCommand, pEnumRows);
672 2 : m_oSetSelectLayers.insert(poLayer);
673 2 : return poLayer;
674 : }
675 : else
676 : {
677 1 : delete pEnumRows;
678 1 : return nullptr;
679 : }
680 : }
681 :
682 : /************************************************************************/
683 : /* ReleaseResultSet() */
684 : /************************************************************************/
685 :
686 120 : void FGdbDataSource::ReleaseResultSet(OGRLayer *poResultsSet)
687 : {
688 120 : if (poResultsSet)
689 120 : m_oSetSelectLayers.erase(poResultsSet);
690 120 : delete poResultsSet;
691 120 : }
692 :
693 : /************************************************************************/
694 : /* GetFieldDomain() */
695 : /************************************************************************/
696 :
697 : const OGRFieldDomain *
698 3 : FGdbDataSource::GetFieldDomain(const std::string &name) const
699 : {
700 3 : const auto baseRet = GDALDataset::GetFieldDomain(name);
701 3 : if (baseRet)
702 0 : return baseRet;
703 :
704 6 : std::string domainDef;
705 : const auto hr =
706 3 : m_pGeodatabase->GetDomainDefinition(StringToWString(name), domainDef);
707 3 : if (FAILED(hr))
708 : {
709 : // GDBErr(hr, "Failed in GetDomainDefinition()");
710 1 : return nullptr;
711 : }
712 :
713 4 : auto poDomain = ParseXMLFieldDomainDef(domainDef);
714 2 : if (!poDomain)
715 0 : return nullptr;
716 4 : const std::string domainName(poDomain->GetName());
717 2 : m_oMapFieldDomains[domainName] = std::move(poDomain);
718 2 : return GDALDataset::GetFieldDomain(name);
719 : }
720 :
721 : /************************************************************************/
722 : /* GetFieldDomainNames() */
723 : /************************************************************************/
724 :
725 1 : std::vector<std::string> FGdbDataSource::GetFieldDomainNames(CSLConstList) const
726 : {
727 2 : std::vector<std::wstring> oDomainNamesWList;
728 1 : const auto hr = m_pGeodatabase->GetDomains(oDomainNamesWList);
729 1 : if (FAILED(hr))
730 : {
731 0 : GDBErr(hr, "Failed in GetDomains()");
732 0 : return std::vector<std::string>();
733 : }
734 :
735 2 : std::vector<std::string> oDomainNamesList;
736 1 : oDomainNamesList.reserve(oDomainNamesWList.size());
737 4 : for (const auto &osNameW : oDomainNamesWList)
738 : {
739 3 : oDomainNamesList.emplace_back(WStringToString(osNameW));
740 : }
741 1 : return oDomainNamesList;
742 : }
743 :
744 : /************************************************************************/
745 : /* GetRelationshipNames() */
746 : /************************************************************************/
747 :
748 : std::vector<std::string>
749 3 : FGdbDataSource::GetRelationshipNames(CPL_UNUSED CSLConstList papszOptions) const
750 :
751 : {
752 3 : std::vector<std::string> oasNames;
753 3 : oasNames.reserve(m_osMapRelationships.size());
754 14 : for (auto it = m_osMapRelationships.begin();
755 25 : it != m_osMapRelationships.end(); ++it)
756 : {
757 11 : oasNames.emplace_back(it->first);
758 : }
759 3 : return oasNames;
760 : }
761 :
762 : /************************************************************************/
763 : /* GetRelationship() */
764 : /************************************************************************/
765 :
766 : const GDALRelationship *
767 8 : FGdbDataSource::GetRelationship(const std::string &name) const
768 :
769 : {
770 8 : auto it = m_osMapRelationships.find(name);
771 8 : if (it == m_osMapRelationships.end())
772 1 : return nullptr;
773 :
774 7 : return it->second.get();
775 : }
|