Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements Open FileGDB OGR driver.
5 : * Author: Even Rouault, <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "ogr_openfilegdb.h"
15 :
16 : #include <stddef.h>
17 : #include <stdio.h>
18 : #include <string.h>
19 : #include <map>
20 : #include <memory>
21 : #include <set>
22 : #include <string>
23 : #include <utility>
24 : #include <vector>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi.h"
30 : #include "filegdbtable.h"
31 : #include "gdal.h"
32 : #include "ogr_core.h"
33 : #include "ogr_feature.h"
34 : #include "ogr_geometry.h"
35 : #include "memdataset.h"
36 : #include "ogrsf_frmts.h"
37 : #include "ogr_swq.h"
38 :
39 : #include "filegdb_fielddomain.h"
40 : #include "filegdb_relationship.h"
41 :
42 : /************************************************************************/
43 : /* OGROpenFileGDBGroup */
44 : /************************************************************************/
45 :
46 : class OGROpenFileGDBGroup final : public GDALGroup
47 : {
48 : protected:
49 : friend class OGROpenFileGDBDataSource;
50 : std::vector<std::shared_ptr<GDALGroup>> m_apoSubGroups{};
51 : std::vector<OGRLayer *> m_apoLayers{};
52 : std::string m_osDefinition{};
53 :
54 : public:
55 518 : OGROpenFileGDBGroup(const std::string &osParentName, const char *pszName)
56 518 : : GDALGroup(osParentName, pszName)
57 : {
58 518 : }
59 :
60 21 : void SetDefinition(const std::string &osDefinition)
61 : {
62 21 : m_osDefinition = osDefinition;
63 21 : }
64 :
65 28 : const std::string &GetDefinition() const
66 : {
67 28 : return m_osDefinition;
68 : }
69 :
70 : std::vector<std::string>
71 : GetGroupNames(CSLConstList papszOptions) const override;
72 : std::shared_ptr<GDALGroup>
73 : OpenGroup(const std::string &osName,
74 : CSLConstList papszOptions) const override;
75 :
76 : std::vector<std::string>
77 : GetVectorLayerNames(CSLConstList papszOptions) const override;
78 : OGRLayer *OpenVectorLayer(const std::string &osName,
79 : CSLConstList papszOptions) const override;
80 : };
81 :
82 : /************************************************************************/
83 : /* OGROpenFileGDBDataSource() */
84 : /************************************************************************/
85 808 : OGROpenFileGDBDataSource::OGROpenFileGDBDataSource()
86 808 : : m_papszFiles(nullptr), bLastSQLUsedOptimizedImplementation(false)
87 : {
88 808 : }
89 :
90 : /************************************************************************/
91 : /* ~OGROpenFileGDBDataSource() */
92 : /************************************************************************/
93 1616 : OGROpenFileGDBDataSource::~OGROpenFileGDBDataSource()
94 :
95 : {
96 808 : OGROpenFileGDBDataSource::Close();
97 1616 : }
98 :
99 : /************************************************************************/
100 : /* Close() */
101 : /************************************************************************/
102 :
103 1525 : CPLErr OGROpenFileGDBDataSource::Close(GDALProgressFunc, void *)
104 : {
105 1525 : CPLErr eErr = CE_None;
106 1525 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
107 : {
108 808 : if (m_bInTransaction)
109 2 : OGROpenFileGDBDataSource::RollbackTransaction();
110 :
111 808 : if (OGROpenFileGDBDataSource::FlushCache(true) != CE_None)
112 0 : eErr = CE_Failure;
113 :
114 808 : m_apoLayers.clear();
115 808 : m_apoHiddenLayers.clear();
116 808 : CSLDestroy(m_papszFiles);
117 :
118 808 : if (GDALDataset::Close() != CE_None)
119 0 : eErr = CE_Failure;
120 : }
121 1525 : return eErr;
122 : }
123 :
124 : /************************************************************************/
125 : /* FileExists() */
126 : /************************************************************************/
127 :
128 17512 : int OGROpenFileGDBDataSource::FileExists(const char *pszFilename)
129 : {
130 17512 : if (m_papszFiles)
131 17503 : return CSLFindString(m_papszFiles, CPLGetFilename(pszFilename)) >= 0;
132 :
133 : VSIStatBufL sStat;
134 9 : CPLString osFilename(pszFilename);
135 9 : return VSIStatExL(osFilename, &sStat, VSI_STAT_EXISTS_FLAG) == 0;
136 : }
137 :
138 : /************************************************************************/
139 : /* Open() */
140 : /************************************************************************/
141 :
142 563 : bool OGROpenFileGDBDataSource::Open(const GDALOpenInfo *poOpenInfo,
143 : bool &bRetryFileGDBOut)
144 : {
145 563 : bRetryFileGDBOut = false;
146 :
147 563 : if (poOpenInfo->nOpenFlags == (GDAL_OF_RASTER | GDAL_OF_UPDATE))
148 : {
149 0 : CPLError(CE_Failure, CPLE_NotSupported,
150 : "Update mode of rasters is not supported");
151 0 : return false;
152 : }
153 :
154 563 : m_osDirName = poOpenInfo->pszFilename;
155 :
156 1126 : std::string osRasterLayerName;
157 563 : if (STARTS_WITH(poOpenInfo->pszFilename, "OpenFileGDB:"))
158 : {
159 : const CPLStringList aosTokens(CSLTokenizeString2(
160 29 : poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS));
161 29 : if (aosTokens.size() == 4 && strlen(aosTokens[1]) == 1)
162 : {
163 0 : m_osDirName = aosTokens[1];
164 0 : m_osDirName += ':';
165 0 : m_osDirName += aosTokens[2];
166 0 : osRasterLayerName = aosTokens[3];
167 : }
168 29 : else if (aosTokens.size() == 3)
169 : {
170 29 : m_osDirName = aosTokens[1];
171 29 : osRasterLayerName = aosTokens[2];
172 : }
173 : else
174 : {
175 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid connection string");
176 0 : return false;
177 : }
178 : }
179 :
180 1126 : FileGDBTable oTable;
181 :
182 : // Whether to open directly a given .gdbtable file (mostly for debugging
183 : // purposes)
184 563 : int nInterestTable = 0;
185 563 : unsigned int unInterestTable = 0;
186 563 : const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
187 1205 : if (poOpenInfo->nHeaderBytes > 0 &&
188 79 : strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
189 22 : pszFilenameWithoutPath[0] == 'a' &&
190 663 : EQUAL(pszFilenameWithoutPath + strlen("a00000000"), ".gdbtable") &&
191 21 : sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
192 : {
193 21 : nInterestTable = static_cast<int>(unInterestTable);
194 21 : m_osDirName = CPLGetPathSafe(m_osDirName);
195 : }
196 :
197 655 : if (EQUAL(CPLGetExtensionSafe(m_osDirName).c_str(), "zip") &&
198 92 : !STARTS_WITH(m_osDirName, "/vsizip/"))
199 : {
200 85 : m_osDirName = "/vsizip/" + m_osDirName;
201 : }
202 478 : else if (EQUAL(CPLGetExtensionSafe(m_osDirName).c_str(), "tar") &&
203 0 : !STARTS_WITH(m_osDirName, "/vsitar/"))
204 : {
205 0 : m_osDirName = "/vsitar/" + m_osDirName;
206 : }
207 :
208 1031 : if (STARTS_WITH(m_osDirName, "/vsizip/") ||
209 468 : STARTS_WITH(m_osDirName, "/vsitar/"))
210 : {
211 : /* Look for one subdirectory ending with .gdb extension */
212 96 : char **papszDir = VSIReadDir(m_osDirName);
213 96 : int iCandidate = -1;
214 822 : for (int i = 0; papszDir && papszDir[i] != nullptr; i++)
215 : {
216 : VSIStatBufL sStat;
217 726 : if (EQUAL(CPLGetExtensionSafe(papszDir[i]).c_str(), "gdb") &&
218 85 : VSIStatL(CPLSPrintf("%s/%s", m_osDirName.c_str(), papszDir[i]),
219 811 : &sStat) == 0 &&
220 85 : VSI_ISDIR(sStat.st_mode))
221 : {
222 85 : if (iCandidate < 0)
223 85 : iCandidate = i;
224 : else
225 : {
226 0 : iCandidate = -1;
227 0 : break;
228 : }
229 : }
230 : }
231 96 : if (iCandidate >= 0)
232 : {
233 85 : m_osDirName += "/";
234 85 : m_osDirName += papszDir[iCandidate];
235 : }
236 96 : CSLDestroy(papszDir);
237 : }
238 :
239 : const std::string osTransactionBackupDirname = CPLFormFilenameSafe(
240 1126 : m_osDirName.c_str(), ".ogrtransaction_backup", nullptr);
241 : VSIStatBufL sStat;
242 563 : if (VSIStatL(osTransactionBackupDirname.c_str(), &sStat) == 0)
243 : {
244 2 : if (poOpenInfo->eAccess == GA_Update)
245 : {
246 1 : CPLError(
247 : CE_Failure, CPLE_AppDefined,
248 : "A previous backup directory %s already exists, which means "
249 : "that a previous transaction was not cleanly committed or "
250 : "rolled back.\n"
251 : "Either manually restore the previous state from that "
252 : "directory or remove it, before opening in update mode.",
253 : osTransactionBackupDirname.c_str());
254 1 : return false;
255 : }
256 : else
257 : {
258 1 : CPLError(
259 : CE_Warning, CPLE_AppDefined,
260 : "A previous backup directory %s already exists, which means "
261 : "that a previous transaction was not cleanly committed or "
262 : "rolled back.\n"
263 : "You may want to restore the previous state from that "
264 : "directory or remove it.",
265 : osTransactionBackupDirname.c_str());
266 : }
267 : }
268 :
269 562 : m_papszFiles = VSIReadDir(m_osDirName);
270 :
271 : /* Explore catalog table */
272 : m_osGDBSystemCatalogFilename =
273 562 : CPLFormFilenameSafe(m_osDirName, "a00000001", "gdbtable");
274 1114 : if (!FileExists(m_osGDBSystemCatalogFilename.c_str()) ||
275 552 : !oTable.Open(m_osGDBSystemCatalogFilename.c_str(),
276 552 : poOpenInfo->eAccess == GA_Update))
277 : {
278 36 : if (nInterestTable > 0 && FileExists(poOpenInfo->pszFilename))
279 : {
280 1 : const char *pszLyrName = CPLSPrintf("a%08x", nInterestTable);
281 : auto poLayer = std::make_unique<OGROpenFileGDBLayer>(
282 1 : this, poOpenInfo->pszFilename, pszLyrName, "", "",
283 2 : eAccess == GA_Update);
284 : const std::string osTablX =
285 2 : CPLResetExtensionSafe(poOpenInfo->pszFilename, "gdbtablx");
286 1 : if ((!FileExists(osTablX.c_str()) &&
287 0 : poLayer->GetLayerDefn()->GetFieldCount() == 0 &&
288 2 : poLayer->GetFeatureCount() == 0) ||
289 1 : !poLayer->IsValidLayerDefn())
290 : {
291 0 : return false;
292 : }
293 1 : m_apoLayers.push_back(std::move(poLayer));
294 1 : return true;
295 : }
296 35 : return false;
297 : }
298 :
299 526 : const int idxName = oTable.GetFieldIdx("Name");
300 526 : const int idxFileformat = oTable.GetFieldIdx("FileFormat");
301 2098 : if (!(idxName >= 0 && idxFileformat >= 0 &&
302 524 : oTable.GetField(idxName)->GetType() == FGFT_STRING &&
303 1048 : (oTable.GetField(idxFileformat)->GetType() == FGFT_INT16 ||
304 524 : oTable.GetField(idxFileformat)->GetType() == FGFT_INT32)))
305 : {
306 2 : return false;
307 : }
308 :
309 524 : int iGDBItems = -1; /* V10 */
310 524 : int iGDBFeatureClasses = -1; /* V9.X */
311 524 : int iGDBObjectClasses = -1; /* V9.X */
312 :
313 1048 : std::vector<std::string> aosTableNames;
314 : try
315 : {
316 20180 : for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
317 : {
318 19656 : if (!oTable.SelectRow(i))
319 : {
320 5003 : if (oTable.HasGotError())
321 0 : break;
322 5003 : aosTableNames.push_back("");
323 5003 : continue;
324 : }
325 :
326 14653 : const OGRField *psField = oTable.GetFieldValue(idxName);
327 14653 : if (psField != nullptr)
328 : {
329 14563 : aosTableNames.push_back(psField->String);
330 14563 : if (strcmp(psField->String, "GDB_SpatialRefs") == 0)
331 : {
332 : // Normally a00000003
333 1044 : m_osGDBSpatialRefsFilename = CPLFormFilenameSafe(
334 : m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
335 522 : nullptr);
336 : }
337 14041 : else if (strcmp(psField->String, "GDB_Items") == 0)
338 : {
339 : // Normally a00000004 but it has been seen in some datasets
340 : // to not be a00000004
341 994 : m_osGDBItemsFilename = CPLFormFilenameSafe(
342 : m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
343 497 : nullptr);
344 497 : iGDBItems = i;
345 : }
346 13544 : else if (strcmp(psField->String, "GDB_ItemRelationships") == 0)
347 : {
348 : // Normally a00000006 but can be a00000005 sometimes (e.g
349 : // autotest/ogr/data/filegdb/Domains.gdb)
350 998 : m_osGDBItemRelationshipsFilename = CPLFormFilenameSafe(
351 : m_osDirName, CPLSPrintf("a%08x.gdbtable", i + 1),
352 499 : nullptr);
353 : }
354 13045 : else if (strcmp(psField->String, "GDB_FeatureClasses") == 0)
355 : {
356 : // FileGDB v9
357 23 : iGDBFeatureClasses = i;
358 : }
359 13022 : else if (strcmp(psField->String, "GDB_ObjectClasses") == 0)
360 : {
361 : // FileGDB v9
362 23 : iGDBObjectClasses = i;
363 : }
364 14563 : m_osMapNameToIdx[psField->String] = 1 + i;
365 : }
366 : else
367 : {
368 90 : aosTableNames.push_back("");
369 : }
370 : }
371 : }
372 0 : catch (const std::exception &)
373 : {
374 0 : return false;
375 : }
376 :
377 524 : oTable.Close();
378 :
379 1048 : std::set<int> oSetIgnoredRasterLayerTableNum;
380 524 : if (iGDBItems >= 0)
381 : {
382 497 : eAccess = poOpenInfo->eAccess;
383 :
384 497 : const bool bRet = OpenFileGDBv10(
385 : iGDBItems, nInterestTable, poOpenInfo, osRasterLayerName,
386 : oSetIgnoredRasterLayerTableNum, bRetryFileGDBOut);
387 497 : if (!bRet)
388 2 : return false;
389 : }
390 27 : else if (iGDBFeatureClasses >= 0 && iGDBObjectClasses >= 0)
391 : {
392 23 : if (poOpenInfo->eAccess == GA_Update)
393 : {
394 0 : CPLError(CE_Failure, CPLE_NotSupported,
395 : "Edition of existing FileGDB v9 datasets not supported");
396 0 : return false;
397 : }
398 :
399 23 : int bRet = OpenFileGDBv9(iGDBFeatureClasses, iGDBObjectClasses,
400 : nInterestTable, poOpenInfo, osRasterLayerName,
401 : oSetIgnoredRasterLayerTableNum);
402 23 : if (!bRet)
403 23 : return false;
404 : }
405 : else
406 : {
407 4 : CPLError(CE_Failure, CPLE_AppDefined,
408 : "No GDB_Items nor GDB_FeatureClasses table");
409 4 : return false;
410 : }
411 :
412 518 : if (m_apoLayers.empty() && nInterestTable > 0)
413 : {
414 19 : if (FileExists(poOpenInfo->pszFilename))
415 : {
416 19 : const char *pszLyrName = nullptr;
417 38 : if (nInterestTable <= static_cast<int>(aosTableNames.size()) &&
418 19 : !aosTableNames[nInterestTable - 1].empty())
419 19 : pszLyrName = aosTableNames[nInterestTable - 1].c_str();
420 : else
421 0 : pszLyrName = CPLSPrintf("a%08x", nInterestTable);
422 19 : m_apoLayers.push_back(std::make_unique<OGROpenFileGDBLayer>(
423 19 : this, poOpenInfo->pszFilename, pszLyrName, "", "",
424 38 : eAccess == GA_Update));
425 : }
426 : else
427 : {
428 0 : return false;
429 : }
430 : }
431 :
432 518 : if (nInterestTable == 0)
433 : {
434 498 : const bool bListAllTables = CPLTestBool(CSLFetchNameValueDef(
435 498 : poOpenInfo->papszOpenOptions, "LIST_ALL_TABLES", "NO"));
436 :
437 : // add additional tables which are not present in the
438 : // GDB_Items/GDB_FeatureClasses/GDB_ObjectClasses tables
439 11000 : for (const auto &oIter : m_osMapNameToIdx)
440 : {
441 : // test if layer is already added
442 10502 : if (GDALDataset::GetLayerByName(oIter.first.c_str()))
443 0 : continue;
444 :
445 10502 : if (bListAllTables || !IsPrivateLayerName(oIter.first))
446 : {
447 5905 : const int idx = oIter.second;
448 5905 : const CPLString osFilename(CPLFormFilenameSafe(
449 11810 : m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"));
450 11570 : if (!cpl::contains(oSetIgnoredRasterLayerTableNum, idx) &&
451 5665 : FileExists(osFilename))
452 : {
453 : m_apoLayers.emplace_back(
454 5284 : std::make_unique<OGROpenFileGDBLayer>(
455 5284 : this, osFilename, oIter.first.c_str(), "", "",
456 10568 : eAccess == GA_Update));
457 5284 : if (m_poRootGroup)
458 : {
459 5260 : cpl::down_cast<OGROpenFileGDBGroup *>(
460 : m_poRootGroup.get())
461 : ->m_apoLayers.emplace_back(
462 5260 : m_apoLayers.back().get());
463 : }
464 : }
465 : }
466 : }
467 : }
468 :
469 518 : if (!osRasterLayerName.empty())
470 : {
471 29 : m_aosSubdatasets.Clear();
472 29 : SetDescription(poOpenInfo->pszFilename);
473 29 : return nBands != 0;
474 : }
475 :
476 : // If opening in raster-only mode, return false if there are no raster
477 : // layers
478 489 : if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
479 50 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) == 0)
480 : {
481 21 : if (m_aosSubdatasets.empty())
482 : {
483 13 : return false;
484 : }
485 8 : else if (m_aosSubdatasets.size() == 2)
486 : {
487 : // If there is a single raster dataset, open it right away.
488 7 : return true;
489 : }
490 : }
491 : // If opening in vector-only mode, return false if there are no vector
492 : // layers and opened in read-only mode
493 468 : else if ((poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
494 439 : (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0)
495 : {
496 436 : if (m_apoLayers.empty() && poOpenInfo->eAccess == GA_ReadOnly)
497 : {
498 2 : return false;
499 : }
500 : }
501 :
502 467 : return true;
503 : }
504 :
505 : /************************************************************************/
506 : /* AddLayer() */
507 : /************************************************************************/
508 :
509 3801 : OGRLayer *OGROpenFileGDBDataSource::AddLayer(
510 : const CPLString &osName, int nInterestTable, int &nCandidateLayers,
511 : int &nLayersCDF, const CPLString &osDefinition,
512 : const CPLString &osDocumentation, OGRwkbGeometryType eGeomType,
513 : const std::string &osParentDefinition)
514 : {
515 3801 : const auto oIter = m_osMapNameToIdx.find(osName);
516 3801 : int idx = 0;
517 3801 : if (oIter != m_osMapNameToIdx.end())
518 3787 : idx = oIter->second;
519 3801 : if (idx > 0 && (nInterestTable <= 0 || nInterestTable == idx))
520 : {
521 3710 : m_osMapNameToIdx.erase(oIter);
522 :
523 7420 : const CPLString osFilename = CPLFormFilenameSafe(
524 7420 : m_osDirName, CPLOPrintf("a%08x", idx).c_str(), "gdbtable");
525 3710 : if (FileExists(osFilename))
526 : {
527 3696 : nCandidateLayers++;
528 :
529 3696 : if (m_papszFiles != nullptr)
530 : {
531 : const CPLString osSDC =
532 3696 : CPLResetExtensionSafe(osFilename, "gdbtable.sdc");
533 : const CPLString osCDF =
534 3696 : CPLResetExtensionSafe(osFilename, "gdbtable.cdf");
535 3696 : if (FileExists(osCDF))
536 : {
537 3 : nLayersCDF++;
538 :
539 3 : if (GDALGetDriverByName("FileGDB"))
540 : {
541 2 : CPLDebug(
542 : "OpenFileGDB",
543 : "%s layer has a %s file using Compressed Data "
544 : "Format (CDF) that is unhandled by the OpenFileGDB "
545 : "driver, but could be handled by the FileGDB "
546 : "driver (which will be attempted).",
547 : osName.c_str(), osCDF.c_str());
548 : }
549 : else
550 : {
551 : // Look into hidden drivers, that is drivers that have
552 : // been built as plugins, but are not currently available
553 : // on the system.
554 2 : std::string osFileGDBDriverMsg;
555 1 : auto poDM = GetGDALDriverManager();
556 : const int nAllDrivercount =
557 1 : poDM->GetDriverCount(/* bIncludeHidden = */ true);
558 229 : for (int i = 0; i < nAllDrivercount; ++i)
559 : {
560 : auto poMissingPluginDriver =
561 228 : poDM->GetDriver(i, /* bIncludeHidden = */ true);
562 228 : if (EQUAL(poMissingPluginDriver->GetDescription(),
563 : "FileGDB"))
564 : {
565 0 : osFileGDBDriverMsg += " However the plugin ";
566 : osFileGDBDriverMsg +=
567 : poMissingPluginDriver->GetMetadataItem(
568 0 : "MISSING_PLUGIN_FILENAME");
569 : osFileGDBDriverMsg +=
570 : " is not available in your "
571 0 : "installation.";
572 0 : if (const char *pszInstallationMsg =
573 0 : poMissingPluginDriver->GetMetadataItem(
574 0 : GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE))
575 : {
576 0 : osFileGDBDriverMsg += " ";
577 0 : osFileGDBDriverMsg += pszInstallationMsg;
578 : }
579 0 : break;
580 : }
581 : }
582 1 : CPLError(CE_Warning, CPLE_AppDefined,
583 : "%s layer has a %s file using Compressed Data "
584 : "Format (CDF) that is unhandled by the "
585 : "OpenFileGDB driver, but could be handled by "
586 : "the FileGDB driver.%s",
587 : osName.c_str(), osCDF.c_str(),
588 : osFileGDBDriverMsg.c_str());
589 : }
590 3 : return nullptr;
591 : }
592 3693 : else if (FileExists(osSDC))
593 : {
594 : // SDC is not handled by the FileGDB driver
595 0 : CPLError(CE_Warning, CPLE_AppDefined,
596 : "%s layer has a %s file using Smart Data "
597 : "Compression (SDC) that is unhandled.",
598 : osName.c_str(), osSDC.c_str());
599 0 : return nullptr;
600 : }
601 : }
602 :
603 3693 : m_apoLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
604 : this, osFilename, osName, osDefinition, osDocumentation,
605 3693 : eAccess == GA_Update, eGeomType, osParentDefinition));
606 3693 : return m_apoLayers.back().get();
607 : }
608 : }
609 105 : return nullptr;
610 : }
611 :
612 11 : std::vector<std::string> OGROpenFileGDBGroup::GetGroupNames(CSLConstList) const
613 : {
614 11 : std::vector<std::string> ret;
615 18 : for (const auto &poSubGroup : m_apoSubGroups)
616 7 : ret.emplace_back(poSubGroup->GetName());
617 11 : return ret;
618 : }
619 :
620 : std::shared_ptr<GDALGroup>
621 6 : OGROpenFileGDBGroup::OpenGroup(const std::string &osName, CSLConstList) const
622 : {
623 10 : for (const auto &poSubGroup : m_apoSubGroups)
624 : {
625 9 : if (poSubGroup->GetName() == osName)
626 5 : return poSubGroup;
627 : }
628 1 : return nullptr;
629 : }
630 :
631 : std::vector<std::string>
632 11 : OGROpenFileGDBGroup::GetVectorLayerNames(CSLConstList) const
633 : {
634 11 : std::vector<std::string> ret;
635 49 : for (const auto &poLayer : m_apoLayers)
636 38 : ret.emplace_back(poLayer->GetName());
637 11 : return ret;
638 : }
639 :
640 23 : OGRLayer *OGROpenFileGDBGroup::OpenVectorLayer(const std::string &osName,
641 : CSLConstList) const
642 : {
643 105 : for (const auto &poLayer : m_apoLayers)
644 : {
645 104 : if (poLayer->GetName() == osName)
646 22 : return poLayer;
647 : }
648 1 : return nullptr;
649 : }
650 :
651 : /************************************************************************/
652 : /* OpenFileGDBv10() */
653 : /************************************************************************/
654 :
655 497 : bool OGROpenFileGDBDataSource::OpenFileGDBv10(
656 : int iGDBItems, int nInterestTable, const GDALOpenInfo *poOpenInfo,
657 : const std::string &osRasterLayerName,
658 : std::set<int> &oSetIgnoredRasterLayerTableNum, bool &bRetryFileGDBOut)
659 : {
660 :
661 497 : CPLDebug("OpenFileGDB", "FileGDB v10 or later");
662 :
663 994 : FileGDBTable oTable;
664 :
665 497 : const CPLString osFilename(CPLFormFilenameSafe(
666 994 : m_osDirName, CPLSPrintf("a%08x.gdbtable", iGDBItems + 1), nullptr));
667 :
668 : // Normally we don't need to update in update mode the GDB_Items table,
669 : // but this may help repairing it, if we have corrupted it with past
670 : // GDAL versions.
671 497 : const bool bOpenInUpdateMode =
672 497 : (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0;
673 497 : if (!oTable.Open(osFilename, bOpenInUpdateMode))
674 0 : return false;
675 :
676 497 : const int iUUID = oTable.GetFieldIdx("UUID");
677 497 : const int iType = oTable.GetFieldIdx("Type");
678 497 : const int iName = oTable.GetFieldIdx("Name");
679 497 : const int iPath = oTable.GetFieldIdx("Path");
680 497 : const int iDefinition = oTable.GetFieldIdx("Definition");
681 497 : const int iDocumentation = oTable.GetFieldIdx("Documentation");
682 497 : if (iUUID < 0 || iType < 0 || iName < 0 || iPath < 0 || iDefinition < 0 ||
683 497 : iDocumentation < 0 ||
684 994 : oTable.GetField(iUUID)->GetType() != FGFT_GLOBALID ||
685 994 : oTable.GetField(iType)->GetType() != FGFT_GUID ||
686 994 : oTable.GetField(iName)->GetType() != FGFT_STRING ||
687 994 : oTable.GetField(iPath)->GetType() != FGFT_STRING ||
688 1491 : oTable.GetField(iDefinition)->GetType() != FGFT_XML ||
689 497 : oTable.GetField(iDocumentation)->GetType() != FGFT_XML)
690 : {
691 0 : CPLError(CE_Failure, CPLE_AppDefined,
692 : "Wrong structure for GDB_Items table");
693 0 : return false;
694 : }
695 :
696 994 : auto poRootGroup = std::make_shared<OGROpenFileGDBGroup>(std::string(), "");
697 497 : m_poRootGroup = poRootGroup;
698 : std::map<std::string, std::shared_ptr<OGROpenFileGDBGroup>>
699 994 : oMapPathToFeatureDataset;
700 :
701 : // First pass to collect FeatureDatasets
702 994 : std::set<int> oSetBrokenRos;
703 9775 : for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
704 : {
705 9278 : if (!oTable.SelectRow(i, /* bWarnOnlyOnDeletedRows = */ true))
706 : {
707 2137 : oSetBrokenRos.insert(i);
708 2137 : if (oTable.HasGotError())
709 0 : break;
710 2137 : continue;
711 : }
712 :
713 14282 : CPLString osType;
714 7141 : const OGRField *psTypeField = oTable.GetFieldValue(iType);
715 7141 : if (psTypeField != nullptr)
716 : {
717 7141 : const char *pszType = psTypeField->String;
718 7141 : if (pszType)
719 7141 : osType = pszType;
720 : }
721 :
722 14282 : std::string osPath;
723 7141 : const OGRField *psField = oTable.GetFieldValue(iPath);
724 7141 : if (psField != nullptr)
725 : {
726 7141 : const char *pszPath = psField->String;
727 7141 : if (pszPath)
728 7141 : osPath = pszPath;
729 : }
730 :
731 7141 : if (osPath == "\\")
732 : {
733 497 : psField = oTable.GetFieldValue(iUUID);
734 497 : if (psField != nullptr)
735 : {
736 497 : const char *pszUUID = psField->String;
737 497 : if (pszUUID)
738 497 : m_osRootGUID = pszUUID;
739 : }
740 : }
741 :
742 7141 : psField = oTable.GetFieldValue(iDefinition);
743 7141 : if (psField != nullptr &&
744 6644 : strstr(psField->String, "DEFeatureDataset") != nullptr)
745 : {
746 42 : const std::string osDefinition(psField->String);
747 :
748 42 : std::string osName;
749 21 : psField = oTable.GetFieldValue(iName);
750 21 : if (psField != nullptr)
751 : {
752 21 : const char *pszName = psField->String;
753 21 : if (pszName)
754 21 : osName = pszName;
755 : }
756 :
757 21 : if (!osName.empty() && !osPath.empty())
758 : {
759 : auto poSubGroup = std::make_shared<OGROpenFileGDBGroup>(
760 42 : poRootGroup->GetName(), osName.c_str());
761 21 : poSubGroup->SetDefinition(osDefinition);
762 21 : oMapPathToFeatureDataset[osPath] = poSubGroup;
763 21 : poRootGroup->m_apoSubGroups.emplace_back(poSubGroup);
764 21 : }
765 : }
766 13743 : else if (psField != nullptr &&
767 6623 : osType.tolower().compare(pszRelationshipTypeUUID) == 0)
768 : {
769 : // relationship item
770 720 : auto poRelationship = ParseXMLRelationshipDef(psField->String);
771 240 : if (poRelationship)
772 : {
773 240 : const auto &relationshipName = poRelationship->GetName();
774 480 : m_osMapRelationships.insert(std::pair(
775 720 : std::string(relationshipName), std::move(poRelationship)));
776 : }
777 : }
778 : }
779 :
780 : // Now collect layers
781 497 : int nCandidateLayers = 0;
782 497 : int nLayersCDF = 0;
783 497 : bool bRet = true;
784 9775 : for (int i = 0; i < oTable.GetTotalRecordCount(); i++)
785 : {
786 9278 : if (cpl::contains(oSetBrokenRos, i))
787 2137 : continue;
788 :
789 7141 : if (!oTable.SelectRow(i))
790 : {
791 0 : if (oTable.HasGotError())
792 0 : break;
793 0 : continue;
794 : }
795 :
796 7141 : const OGRField *psField = oTable.GetFieldValue(iDefinition);
797 7141 : if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
798 5334 : (strstr(psField->String, "DEFeatureClassInfo") != nullptr ||
799 2464 : strstr(psField->String, "DETableInfo") != nullptr))
800 : {
801 6844 : CPLString osDefinition(psField->String);
802 :
803 3422 : psField = oTable.GetFieldValue(iDocumentation);
804 : CPLString osDocumentation(psField != nullptr ? psField->String
805 6844 : : "");
806 :
807 3422 : std::shared_ptr<OGROpenFileGDBGroup> poParent;
808 3422 : psField = oTable.GetFieldValue(iPath);
809 3422 : if (psField != nullptr)
810 : {
811 3422 : const char *pszPath = psField->String;
812 3422 : if (pszPath)
813 : {
814 6844 : std::string osPath(pszPath);
815 3422 : const auto nPos = osPath.rfind('\\');
816 3422 : if (nPos != 0 && nPos != std::string::npos)
817 : {
818 56 : std::string osPathParent = osPath.substr(0, nPos);
819 : const auto oIter =
820 28 : oMapPathToFeatureDataset.find(osPathParent);
821 28 : if (oIter == oMapPathToFeatureDataset.end())
822 : {
823 0 : CPLError(CE_Warning, CPLE_AppDefined,
824 : "Cannot find feature dataset of "
825 : "path %s referenced by table %s",
826 : osPathParent.c_str(), pszPath);
827 : }
828 : else
829 : {
830 28 : poParent = oIter->second;
831 : }
832 : }
833 : }
834 : }
835 :
836 3422 : psField = oTable.GetFieldValue(iName);
837 3422 : if (psField != nullptr)
838 : {
839 3422 : OGRLayer *poLayer = AddLayer(
840 3422 : psField->String, nInterestTable, nCandidateLayers,
841 : nLayersCDF, osDefinition, osDocumentation, wkbUnknown,
842 6844 : poParent ? poParent->GetDefinition() : std::string());
843 :
844 3422 : if (poLayer)
845 : {
846 3314 : if (poParent)
847 : {
848 : // Add the layer to the group of the corresponding
849 : // feature dataset
850 24 : poParent->m_apoLayers.emplace_back(poLayer);
851 : }
852 : else
853 : {
854 3290 : poRootGroup->m_apoLayers.emplace_back(poLayer);
855 : }
856 : }
857 3422 : }
858 : }
859 3719 : else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_VECTOR) != 0 &&
860 1912 : (strstr(psField->String, "GPCodedValueDomain2") != nullptr ||
861 834 : strstr(psField->String, "GPRangeDomain2") != nullptr))
862 : {
863 3363 : auto poDomain = ParseXMLFieldDomainDef(psField->String);
864 1121 : if (poDomain)
865 : {
866 1121 : const auto &domainName = poDomain->GetName();
867 : m_oMapFieldDomains.insert(
868 1121 : std::pair(std::string(domainName), std::move(poDomain)));
869 1121 : }
870 : }
871 2598 : else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0 &&
872 1440 : strstr(psField->String, "DERasterDataset"))
873 : {
874 2048 : const std::string osDefinition(psField->String);
875 :
876 1024 : psField = oTable.GetFieldValue(iName);
877 1024 : if (psField)
878 : {
879 2048 : std::string osLayerName(psField->String);
880 :
881 1024 : psField = oTable.GetFieldValue(iDocumentation);
882 : const std::string osDocumentation(psField ? psField->String
883 2048 : : "");
884 :
885 1024 : if (!osRasterLayerName.empty())
886 : {
887 930 : if (osRasterLayerName == osLayerName)
888 : {
889 27 : bRet = OpenRaster(poOpenInfo, osLayerName, osDefinition,
890 : osDocumentation);
891 : }
892 : }
893 : else
894 : {
895 94 : const int iSubDSNum = 1 + m_aosSubdatasets.size() / 2;
896 : m_aosSubdatasets.SetNameValue(
897 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
898 : CPLSPrintf("OpenFileGDB:\"%s\":%s",
899 94 : poOpenInfo->pszFilename,
900 94 : osLayerName.c_str()));
901 :
902 94 : std::string osDesc(std::move(osLayerName));
903 94 : if (!osDocumentation.empty())
904 : {
905 : CPLXMLTreeCloser psTree(
906 186 : CPLParseXMLString(osDocumentation.c_str()));
907 93 : if (psTree)
908 : {
909 93 : const auto psRastInfo = CPLGetXMLNode(
910 : psTree.get(), "=metadata.spdoinfo.rastinfo");
911 93 : if (psRastInfo)
912 : {
913 93 : const char *pszRowCount = CPLGetXMLValue(
914 : psRastInfo, "rowcount", nullptr);
915 93 : const char *pszColCount = CPLGetXMLValue(
916 : psRastInfo, "colcount", nullptr);
917 93 : const char *pszRastBand = CPLGetXMLValue(
918 : psRastInfo, "rastband", nullptr);
919 93 : const char *pszRastBPP = CPLGetXMLValue(
920 : psRastInfo, "rastbpp", nullptr);
921 93 : if (pszRowCount && pszColCount && pszRastBand &&
922 : pszRastBPP)
923 : {
924 : osDesc += CPLSPrintf(
925 : " [%sx%sx%s], %s bits", pszColCount,
926 93 : pszRowCount, pszRastBand, pszRastBPP);
927 : }
928 : }
929 : }
930 : }
931 :
932 : m_aosSubdatasets.SetNameValue(
933 : CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
934 94 : ("Raster " + osDesc).c_str());
935 : }
936 1024 : }
937 : }
938 1574 : else if (psField && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) == 0 &&
939 661 : strstr(psField->String, "DERasterDataset"))
940 : {
941 50 : psField = oTable.GetFieldValue(iName);
942 50 : if (psField)
943 : {
944 100 : const std::string osLayerName(psField->String);
945 50 : auto oIter = m_osMapNameToIdx.find(osLayerName);
946 50 : if (oIter != m_osMapNameToIdx.end())
947 : {
948 47 : oSetIgnoredRasterLayerTableNum.insert(oIter->second);
949 :
950 188 : for (const char *pszPrefix :
951 235 : {"fras_ras_", "fras_aux_", "fras_bnd_", "fras_blk_"})
952 : {
953 : oIter = m_osMapNameToIdx.find(
954 188 : std::string(pszPrefix).append(osLayerName).c_str());
955 188 : if (oIter != m_osMapNameToIdx.end())
956 : {
957 : oSetIgnoredRasterLayerTableNum.insert(
958 188 : oIter->second);
959 : }
960 : }
961 : }
962 : }
963 : }
964 : }
965 :
966 : // If there's at least one .cdf layer and the FileGDB driver is present,
967 : // retry with it.
968 497 : if (nLayersCDF > 0 && GDALGetDriverByName("FileGDB") != nullptr)
969 : {
970 2 : bRetryFileGDBOut = true;
971 2 : return false;
972 : }
973 :
974 495 : return bRet;
975 : }
976 :
977 : /************************************************************************/
978 : /* OpenFileGDBv9() */
979 : /************************************************************************/
980 :
981 23 : int OGROpenFileGDBDataSource::OpenFileGDBv9(
982 : int iGDBFeatureClasses, int iGDBObjectClasses, int nInterestTable,
983 : const GDALOpenInfo *poOpenInfo, const std::string &osRasterLayerName,
984 : std::set<int> &oSetIgnoredRasterLayerTableNum)
985 : {
986 46 : auto poTable = std::make_unique<FileGDBTable>();
987 :
988 23 : CPLDebug("OpenFileGDB", "FileGDB v9");
989 :
990 : /* Fetch names of layers */
991 23 : CPLString osFilename(CPLFormFilenameSafe(
992 46 : m_osDirName, CPLSPrintf("a%08x", iGDBObjectClasses + 1), "gdbtable"));
993 23 : if (!poTable->Open(osFilename, false))
994 0 : return FALSE;
995 :
996 23 : int iName = poTable->GetFieldIdx("Name");
997 23 : int iCLSID = poTable->GetFieldIdx("CLSID");
998 23 : if (iName < 0 || poTable->GetField(iName)->GetType() != FGFT_STRING ||
999 46 : iCLSID < 0 || poTable->GetField(iCLSID)->GetType() != FGFT_STRING)
1000 : {
1001 0 : CPLError(CE_Failure, CPLE_AppDefined,
1002 : "Wrong structure for GDB_ObjectClasses table");
1003 0 : return FALSE;
1004 : }
1005 :
1006 46 : std::vector<std::string> aosName;
1007 23 : int nCandidateLayers = 0, nLayersCDF = 0;
1008 433 : for (int i = 0; i < poTable->GetTotalRecordCount(); i++)
1009 : {
1010 410 : if (!poTable->SelectRow(i))
1011 : {
1012 27 : if (poTable->HasGotError())
1013 0 : break;
1014 27 : aosName.push_back("");
1015 27 : continue;
1016 : }
1017 :
1018 383 : const OGRField *psField = poTable->GetFieldValue(iName);
1019 383 : if (psField != nullptr)
1020 : {
1021 766 : std::string osName(psField->String);
1022 383 : psField = poTable->GetFieldValue(iCLSID);
1023 383 : if (psField != nullptr)
1024 : {
1025 : /* Is it a non-spatial table ? */
1026 383 : if (strcmp(psField->String,
1027 : "{7A566981-C114-11D2-8A28-006097AFF44E}") == 0)
1028 : {
1029 51 : aosName.push_back("");
1030 51 : AddLayer(osName, nInterestTable, nCandidateLayers,
1031 102 : nLayersCDF, "", "", wkbNone, std::string());
1032 : }
1033 : else
1034 : {
1035 : /* We should perhaps also check that the CLSID is the one of
1036 : * a spatial table */
1037 332 : aosName.push_back(std::move(osName));
1038 : }
1039 : }
1040 : }
1041 : }
1042 23 : poTable->Close();
1043 :
1044 23 : poTable = std::make_unique<FileGDBTable>();
1045 :
1046 : /* Find tables that are spatial layers */
1047 23 : osFilename = CPLFormFilenameSafe(
1048 23 : m_osDirName, CPLSPrintf("a%08x", iGDBFeatureClasses + 1), "gdbtable");
1049 23 : if (!poTable->Open(osFilename, false))
1050 0 : return FALSE;
1051 :
1052 23 : const int iObjectClassID = poTable->GetFieldIdx("ObjectClassID");
1053 23 : const int iFeatureType = poTable->GetFieldIdx("FeatureType");
1054 23 : const int iGeometryType = poTable->GetFieldIdx("GeometryType");
1055 46 : if (iObjectClassID < 0 || iGeometryType < 0 || iFeatureType < 0 ||
1056 46 : poTable->GetField(iObjectClassID)->GetType() != FGFT_INT32 ||
1057 69 : poTable->GetField(iFeatureType)->GetType() != FGFT_INT32 ||
1058 23 : poTable->GetField(iGeometryType)->GetType() != FGFT_INT32)
1059 : {
1060 0 : CPLError(CE_Failure, CPLE_AppDefined,
1061 : "Wrong structure for GDB_FeatureClasses table");
1062 0 : return FALSE;
1063 : }
1064 :
1065 23 : bool bRet = true;
1066 379 : for (int i = 0; i < poTable->GetTotalRecordCount(); i++)
1067 : {
1068 356 : if (!poTable->SelectRow(i))
1069 : {
1070 24 : if (poTable->HasGotError())
1071 0 : break;
1072 24 : continue;
1073 : }
1074 :
1075 332 : const OGRField *psField = poTable->GetFieldValue(iGeometryType);
1076 332 : if (psField == nullptr)
1077 0 : continue;
1078 332 : const int nGeomType = psField->Integer;
1079 332 : OGRwkbGeometryType eGeomType = wkbUnknown;
1080 332 : switch (nGeomType)
1081 : {
1082 0 : case FGTGT_NONE: /* doesn't make sense ! */
1083 0 : break;
1084 64 : case FGTGT_POINT:
1085 64 : eGeomType = wkbPoint;
1086 64 : break;
1087 52 : case FGTGT_MULTIPOINT:
1088 52 : eGeomType = wkbMultiPoint;
1089 52 : break;
1090 76 : case FGTGT_LINE:
1091 76 : eGeomType = wkbMultiLineString;
1092 76 : break;
1093 124 : case FGTGT_POLYGON:
1094 124 : eGeomType = wkbMultiPolygon;
1095 124 : break;
1096 16 : case FGTGT_MULTIPATCH:
1097 16 : eGeomType = wkbUnknown;
1098 16 : break;
1099 : }
1100 :
1101 332 : psField = poTable->GetFieldValue(iObjectClassID);
1102 332 : if (psField == nullptr)
1103 0 : continue;
1104 :
1105 332 : const int idx = psField->Integer;
1106 664 : if (idx > 0 && idx <= static_cast<int>(aosName.size()) &&
1107 332 : !aosName[idx - 1].empty())
1108 : {
1109 664 : const std::string osName(aosName[idx - 1]);
1110 :
1111 332 : psField = poTable->GetFieldValue(iFeatureType);
1112 332 : const bool bIsRaster = psField && psField->Integer == 14;
1113 :
1114 332 : if (bIsRaster && (poOpenInfo->nOpenFlags & GDAL_OF_RASTER) != 0)
1115 : {
1116 3 : if (!osRasterLayerName.empty())
1117 : {
1118 2 : if (osRasterLayerName == osName)
1119 : {
1120 2 : bRet = OpenRaster(poOpenInfo, osName, "", "");
1121 : }
1122 : }
1123 : else
1124 : {
1125 1 : const int iSubDSNum = 1 + m_aosSubdatasets.size() / 2;
1126 : m_aosSubdatasets.SetNameValue(
1127 : CPLSPrintf("SUBDATASET_%d_NAME", iSubDSNum),
1128 : CPLSPrintf("OpenFileGDB:\"%s\":%s",
1129 1 : poOpenInfo->pszFilename, osName.c_str()));
1130 : m_aosSubdatasets.SetNameValue(
1131 : CPLSPrintf("SUBDATASET_%d_DESC", iSubDSNum),
1132 1 : ("Raster " + osName).c_str());
1133 3 : }
1134 : }
1135 329 : else if (bIsRaster)
1136 : {
1137 1 : auto oIter = m_osMapNameToIdx.find(osName);
1138 1 : if (oIter != m_osMapNameToIdx.end())
1139 : {
1140 1 : oSetIgnoredRasterLayerTableNum.insert(oIter->second);
1141 :
1142 4 : for (const char *pszPrefix :
1143 5 : {"fras_ras_", "fras_aux_", "fras_bnd_", "fras_blk_"})
1144 : {
1145 : oIter = m_osMapNameToIdx.find(
1146 4 : std::string(pszPrefix).append(osName).c_str());
1147 4 : if (oIter != m_osMapNameToIdx.end())
1148 : {
1149 : oSetIgnoredRasterLayerTableNum.insert(
1150 4 : oIter->second);
1151 : }
1152 : }
1153 : }
1154 : }
1155 : else
1156 : {
1157 328 : AddLayer(osName, nInterestTable, nCandidateLayers, nLayersCDF,
1158 656 : "", "", eGeomType, std::string());
1159 : }
1160 : }
1161 : }
1162 :
1163 23 : if (m_apoLayers.empty() && nCandidateLayers > 0 &&
1164 0 : nCandidateLayers == nLayersCDF)
1165 0 : return FALSE;
1166 :
1167 23 : return bRet;
1168 : }
1169 :
1170 : /************************************************************************/
1171 : /* TestCapability() */
1172 : /************************************************************************/
1173 :
1174 727 : int OGROpenFileGDBDataSource::TestCapability(const char *pszCap) const
1175 : {
1176 727 : if (EQUAL(pszCap, ODsCCreateLayer) || EQUAL(pszCap, ODsCDeleteLayer) ||
1177 633 : EQUAL(pszCap, ODsCAddFieldDomain) ||
1178 629 : EQUAL(pszCap, ODsCDeleteFieldDomain) ||
1179 628 : EQUAL(pszCap, ODsCUpdateFieldDomain) ||
1180 627 : EQUAL(pszCap, GDsCAddRelationship) ||
1181 627 : EQUAL(pszCap, GDsCDeleteRelationship) ||
1182 627 : EQUAL(pszCap, GDsCUpdateRelationship) ||
1183 627 : EQUAL(pszCap, ODsCEmulatedTransactions))
1184 : {
1185 103 : return eAccess == GA_Update;
1186 : }
1187 :
1188 624 : else if (EQUAL(pszCap, ODsCMeasuredGeometries))
1189 183 : return TRUE;
1190 441 : else if (EQUAL(pszCap, ODsCZGeometries))
1191 183 : return TRUE;
1192 258 : else if (EQUAL(pszCap, ODsCCurveGeometries))
1193 200 : return TRUE;
1194 :
1195 58 : return FALSE;
1196 : }
1197 :
1198 : /************************************************************************/
1199 : /* GetLayer() */
1200 : /************************************************************************/
1201 :
1202 1195470 : const OGRLayer *OGROpenFileGDBDataSource::GetLayer(int iIndex) const
1203 : {
1204 1195470 : if (iIndex < 0 || iIndex >= static_cast<int>(m_apoLayers.size()))
1205 18 : return nullptr;
1206 1195450 : return m_apoLayers[iIndex].get();
1207 : }
1208 :
1209 : /************************************************************************/
1210 : /* BuildLayerFromName() */
1211 : /************************************************************************/
1212 :
1213 : std::unique_ptr<OGROpenFileGDBLayer>
1214 903 : OGROpenFileGDBDataSource::BuildLayerFromName(const char *pszName)
1215 : {
1216 :
1217 903 : const auto oIter = m_osMapNameToIdx.find(pszName);
1218 903 : if (oIter != m_osMapNameToIdx.end())
1219 : {
1220 165 : const int idx = oIter->second;
1221 165 : const CPLString osFilename(CPLFormFilenameSafe(
1222 165 : m_osDirName, CPLSPrintf("a%08x", idx), "gdbtable"));
1223 165 : if (FileExists(osFilename))
1224 : {
1225 : return std::make_unique<OGROpenFileGDBLayer>(
1226 164 : this, osFilename, pszName, "", "", eAccess == GA_Update);
1227 : }
1228 : }
1229 739 : return nullptr;
1230 : }
1231 :
1232 : /************************************************************************/
1233 : /* GetLayerByName() */
1234 : /************************************************************************/
1235 :
1236 : OGROpenFileGDBLayer *
1237 3435 : OGROpenFileGDBDataSource::GetLayerByName(const char *pszName)
1238 : {
1239 35050 : for (auto &poLayer : m_apoLayers)
1240 : {
1241 34207 : if (EQUAL(poLayer->GetName(), pszName))
1242 2592 : return poLayer.get();
1243 : }
1244 :
1245 5583 : for (auto &poLayer : m_apoHiddenLayers)
1246 : {
1247 4741 : if (EQUAL(poLayer->GetName(), pszName))
1248 1 : return poLayer.get();
1249 : }
1250 :
1251 1684 : auto poLayer = BuildLayerFromName(pszName);
1252 842 : if (poLayer)
1253 : {
1254 104 : m_apoHiddenLayers.emplace_back(std::move(poLayer));
1255 104 : return m_apoHiddenLayers.back().get();
1256 : }
1257 738 : return nullptr;
1258 : }
1259 :
1260 : /************************************************************************/
1261 : /* IsPrivateLayerName() */
1262 : /************************************************************************/
1263 :
1264 10550 : bool OGROpenFileGDBDataSource::IsPrivateLayerName(const CPLString &osName)
1265 : {
1266 10550 : const CPLString osLCTableName(CPLString(osName).tolower());
1267 :
1268 : // tables beginning with "GDB_" are private tables
1269 : // Also consider "VAT_" tables as private ones.
1270 27034 : return osLCTableName.size() >= 4 && (osLCTableName.substr(0, 4) == "gdb_" ||
1271 27034 : osLCTableName.substr(0, 4) == "vat_");
1272 : }
1273 :
1274 : /************************************************************************/
1275 : /* IsLayerPrivate() */
1276 : /************************************************************************/
1277 :
1278 103 : bool OGROpenFileGDBDataSource::IsLayerPrivate(int iLayer) const
1279 : {
1280 103 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1281 0 : return false;
1282 :
1283 103 : const std::string osName(m_apoLayers[iLayer]->GetName());
1284 103 : return IsPrivateLayerName(osName);
1285 : }
1286 :
1287 : /************************************************************************/
1288 : /* GetMetadata() */
1289 : /************************************************************************/
1290 :
1291 26 : CSLConstList OGROpenFileGDBDataSource::GetMetadata(const char *pszDomain)
1292 : {
1293 26 : if (pszDomain && EQUAL(pszDomain, "SUBDATASETS"))
1294 6 : return m_aosSubdatasets.List();
1295 20 : return GDALDataset::GetMetadata(pszDomain);
1296 : }
1297 :
1298 : /************************************************************************/
1299 : /* OGROpenFileGDBSingleFeatureLayer() */
1300 : /************************************************************************/
1301 :
1302 239 : OGROpenFileGDBSingleFeatureLayer::OGROpenFileGDBSingleFeatureLayer(
1303 239 : const char *pszLayerName, const char *pszValIn)
1304 239 : : pszVal(pszValIn ? CPLStrdup(pszValIn) : nullptr),
1305 478 : poFeatureDefn(new OGRFeatureDefn(pszLayerName)), iNextShapeId(0)
1306 : {
1307 239 : SetDescription(poFeatureDefn->GetName());
1308 239 : poFeatureDefn->Reference();
1309 478 : OGRFieldDefn oField("FIELD_1", OFTString);
1310 239 : poFeatureDefn->AddFieldDefn(&oField);
1311 239 : }
1312 :
1313 : /************************************************************************/
1314 : /* ~OGROpenFileGDBSingleFeatureLayer() */
1315 : /************************************************************************/
1316 :
1317 478 : OGROpenFileGDBSingleFeatureLayer::~OGROpenFileGDBSingleFeatureLayer()
1318 : {
1319 239 : if (poFeatureDefn != nullptr)
1320 239 : poFeatureDefn->Release();
1321 239 : CPLFree(pszVal);
1322 478 : }
1323 :
1324 : /************************************************************************/
1325 : /* GetNextFeature() */
1326 : /************************************************************************/
1327 :
1328 258 : OGRFeature *OGROpenFileGDBSingleFeatureLayer::GetNextFeature()
1329 : {
1330 258 : if (iNextShapeId != 0)
1331 19 : return nullptr;
1332 :
1333 239 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
1334 239 : if (pszVal)
1335 239 : poFeature->SetField(0, pszVal);
1336 239 : poFeature->SetFID(iNextShapeId++);
1337 239 : return poFeature;
1338 : }
1339 :
1340 : /************************************************************************/
1341 : /* OGROpenFileGDBSimpleSQLLayer */
1342 : /************************************************************************/
1343 :
1344 : class OGROpenFileGDBSimpleSQLLayer final : public OGRLayer
1345 : {
1346 : OGRLayer *poBaseLayer;
1347 : FileGDBIterator *poIter;
1348 : OGRFeatureDefn *poFeatureDefn;
1349 : GIntBig m_nOffset;
1350 : GIntBig m_nLimit;
1351 : GIntBig m_nSkipped = 0;
1352 : GIntBig m_nIterated = 0;
1353 :
1354 : CPL_DISALLOW_COPY_ASSIGN(OGROpenFileGDBSimpleSQLLayer)
1355 :
1356 : public:
1357 : OGROpenFileGDBSimpleSQLLayer(OGRLayer *poBaseLayer, FileGDBIterator *poIter,
1358 : int nColumns, const swq_col_def *pasColDefs,
1359 : GIntBig nOffset, GIntBig nLimit);
1360 : ~OGROpenFileGDBSimpleSQLLayer() override;
1361 :
1362 : void ResetReading() override;
1363 : OGRFeature *GetNextFeature() override;
1364 : OGRFeature *GetFeature(GIntBig nFeatureId) override;
1365 :
1366 161 : const OGRFeatureDefn *GetLayerDefn() const override
1367 : {
1368 161 : return poFeatureDefn;
1369 : }
1370 :
1371 : int TestCapability(const char *) const override;
1372 :
1373 60 : const char *GetFIDColumn() const override
1374 : {
1375 60 : return poBaseLayer->GetFIDColumn();
1376 : }
1377 :
1378 12 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
1379 : bool bForce) override
1380 : {
1381 12 : return poBaseLayer->GetExtent(iGeomField, psExtent, bForce);
1382 : }
1383 :
1384 0 : OGRErr IGetExtent3D(int iGeomField, OGREnvelope3D *psExtent,
1385 : bool bForce) override
1386 : {
1387 0 : return poBaseLayer->GetExtent3D(iGeomField, psExtent, bForce);
1388 : }
1389 :
1390 : GIntBig GetFeatureCount(int bForce) override;
1391 : };
1392 :
1393 : /************************************************************************/
1394 : /* OGROpenFileGDBSimpleSQLLayer() */
1395 : /************************************************************************/
1396 :
1397 16 : OGROpenFileGDBSimpleSQLLayer::OGROpenFileGDBSimpleSQLLayer(
1398 : OGRLayer *poBaseLayerIn, FileGDBIterator *poIterIn, int nColumns,
1399 16 : const swq_col_def *pasColDefs, GIntBig nOffset, GIntBig nLimit)
1400 : : poBaseLayer(poBaseLayerIn), poIter(poIterIn), poFeatureDefn(nullptr),
1401 16 : m_nOffset(nOffset), m_nLimit(nLimit)
1402 : {
1403 16 : if (nColumns == 1 && strcmp(pasColDefs[0].field_name, "*") == 0)
1404 : {
1405 14 : poFeatureDefn = poBaseLayer->GetLayerDefn();
1406 14 : poFeatureDefn->Reference();
1407 : }
1408 : else
1409 : {
1410 2 : poFeatureDefn = new OGRFeatureDefn(poBaseLayer->GetName());
1411 2 : poFeatureDefn->SetGeomType(poBaseLayer->GetGeomType());
1412 2 : poFeatureDefn->Reference();
1413 2 : if (poBaseLayer->GetGeomType() != wkbNone)
1414 : {
1415 4 : poFeatureDefn->GetGeomFieldDefn(0)->SetName(
1416 2 : poBaseLayer->GetGeometryColumn());
1417 4 : poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(
1418 2 : poBaseLayer->GetSpatialRef());
1419 : }
1420 6 : for (int i = 0; i < nColumns; i++)
1421 : {
1422 4 : if (strcmp(pasColDefs[i].field_name, "*") == 0)
1423 : {
1424 0 : for (int j = 0;
1425 0 : j < poBaseLayer->GetLayerDefn()->GetFieldCount(); j++)
1426 0 : poFeatureDefn->AddFieldDefn(
1427 0 : poBaseLayer->GetLayerDefn()->GetFieldDefn(j));
1428 : }
1429 : else
1430 : {
1431 : OGRFieldDefn *poFieldDefn =
1432 8 : poBaseLayer->GetLayerDefn()->GetFieldDefn(
1433 8 : poBaseLayer->GetLayerDefn()->GetFieldIndex(
1434 4 : pasColDefs[i].field_name));
1435 4 : CPLAssert(poFieldDefn != nullptr); /* already checked before */
1436 4 : poFeatureDefn->AddFieldDefn(poFieldDefn);
1437 : }
1438 : }
1439 : }
1440 16 : SetDescription(poFeatureDefn->GetName());
1441 16 : OGROpenFileGDBSimpleSQLLayer::ResetReading();
1442 16 : }
1443 :
1444 : /************************************************************************/
1445 : /* ~OGROpenFileGDBSimpleSQLLayer() */
1446 : /************************************************************************/
1447 :
1448 32 : OGROpenFileGDBSimpleSQLLayer::~OGROpenFileGDBSimpleSQLLayer()
1449 : {
1450 16 : if (poFeatureDefn)
1451 : {
1452 16 : poFeatureDefn->Release();
1453 : }
1454 16 : delete poIter;
1455 32 : }
1456 :
1457 : /************************************************************************/
1458 : /* ResetReading() */
1459 : /************************************************************************/
1460 :
1461 210 : void OGROpenFileGDBSimpleSQLLayer::ResetReading()
1462 : {
1463 210 : poIter->Reset();
1464 210 : m_nSkipped = 0;
1465 210 : m_nIterated = 0;
1466 210 : }
1467 :
1468 : /************************************************************************/
1469 : /* GetFeature() */
1470 : /************************************************************************/
1471 :
1472 696 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetFeature(GIntBig nFeatureId)
1473 : {
1474 696 : OGRFeature *poSrcFeature = poBaseLayer->GetFeature(nFeatureId);
1475 696 : if (poSrcFeature == nullptr)
1476 9 : return nullptr;
1477 :
1478 687 : if (poFeatureDefn == poBaseLayer->GetLayerDefn())
1479 536 : return poSrcFeature;
1480 : else
1481 : {
1482 151 : OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
1483 151 : poFeature->SetFrom(poSrcFeature);
1484 151 : poFeature->SetFID(poSrcFeature->GetFID());
1485 151 : delete poSrcFeature;
1486 151 : return poFeature;
1487 : }
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* GetNextFeature() */
1492 : /************************************************************************/
1493 :
1494 758 : OGRFeature *OGROpenFileGDBSimpleSQLLayer::GetNextFeature()
1495 : {
1496 : while (true)
1497 : {
1498 758 : if (m_nLimit >= 0 && m_nIterated == m_nLimit)
1499 1 : return nullptr;
1500 :
1501 757 : const int64_t nRow = poIter->GetNextRowSortedByValue();
1502 757 : if (nRow < 0)
1503 82 : return nullptr;
1504 675 : OGRFeature *poFeature = GetFeature(nRow + 1);
1505 675 : if (poFeature == nullptr)
1506 0 : return nullptr;
1507 675 : if (m_nOffset >= 0 && m_nSkipped < m_nOffset)
1508 : {
1509 343 : delete poFeature;
1510 343 : m_nSkipped++;
1511 343 : continue;
1512 : }
1513 332 : m_nIterated++;
1514 :
1515 774 : if ((m_poFilterGeom == nullptr ||
1516 631 : FilterGeometry(poFeature->GetGeometryRef())) &&
1517 299 : (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
1518 : {
1519 249 : return poFeature;
1520 : }
1521 :
1522 83 : delete poFeature;
1523 426 : }
1524 : }
1525 :
1526 : /************************************************************************/
1527 : /* GetFeatureCount() */
1528 : /************************************************************************/
1529 :
1530 51 : GIntBig OGROpenFileGDBSimpleSQLLayer::GetFeatureCount(int bForce)
1531 : {
1532 :
1533 : /* No filter */
1534 51 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1535 : {
1536 33 : GIntBig nRowCount = poIter->GetRowCount();
1537 33 : if (m_nOffset > 0)
1538 : {
1539 3 : if (m_nOffset <= nRowCount)
1540 2 : nRowCount -= m_nOffset;
1541 : else
1542 1 : nRowCount = 0;
1543 : }
1544 33 : if (m_nLimit >= 0 && nRowCount > m_nLimit)
1545 5 : nRowCount = m_nLimit;
1546 33 : return nRowCount;
1547 : }
1548 :
1549 18 : return OGRLayer::GetFeatureCount(bForce);
1550 : }
1551 :
1552 : /************************************************************************/
1553 : /* TestCapability() */
1554 : /************************************************************************/
1555 :
1556 99 : int OGROpenFileGDBSimpleSQLLayer::TestCapability(const char *pszCap) const
1557 : {
1558 :
1559 99 : if (EQUAL(pszCap, OLCFastFeatureCount))
1560 : {
1561 0 : return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
1562 : }
1563 99 : else if (EQUAL(pszCap, OLCFastGetExtent))
1564 : {
1565 6 : return TRUE;
1566 : }
1567 93 : else if (EQUAL(pszCap, OLCRandomRead))
1568 : {
1569 0 : return TRUE;
1570 : }
1571 93 : else if (EQUAL(pszCap, OLCStringsAsUTF8))
1572 : {
1573 27 : return TRUE; /* ? */
1574 : }
1575 :
1576 66 : return FALSE;
1577 : }
1578 :
1579 : /************************************************************************/
1580 : /* ExecuteSQL() */
1581 : /************************************************************************/
1582 :
1583 799 : OGRLayer *OGROpenFileGDBDataSource::ExecuteSQL(const char *pszSQLCommand,
1584 : OGRGeometry *poSpatialFilter,
1585 : const char *pszDialect)
1586 : {
1587 :
1588 : /* -------------------------------------------------------------------- */
1589 : /* Special case GetLayerDefinition */
1590 : /* -------------------------------------------------------------------- */
1591 799 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerDefinition "))
1592 : {
1593 32 : const char *pszLayerName =
1594 : pszSQLCommand + strlen("GetLayerDefinition ");
1595 32 : auto poLayer = GetLayerByName(pszLayerName);
1596 32 : if (poLayer)
1597 : {
1598 : OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
1599 31 : "LayerDefinition", poLayer->GetXMLDefinition().c_str());
1600 31 : return poRet;
1601 : }
1602 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1603 : pszLayerName);
1604 1 : return nullptr;
1605 : }
1606 :
1607 : /* -------------------------------------------------------------------- */
1608 : /* Special case GetLayerMetadata */
1609 : /* -------------------------------------------------------------------- */
1610 767 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerMetadata "))
1611 : {
1612 21 : const char *pszLayerName = pszSQLCommand + strlen("GetLayerMetadata ");
1613 21 : auto poLayer = GetLayerByName(pszLayerName);
1614 21 : if (poLayer)
1615 : {
1616 : OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
1617 20 : "LayerMetadata", poLayer->GetXMLDocumentation().c_str());
1618 20 : return poRet;
1619 : }
1620 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1621 : pszLayerName);
1622 1 : return nullptr;
1623 : }
1624 :
1625 : /* -------------------------------------------------------------------- */
1626 : /* Special case GetLayerAttrIndexUse (only for debugging purposes) */
1627 : /* -------------------------------------------------------------------- */
1628 746 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerAttrIndexUse "))
1629 : {
1630 : auto poLayer =
1631 141 : GetLayerByName(pszSQLCommand + strlen("GetLayerAttrIndexUse "));
1632 141 : if (poLayer)
1633 : {
1634 : OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
1635 : "LayerAttrIndexUse",
1636 141 : CPLSPrintf("%d", poLayer->GetAttrIndexUse()));
1637 141 : return poRet;
1638 : }
1639 :
1640 0 : return nullptr;
1641 : }
1642 :
1643 : /* -------------------------------------------------------------------- */
1644 : /* Special case GetLayerSpatialIndexState (only for debugging purposes)
1645 : */
1646 : /* -------------------------------------------------------------------- */
1647 605 : if (STARTS_WITH_CI(pszSQLCommand, "GetLayerSpatialIndexState "))
1648 : {
1649 12 : auto poLayer = GetLayerByName(pszSQLCommand +
1650 : strlen("GetLayerSpatialIndexState "));
1651 12 : if (poLayer)
1652 : {
1653 : OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
1654 : "LayerSpatialIndexState",
1655 12 : CPLSPrintf("%d", poLayer->GetSpatialIndexState()));
1656 12 : return poRet;
1657 : }
1658 :
1659 0 : return nullptr;
1660 : }
1661 :
1662 : /* -------------------------------------------------------------------- */
1663 : /* Special case GetLastSQLUsedOptimizedImplementation (only for
1664 : * debugging purposes) */
1665 : /* -------------------------------------------------------------------- */
1666 593 : if (EQUAL(pszSQLCommand, "GetLastSQLUsedOptimizedImplementation"))
1667 : {
1668 : OGRLayer *poRet = new OGROpenFileGDBSingleFeatureLayer(
1669 : "GetLastSQLUsedOptimizedImplementation",
1670 46 : CPLSPrintf("%d",
1671 23 : static_cast<int>(bLastSQLUsedOptimizedImplementation)));
1672 23 : return poRet;
1673 : }
1674 :
1675 : /* -------------------------------------------------------------------- */
1676 : /* Special case for "CREATE SPATIAL INDEX ON " */
1677 : /* -------------------------------------------------------------------- */
1678 570 : if (STARTS_WITH_CI(pszSQLCommand, "CREATE SPATIAL INDEX ON "))
1679 : {
1680 0 : const char *pszLayerName =
1681 : pszSQLCommand + strlen("CREATE SPATIAL INDEX ON ");
1682 0 : auto poLayer = GetLayerByName(pszLayerName);
1683 0 : if (poLayer)
1684 : {
1685 0 : poLayer->CreateSpatialIndex();
1686 : }
1687 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1688 : pszLayerName);
1689 0 : return nullptr;
1690 : }
1691 :
1692 : /* -------------------------------------------------------------------- */
1693 : /* Special case for "CREATE INDEX idx_name ON table_name(col_name) */
1694 : /* -------------------------------------------------------------------- */
1695 570 : if (STARTS_WITH_CI(pszSQLCommand, "CREATE INDEX "))
1696 : {
1697 42 : CPLString osSQL(pszSQLCommand);
1698 21 : const auto nPosON = osSQL.ifind(" ON ");
1699 21 : if (nPosON != std::string::npos)
1700 : {
1701 : const std::string osIdxName = osSQL.substr(
1702 21 : strlen("CREATE INDEX "), nPosON - strlen("CREATE INDEX "));
1703 21 : const std::string afterOn = osSQL.substr(nPosON + strlen(" ON "));
1704 21 : const auto nOpenParPos = afterOn.find('(');
1705 21 : if (nOpenParPos != std::string::npos && afterOn.back() == ')')
1706 : {
1707 42 : const std::string osLayerName = afterOn.substr(0, nOpenParPos);
1708 : const std::string osExpression = afterOn.substr(
1709 42 : nOpenParPos + 1, afterOn.size() - nOpenParPos - 2);
1710 21 : if (osExpression.find(',') != std::string::npos)
1711 : {
1712 1 : CPLError(
1713 : CE_Failure, CPLE_NotSupported,
1714 : "Creation of multiple-column indices is not supported");
1715 1 : return nullptr;
1716 : }
1717 20 : auto poLayer = GetLayerByName(osLayerName.c_str());
1718 20 : if (poLayer)
1719 : {
1720 19 : poLayer->CreateIndex(osIdxName, osExpression);
1721 19 : return nullptr;
1722 : }
1723 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1724 : osLayerName.c_str());
1725 1 : return nullptr;
1726 : }
1727 : }
1728 0 : CPLError(CE_Failure, CPLE_AppDefined,
1729 : "Bad syntax. Expected CREATE INDEX idx_name ON "
1730 : "table_name(col_name)");
1731 0 : return nullptr;
1732 : }
1733 :
1734 : /* -------------------------------------------------------------------- */
1735 : /* Special case for "RECOMPUTE EXTENT ON " */
1736 : /* -------------------------------------------------------------------- */
1737 549 : if (STARTS_WITH_CI(pszSQLCommand, "RECOMPUTE EXTENT ON "))
1738 : {
1739 3 : const char *pszLayerName =
1740 : pszSQLCommand + strlen("RECOMPUTE EXTENT ON ");
1741 3 : auto poLayer = GetLayerByName(pszLayerName);
1742 3 : if (poLayer)
1743 : {
1744 2 : poLayer->RecomputeExtent();
1745 : }
1746 : else
1747 : {
1748 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1749 : pszLayerName);
1750 : }
1751 3 : return nullptr;
1752 : }
1753 :
1754 : /* -------------------------------------------------------------------- */
1755 : /* Special case for "DELLAYER:" */
1756 : /* -------------------------------------------------------------------- */
1757 546 : if (STARTS_WITH_CI(pszSQLCommand, "DELLAYER:"))
1758 : {
1759 3 : const char *pszLayerName = pszSQLCommand + strlen("DELLAYER:");
1760 5 : for (int i = 0; i < static_cast<int>(m_apoLayers.size()); ++i)
1761 : {
1762 3 : if (strcmp(pszLayerName, m_apoLayers[i]->GetName()) == 0)
1763 : {
1764 1 : DeleteLayer(i);
1765 1 : return nullptr;
1766 : }
1767 : }
1768 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1769 : pszLayerName);
1770 2 : return nullptr;
1771 : }
1772 :
1773 : /* -------------------------------------------------------------------- */
1774 : /* Special case for "CHECK_FREELIST_CONSISTENCY:" */
1775 : /* -------------------------------------------------------------------- */
1776 543 : if (STARTS_WITH_CI(pszSQLCommand, "CHECK_FREELIST_CONSISTENCY:"))
1777 : {
1778 8 : const char *pszLayerName =
1779 : pszSQLCommand + strlen("CHECK_FREELIST_CONSISTENCY:");
1780 8 : auto poLayer = GetLayerByName(pszLayerName);
1781 8 : if (poLayer)
1782 : {
1783 : return new OGROpenFileGDBSingleFeatureLayer(
1784 : "result",
1785 16 : CPLSPrintf("%d", static_cast<int>(
1786 8 : poLayer->CheckFreeListConsistency())));
1787 : }
1788 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1789 : pszLayerName);
1790 0 : return nullptr;
1791 : }
1792 :
1793 : /* -------------------------------------------------------------------- */
1794 : /* Special case for "REPACK" */
1795 : /* -------------------------------------------------------------------- */
1796 535 : if (EQUAL(pszSQLCommand, "REPACK"))
1797 : {
1798 3 : bool bSuccess = true;
1799 6 : for (auto &poLayer : m_apoLayers)
1800 : {
1801 3 : if (!poLayer->Repack(nullptr, nullptr))
1802 0 : bSuccess = false;
1803 : }
1804 : return new OGROpenFileGDBSingleFeatureLayer(
1805 3 : "result", bSuccess ? "true" : "false");
1806 : }
1807 532 : else if (STARTS_WITH(pszSQLCommand, "REPACK "))
1808 : {
1809 2 : const char *pszLayerName = pszSQLCommand + strlen("REPACK ");
1810 2 : auto poLayer = GetLayerByName(pszLayerName);
1811 2 : if (poLayer)
1812 : {
1813 1 : const bool bSuccess = poLayer->Repack(nullptr, nullptr);
1814 : return new OGROpenFileGDBSingleFeatureLayer(
1815 1 : "result", bSuccess ? "true" : "false");
1816 : }
1817 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid layer name: %s",
1818 : pszLayerName);
1819 1 : return nullptr;
1820 : }
1821 :
1822 530 : bLastSQLUsedOptimizedImplementation = false;
1823 :
1824 : /* -------------------------------------------------------------------- */
1825 : /* Special cases for SQL optimizations */
1826 : /* -------------------------------------------------------------------- */
1827 521 : if (STARTS_WITH_CI(pszSQLCommand, "SELECT ") &&
1828 39 : (pszDialect == nullptr || EQUAL(pszDialect, "") ||
1829 1051 : EQUAL(pszDialect, "OGRSQL")) &&
1830 521 : CPLTestBool(CPLGetConfigOption("OPENFILEGDB_USE_INDEX", "YES")))
1831 : {
1832 521 : swq_select oSelect;
1833 521 : if (oSelect.preparse(pszSQLCommand) != CE_None)
1834 1 : return nullptr;
1835 :
1836 : /* --------------------------------------------------------------------
1837 : */
1838 : /* MIN/MAX/SUM/AVG/COUNT optimization */
1839 : /* --------------------------------------------------------------------
1840 : */
1841 520 : if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
1842 520 : oSelect.table_count == 1 && oSelect.order_specs == 0 &&
1843 493 : oSelect.query_mode != SWQM_DISTINCT_LIST &&
1844 493 : oSelect.where_expr == nullptr)
1845 : {
1846 : OGROpenFileGDBLayer *poLayer =
1847 624 : reinterpret_cast<OGROpenFileGDBLayer *>(
1848 312 : GetLayerByName(oSelect.table_defs[0].table_name));
1849 312 : if (poLayer)
1850 : {
1851 311 : OGRMemLayer *poMemLayer = nullptr;
1852 :
1853 311 : int i = 0; // Used after for.
1854 369 : for (; i < oSelect.result_columns(); i++)
1855 : {
1856 333 : swq_col_func col_func = oSelect.column_defs[i].col_func;
1857 514 : if (!(col_func == SWQCF_MIN || col_func == SWQCF_MAX ||
1858 187 : col_func == SWQCF_COUNT || col_func == SWQCF_AVG ||
1859 : col_func == SWQCF_SUM))
1860 275 : break;
1861 :
1862 153 : if (oSelect.column_defs[i].field_name == nullptr)
1863 0 : break;
1864 153 : if (oSelect.column_defs[i].distinct_flag)
1865 0 : break;
1866 153 : if (oSelect.column_defs[i].target_type != SWQ_OTHER)
1867 0 : break;
1868 :
1869 153 : int idx = poLayer->GetLayerDefn()->GetFieldIndex(
1870 153 : oSelect.column_defs[i].field_name);
1871 153 : if (idx < 0)
1872 90 : break;
1873 :
1874 : const OGRFieldDefn *poFieldDefn =
1875 63 : poLayer->GetLayerDefn()->GetFieldDefn(idx);
1876 :
1877 64 : if (col_func == SWQCF_SUM &&
1878 1 : poFieldDefn->GetType() == OFTDateTime)
1879 0 : break;
1880 :
1881 63 : int eOutOGRType = -1;
1882 63 : const OGRField *psField = nullptr;
1883 : OGRField sField;
1884 63 : if (col_func == SWQCF_MIN || col_func == SWQCF_MAX)
1885 : {
1886 55 : psField = poLayer->GetMinMaxValue(
1887 : poFieldDefn, col_func == SWQCF_MIN, eOutOGRType);
1888 55 : if (eOutOGRType < 0)
1889 5 : break;
1890 : }
1891 : else
1892 : {
1893 8 : double dfMin = 0.0;
1894 8 : double dfMax = 0.0;
1895 8 : int nCount = 0;
1896 8 : double dfSum = 0.0;
1897 :
1898 8 : if (!poLayer->GetMinMaxSumCount(poFieldDefn, dfMin,
1899 : dfMax, dfSum, nCount))
1900 0 : break;
1901 8 : psField = &sField;
1902 8 : if (col_func == SWQCF_AVG)
1903 : {
1904 6 : if (nCount == 0)
1905 : {
1906 1 : eOutOGRType = OFTReal;
1907 1 : psField = nullptr;
1908 : }
1909 : else
1910 : {
1911 5 : if (poFieldDefn->GetType() == OFTDateTime)
1912 : {
1913 1 : eOutOGRType = OFTDateTime;
1914 1 : FileGDBDoubleDateToOGRDate(dfSum / nCount,
1915 : false, &sField);
1916 : }
1917 : else
1918 : {
1919 4 : eOutOGRType = OFTReal;
1920 4 : sField.Real = dfSum / nCount;
1921 : }
1922 : }
1923 : }
1924 2 : else if (col_func == SWQCF_COUNT)
1925 : {
1926 1 : sField.Integer = nCount;
1927 1 : eOutOGRType = OFTInteger;
1928 : }
1929 : else
1930 : {
1931 1 : sField.Real = dfSum;
1932 1 : eOutOGRType = OFTReal;
1933 : }
1934 : }
1935 :
1936 58 : if (poMemLayer == nullptr)
1937 : {
1938 36 : poMemLayer =
1939 36 : new OGRMemLayer("SELECT", nullptr, wkbNone);
1940 36 : CPL_IGNORE_RET_VAL(poMemLayer->CreateFeature(
1941 36 : std::make_unique<OGRFeature>(
1942 72 : poMemLayer->GetLayerDefn())));
1943 : }
1944 :
1945 : const char *pszMinMaxFieldName =
1946 102 : CPLSPrintf("%s_%s",
1947 : (col_func == SWQCF_MIN) ? "MIN"
1948 52 : : (col_func == SWQCF_MAX) ? "MAX"
1949 10 : : (col_func == SWQCF_AVG) ? "AVG"
1950 2 : : (col_func == SWQCF_SUM) ? "SUM"
1951 : : "COUNT",
1952 58 : oSelect.column_defs[i].field_name);
1953 : OGRFieldDefn oFieldDefn(
1954 : pszMinMaxFieldName,
1955 116 : static_cast<OGRFieldType>(eOutOGRType));
1956 58 : poMemLayer->CreateField(&oFieldDefn);
1957 58 : if (psField != nullptr)
1958 : {
1959 : auto poFeature = std::unique_ptr<OGRFeature>(
1960 55 : poMemLayer->GetFeature(0));
1961 55 : poFeature->SetField(oFieldDefn.GetNameRef(), psField);
1962 55 : CPL_IGNORE_RET_VAL(
1963 55 : poMemLayer->SetFeature(std::move(poFeature)));
1964 : }
1965 : }
1966 311 : if (i != oSelect.result_columns())
1967 : {
1968 275 : delete poMemLayer;
1969 : }
1970 : else
1971 : {
1972 36 : CPLDebug(
1973 : "OpenFileGDB",
1974 : "Using optimized MIN/MAX/SUM/AVG/COUNT implementation");
1975 36 : bLastSQLUsedOptimizedImplementation = true;
1976 36 : return poMemLayer;
1977 : }
1978 : }
1979 : }
1980 :
1981 : /* --------------------------------------------------------------------
1982 : */
1983 : /* ORDER BY optimization */
1984 : /* --------------------------------------------------------------------
1985 : */
1986 484 : if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
1987 484 : oSelect.table_count == 1 && oSelect.order_specs == 1 &&
1988 26 : oSelect.query_mode != SWQM_DISTINCT_LIST)
1989 : {
1990 : OGROpenFileGDBLayer *poLayer =
1991 50 : reinterpret_cast<OGROpenFileGDBLayer *>(
1992 25 : GetLayerByName(oSelect.table_defs[0].table_name));
1993 50 : if (poLayer != nullptr &&
1994 25 : poLayer->HasIndexForField(oSelect.order_defs[0].field_name))
1995 : {
1996 23 : OGRErr eErr = OGRERR_NONE;
1997 23 : if (oSelect.where_expr != nullptr)
1998 : {
1999 : /* The where must be a simple comparison on the column */
2000 : /* that is used for ordering */
2001 12 : if (oSelect.where_expr->eNodeType == SNT_OPERATION &&
2002 4 : OGROpenFileGDBIsComparisonOp(
2003 4 : oSelect.where_expr->nOperation) &&
2004 3 : oSelect.where_expr->nOperation != SWQ_NE &&
2005 3 : oSelect.where_expr->nSubExprCount == 2 &&
2006 3 : (oSelect.where_expr->papoSubExpr[0]->eNodeType ==
2007 0 : SNT_COLUMN ||
2008 0 : oSelect.where_expr->papoSubExpr[0]->eNodeType ==
2009 3 : SNT_CONSTANT) &&
2010 3 : oSelect.where_expr->papoSubExpr[0]->field_type ==
2011 3 : SWQ_STRING &&
2012 3 : EQUAL(oSelect.where_expr->papoSubExpr[0]->string_value,
2013 8 : oSelect.order_defs[0].field_name) &&
2014 2 : oSelect.where_expr->papoSubExpr[1]->eNodeType ==
2015 : SNT_CONSTANT)
2016 : {
2017 : /* ok */
2018 : }
2019 : else
2020 2 : eErr = OGRERR_FAILURE;
2021 : }
2022 23 : if (eErr == OGRERR_NONE)
2023 : {
2024 21 : int i = 0; // Used after for.
2025 40 : for (; i < oSelect.result_columns(); i++)
2026 : {
2027 23 : if (oSelect.column_defs[i].col_func != SWQCF_NONE)
2028 1 : break;
2029 22 : if (oSelect.column_defs[i].field_name == nullptr)
2030 0 : break;
2031 22 : if (oSelect.column_defs[i].distinct_flag)
2032 0 : break;
2033 22 : if (oSelect.column_defs[i].target_type != SWQ_OTHER)
2034 1 : break;
2035 21 : if (strcmp(oSelect.column_defs[i].field_name, "*") !=
2036 27 : 0 &&
2037 12 : poLayer->GetLayerDefn()->GetFieldIndex(
2038 6 : oSelect.column_defs[i].field_name) < 0)
2039 2 : break;
2040 : }
2041 21 : if (i != oSelect.result_columns())
2042 4 : eErr = OGRERR_FAILURE;
2043 : }
2044 23 : if (eErr == OGRERR_NONE)
2045 : {
2046 17 : int op = -1;
2047 17 : swq_expr_node *poValue = nullptr;
2048 17 : if (oSelect.where_expr != nullptr)
2049 : {
2050 2 : op = oSelect.where_expr->nOperation;
2051 2 : poValue = oSelect.where_expr->papoSubExpr[1];
2052 : }
2053 :
2054 34 : FileGDBIterator *poIter = poLayer->BuildIndex(
2055 17 : oSelect.order_defs[0].field_name,
2056 17 : oSelect.order_defs[0].ascending_flag, op, poValue);
2057 :
2058 : /* Check that they are no NULL values */
2059 32 : if (oSelect.where_expr == nullptr && poIter != nullptr &&
2060 15 : poIter->GetRowCount() !=
2061 15 : poLayer->GetFeatureCount(FALSE))
2062 : {
2063 1 : delete poIter;
2064 1 : poIter = nullptr;
2065 : }
2066 :
2067 17 : if (poIter != nullptr)
2068 : {
2069 16 : CPLDebug("OpenFileGDB",
2070 : "Using OGROpenFileGDBSimpleSQLLayer");
2071 16 : bLastSQLUsedOptimizedImplementation = true;
2072 : return new OGROpenFileGDBSimpleSQLLayer(
2073 16 : poLayer, poIter, oSelect.result_columns(),
2074 16 : oSelect.column_defs.data(), oSelect.offset,
2075 16 : oSelect.limit);
2076 : }
2077 : }
2078 : }
2079 : }
2080 : }
2081 :
2082 477 : return GDALDataset::ExecuteSQL(pszSQLCommand, poSpatialFilter, pszDialect);
2083 : }
2084 :
2085 : /************************************************************************/
2086 : /* ReleaseResultSet() */
2087 : /************************************************************************/
2088 :
2089 748 : void OGROpenFileGDBDataSource::ReleaseResultSet(OGRLayer *poResultsSet)
2090 : {
2091 748 : delete poResultsSet;
2092 748 : }
2093 :
2094 : /************************************************************************/
2095 : /* GetFileList() */
2096 : /************************************************************************/
2097 :
2098 9 : char **OGROpenFileGDBDataSource::GetFileList()
2099 : {
2100 9 : int nInterestTable = -1;
2101 9 : const char *pszFilenameWithoutPath = CPLGetFilename(m_osDirName.c_str());
2102 18 : CPLString osFilenameRadix;
2103 9 : unsigned int unInterestTable = 0;
2104 18 : if (strlen(pszFilenameWithoutPath) == strlen("a00000000.gdbtable") &&
2105 9 : pszFilenameWithoutPath[0] == 'a' &&
2106 0 : sscanf(pszFilenameWithoutPath, "a%08x.gdbtable", &unInterestTable) == 1)
2107 : {
2108 0 : nInterestTable = static_cast<int>(unInterestTable);
2109 0 : osFilenameRadix = CPLSPrintf("a%08x.", nInterestTable);
2110 : }
2111 :
2112 9 : char **papszFiles = VSIReadDir(m_osDirName);
2113 18 : CPLStringList osStringList;
2114 9 : char **papszIter = papszFiles;
2115 1172 : for (; papszIter != nullptr && *papszIter != nullptr; papszIter++)
2116 : {
2117 1163 : if (strcmp(*papszIter, ".") == 0 || strcmp(*papszIter, "..") == 0)
2118 6 : continue;
2119 1157 : if (osFilenameRadix.empty() ||
2120 0 : strncmp(*papszIter, osFilenameRadix, osFilenameRadix.size()) == 0)
2121 : {
2122 1157 : osStringList.push_back(
2123 2314 : CPLFormFilenameSafe(m_osDirName, *papszIter, nullptr));
2124 : }
2125 : }
2126 9 : CSLDestroy(papszFiles);
2127 18 : return osStringList.StealList();
2128 : }
2129 :
2130 : /************************************************************************/
2131 : /* BuildSRS() */
2132 : /************************************************************************/
2133 :
2134 : OGRSpatialReferenceRefCountedPtr
2135 2827 : OGROpenFileGDBDataSource::BuildSRS(const CPLXMLNode *psInfo)
2136 : {
2137 : const char *pszWKT =
2138 2827 : CPLGetXMLValue(psInfo, "SpatialReference.WKT", nullptr);
2139 : const int nWKID =
2140 2827 : atoi(CPLGetXMLValue(psInfo, "SpatialReference.WKID", "0"));
2141 : // The concept of LatestWKID is explained in
2142 : // https://support.esri.com/en/technical-article/000013950
2143 : int nLatestWKID =
2144 2827 : atoi(CPLGetXMLValue(psInfo, "SpatialReference.LatestWKID", "0"));
2145 :
2146 2827 : OGRSpatialReferenceRefCountedPtr poSRS;
2147 2827 : if (nWKID > 0 || nLatestWKID > 0)
2148 : {
2149 : const auto ImportFromCode =
2150 2421 : [](OGRSpatialReference &oSRS, int nLatestCode, int nCode)
2151 : {
2152 2421 : bool bSuccess = false;
2153 2421 : CPLErrorStateBackuper oQuietError(CPLQuietErrorHandler);
2154 :
2155 : // Try first with nLatestWKID as there is a higher chance it is a
2156 : // EPSG code and not an ESRI one.
2157 2421 : if (nLatestCode > 0)
2158 : {
2159 142 : if (nLatestCode > 32767)
2160 : {
2161 1 : if (oSRS.SetFromUserInput(
2162 1 : CPLSPrintf("ESRI:%d", nLatestCode)) == OGRERR_NONE)
2163 : {
2164 0 : bSuccess = true;
2165 : }
2166 : }
2167 141 : else if (oSRS.importFromEPSG(nLatestCode) == OGRERR_NONE)
2168 : {
2169 141 : bSuccess = true;
2170 : }
2171 142 : if (!bSuccess)
2172 : {
2173 1 : CPLDebug("OpenFileGDB", "Cannot import SRID %d",
2174 : nLatestCode);
2175 : }
2176 : }
2177 2421 : if (!bSuccess && nCode > 0)
2178 : {
2179 2280 : if (nCode > 32767)
2180 : {
2181 1 : if (oSRS.SetFromUserInput(CPLSPrintf("ESRI:%d", nCode)) ==
2182 : OGRERR_NONE)
2183 : {
2184 0 : bSuccess = true;
2185 : }
2186 : }
2187 2279 : else if (oSRS.importFromEPSG(nCode) == OGRERR_NONE)
2188 : {
2189 2279 : bSuccess = true;
2190 : }
2191 2280 : if (!bSuccess)
2192 : {
2193 1 : CPLDebug("OpenFileGDB", "Cannot import SRID %d", nCode);
2194 : }
2195 : }
2196 :
2197 4842 : return bSuccess;
2198 : };
2199 :
2200 2420 : poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
2201 2420 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2202 2420 : if (!ImportFromCode(*poSRS.get(), nLatestWKID, nWKID))
2203 : {
2204 0 : poSRS.reset();
2205 : }
2206 : else
2207 : {
2208 2420 : const int nLatestVCSWKID = atoi(
2209 : CPLGetXMLValue(psInfo, "SpatialReference.LatestVCSWKID", "0"));
2210 : const int nVCSWKID =
2211 2420 : atoi(CPLGetXMLValue(psInfo, "SpatialReference.VCSWKID", "0"));
2212 2420 : if (nVCSWKID > 0 || nLatestVCSWKID > 0)
2213 : {
2214 : auto poVertSRS =
2215 2 : OGRSpatialReferenceRefCountedPtr::makeInstance();
2216 1 : if (ImportFromCode(*poVertSRS.get(), nLatestVCSWKID, nVCSWKID))
2217 : {
2218 : auto poCompoundSRS =
2219 0 : OGRSpatialReferenceRefCountedPtr::makeInstance();
2220 0 : if (poCompoundSRS->SetCompoundCS(
2221 0 : std::string(poSRS->GetName())
2222 0 : .append(" + ")
2223 0 : .append(poVertSRS->GetName())
2224 : .c_str(),
2225 0 : poSRS.get(), poVertSRS.get()) == OGRERR_NONE)
2226 : {
2227 0 : poCompoundSRS->SetAxisMappingStrategy(
2228 : OAMS_TRADITIONAL_GIS_ORDER);
2229 0 : poSRS = std::move(poCompoundSRS);
2230 : }
2231 : }
2232 2 : if (!poSRS->IsCompound() &&
2233 1 : !(pszWKT != nullptr && pszWKT[0] != '{'))
2234 : {
2235 0 : poSRS.reset();
2236 : }
2237 : }
2238 : }
2239 : }
2240 7667 : if (pszWKT != nullptr && pszWKT[0] != '{' &&
2241 2421 : (poSRS == nullptr ||
2242 2419 : (strstr(pszWKT, "VERTCS") && !poSRS->IsCompound())))
2243 : {
2244 3 : poSRS = BuildSRS(pszWKT);
2245 : }
2246 2827 : return poSRS;
2247 : }
2248 :
2249 : /************************************************************************/
2250 : /* BuildSRS() */
2251 : /************************************************************************/
2252 :
2253 : OGRSpatialReferenceRefCountedPtr
2254 365 : OGROpenFileGDBDataSource::BuildSRS(const char *pszWKT)
2255 : {
2256 365 : OGRSpatialReferenceRefCountedPtr poSRS;
2257 365 : m_oCacheWKTToSRS.tryGet(pszWKT, poSRS);
2258 365 : if (poSRS)
2259 199 : return poSRS;
2260 :
2261 166 : poSRS = OGRSpatialReferenceRefCountedPtr::makeInstance();
2262 166 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2263 166 : if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
2264 : {
2265 0 : poSRS.reset();
2266 : }
2267 166 : if (poSRS != nullptr)
2268 : {
2269 166 : if (CPLTestBool(CPLGetConfigOption("USE_OSR_FIND_MATCHES", "YES")))
2270 : {
2271 166 : auto poSRSMatch = poSRS->FindBestMatch(100);
2272 166 : if (poSRSMatch)
2273 : {
2274 165 : poSRS.reset(poSRSMatch, /* add_ref = */ false);
2275 165 : poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2276 : }
2277 166 : m_oCacheWKTToSRS.insert(pszWKT, poSRS);
2278 : }
2279 : else
2280 : {
2281 0 : poSRS->AutoIdentifyEPSG();
2282 : }
2283 : }
2284 166 : return poSRS;
2285 : }
|