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