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