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) 2022, 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 <limits>
20 : #include <map>
21 : #include <memory>
22 : #include <string>
23 : #include <utility>
24 : #include <vector>
25 :
26 : #include "cpl_conv.h"
27 : #include "cpl_error.h"
28 : #include "cpl_string.h"
29 : #include "cpl_vsi.h"
30 : #include "filegdbtable.h"
31 : #include "gdal.h"
32 : #include "ogr_core.h"
33 : #include "ogrsf_frmts.h"
34 :
35 : #include "filegdb_fielddomain.h"
36 : #include "filegdb_relationship.h"
37 :
38 : /***********************************************************************/
39 : /* GetExistingSpatialRef() */
40 : /***********************************************************************/
41 :
42 194 : bool OGROpenFileGDBDataSource::GetExistingSpatialRef(
43 : const std::string &osWKT, double dfXOrigin, double dfYOrigin,
44 : double dfXYScale, double dfZOrigin, double dfZScale, double dfMOrigin,
45 : double dfMScale, double dfXYTolerance, double dfZTolerance,
46 : double dfMTolerance)
47 : {
48 388 : FileGDBTable oTable;
49 194 : if (!oTable.Open(m_osGDBSpatialRefsFilename.c_str(), false))
50 0 : return false;
51 :
52 194 : FETCH_FIELD_IDX(iSRTEXT, "SRTEXT", FGFT_STRING);
53 194 : FETCH_FIELD_IDX(iFalseX, "FalseX", FGFT_FLOAT64);
54 194 : FETCH_FIELD_IDX(iFalseY, "FalseY", FGFT_FLOAT64);
55 194 : FETCH_FIELD_IDX(iXYUnits, "XYUnits", FGFT_FLOAT64);
56 194 : FETCH_FIELD_IDX(iFalseZ, "FalseZ", FGFT_FLOAT64);
57 194 : FETCH_FIELD_IDX(iZUnits, "ZUnits", FGFT_FLOAT64);
58 194 : FETCH_FIELD_IDX(iFalseM, "FalseM", FGFT_FLOAT64);
59 194 : FETCH_FIELD_IDX(iMUnits, "MUnits", FGFT_FLOAT64);
60 194 : FETCH_FIELD_IDX(iXYTolerance, "XYTolerance", FGFT_FLOAT64);
61 194 : FETCH_FIELD_IDX(iZTolerance, "ZTolerance", FGFT_FLOAT64);
62 194 : FETCH_FIELD_IDX(iMTolerance, "MTolerance", FGFT_FLOAT64);
63 :
64 194 : int64_t iCurFeat = 0;
65 389 : while (iCurFeat < oTable.GetTotalRecordCount())
66 : {
67 219 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
68 219 : if (iCurFeat < 0)
69 0 : break;
70 219 : iCurFeat++;
71 219 : const auto psSRTEXT = oTable.GetFieldValue(iSRTEXT);
72 219 : if (psSRTEXT && psSRTEXT->String == osWKT)
73 : {
74 250 : const auto fetchRealVal = [&oTable](int idx, double dfExpected)
75 : {
76 250 : const auto psVal = oTable.GetFieldValue(idx);
77 250 : return psVal && psVal->Real == dfExpected;
78 34 : };
79 34 : if (fetchRealVal(iFalseX, dfXOrigin) &&
80 24 : fetchRealVal(iFalseY, dfYOrigin) &&
81 24 : fetchRealVal(iXYUnits, dfXYScale) &&
82 24 : fetchRealVal(iFalseZ, dfZOrigin) &&
83 24 : fetchRealVal(iZUnits, dfZScale) &&
84 24 : fetchRealVal(iFalseM, dfMOrigin) &&
85 24 : fetchRealVal(iMUnits, dfMScale) &&
86 24 : fetchRealVal(iXYTolerance, dfXYTolerance) &&
87 82 : fetchRealVal(iZTolerance, dfZTolerance) &&
88 24 : fetchRealVal(iMTolerance, dfMTolerance))
89 : {
90 24 : return true;
91 : }
92 : }
93 : }
94 170 : return false;
95 : }
96 :
97 : /***********************************************************************/
98 : /* AddNewSpatialRef() */
99 : /***********************************************************************/
100 :
101 398 : bool OGROpenFileGDBDataSource::AddNewSpatialRef(
102 : const std::string &osWKT, double dfXOrigin, double dfYOrigin,
103 : double dfXYScale, double dfZOrigin, double dfZScale, double dfMOrigin,
104 : double dfMScale, double dfXYTolerance, double dfZTolerance,
105 : double dfMTolerance)
106 : {
107 796 : FileGDBTable oTable;
108 398 : if (!oTable.Open(m_osGDBSpatialRefsFilename.c_str(), true))
109 0 : return false;
110 :
111 398 : FETCH_FIELD_IDX(iSRTEXT, "SRTEXT", FGFT_STRING);
112 398 : FETCH_FIELD_IDX(iFalseX, "FalseX", FGFT_FLOAT64);
113 398 : FETCH_FIELD_IDX(iFalseY, "FalseY", FGFT_FLOAT64);
114 398 : FETCH_FIELD_IDX(iXYUnits, "XYUnits", FGFT_FLOAT64);
115 398 : FETCH_FIELD_IDX(iFalseZ, "FalseZ", FGFT_FLOAT64);
116 398 : FETCH_FIELD_IDX(iZUnits, "ZUnits", FGFT_FLOAT64);
117 398 : FETCH_FIELD_IDX(iFalseM, "FalseM", FGFT_FLOAT64);
118 398 : FETCH_FIELD_IDX(iMUnits, "MUnits", FGFT_FLOAT64);
119 398 : FETCH_FIELD_IDX(iXYTolerance, "XYTolerance", FGFT_FLOAT64);
120 398 : FETCH_FIELD_IDX(iZTolerance, "ZTolerance", FGFT_FLOAT64);
121 398 : FETCH_FIELD_IDX(iMTolerance, "MTolerance", FGFT_FLOAT64);
122 :
123 398 : std::vector<OGRField> fields(oTable.GetFieldCount(),
124 796 : FileGDBField::UNSET_FIELD);
125 398 : fields[iSRTEXT].String = const_cast<char *>(osWKT.c_str());
126 398 : fields[iFalseX].Real = dfXOrigin;
127 398 : fields[iFalseY].Real = dfYOrigin;
128 398 : fields[iXYUnits].Real = dfXYScale;
129 398 : fields[iFalseZ].Real = dfZOrigin;
130 398 : fields[iZUnits].Real = dfZScale;
131 398 : fields[iFalseM].Real = dfMOrigin;
132 398 : fields[iMUnits].Real = dfMScale;
133 398 : fields[iXYTolerance].Real = dfXYTolerance;
134 398 : fields[iZTolerance].Real = dfZTolerance;
135 398 : fields[iMTolerance].Real = dfMTolerance;
136 :
137 398 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
138 : }
139 :
140 : /***********************************************************************/
141 : /* RegisterLayerInSystemCatalog() */
142 : /***********************************************************************/
143 :
144 267 : bool OGROpenFileGDBDataSource::RegisterLayerInSystemCatalog(
145 : const std::string &osLayerName)
146 : {
147 534 : FileGDBTable oTable;
148 267 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), true))
149 0 : return false;
150 :
151 267 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
152 267 : FETCH_FIELD_IDX(iFileFormat, "FileFormat", FGFT_INT32);
153 :
154 267 : std::vector<OGRField> fields(oTable.GetFieldCount(),
155 534 : FileGDBField::UNSET_FIELD);
156 267 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
157 267 : fields[iFileFormat].Integer = 0;
158 267 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
159 : }
160 :
161 : /***********************************************************************/
162 : /* RegisterInItemRelationships() */
163 : /***********************************************************************/
164 :
165 294 : bool OGROpenFileGDBDataSource::RegisterInItemRelationships(
166 : const std::string &osOriginGUID, const std::string &osDestGUID,
167 : const std::string &osTypeGUID)
168 : {
169 588 : FileGDBTable oTable;
170 294 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
171 0 : return false;
172 :
173 294 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
174 294 : FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID);
175 294 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
176 294 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
177 294 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
178 :
179 294 : std::vector<OGRField> fields(oTable.GetFieldCount(),
180 882 : FileGDBField::UNSET_FIELD);
181 294 : const std::string osGUID = OFGDBGenerateUUID();
182 294 : fields[iUUID].String = const_cast<char *>(osGUID.c_str());
183 294 : fields[iOriginID].String = const_cast<char *>(osOriginGUID.c_str());
184 294 : fields[iDestID].String = const_cast<char *>(osDestGUID.c_str());
185 294 : fields[iType].String = const_cast<char *>(osTypeGUID.c_str());
186 294 : fields[iProperties].Integer = 1;
187 294 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
188 : }
189 :
190 : /***********************************************************************/
191 : /* RegisterRelationshipInItemRelationships() */
192 : /***********************************************************************/
193 :
194 6 : bool OGROpenFileGDBDataSource::RegisterRelationshipInItemRelationships(
195 : const std::string &osRelationshipGUID, const std::string &osOriginGUID,
196 : const std::string &osDestGUID)
197 : {
198 : // Relationships to register:
199 : // 1. Origin table -> new relationship as DatasetsRelatedThrough
200 : // 2. Destination table -> new relationship as DatasetsRelatedThrough
201 : // 3. Root dataset -> new relationship as DatasetInFolder
202 6 : if (!RegisterInItemRelationships(osOriginGUID, osRelationshipGUID,
203 : pszDatasetsRelatedThroughUUID))
204 0 : return false;
205 6 : if (!RegisterInItemRelationships(osDestGUID, osRelationshipGUID,
206 : pszDatasetsRelatedThroughUUID))
207 0 : return false;
208 6 : if (!RegisterInItemRelationships(m_osRootGUID, osRelationshipGUID,
209 : pszDatasetInFolderUUID))
210 0 : return false;
211 :
212 6 : return true;
213 : }
214 :
215 : /***********************************************************************/
216 : /* RemoveRelationshipFromItemRelationships() */
217 : /***********************************************************************/
218 :
219 3 : bool OGROpenFileGDBDataSource::RemoveRelationshipFromItemRelationships(
220 : const std::string &osRelationshipGUID)
221 : {
222 6 : FileGDBTable oTable;
223 3 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
224 0 : return false;
225 :
226 : // while we've only found item relationships with the relationship UUID in
227 : // the DestID field, let's be super-careful and also check against the
228 : // OriginID UUID, just in case there's some previously unencountered
229 : // situations where a relationship UUID is placed in OriginID
230 3 : FETCH_FIELD_IDX_WITH_RET(iOriginID, "OriginID", FGFT_GUID, false);
231 3 : FETCH_FIELD_IDX_WITH_RET(iDestID, "DestID", FGFT_GUID, false);
232 :
233 68 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
234 : ++iCurFeat)
235 : {
236 65 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
237 65 : if (iCurFeat < 0)
238 0 : break;
239 :
240 65 : const auto psOriginID = oTable.GetFieldValue(iOriginID);
241 65 : if (psOriginID && psOriginID->String == osRelationshipGUID)
242 : {
243 0 : oTable.DeleteFeature(iCurFeat + 1);
244 : }
245 : else
246 : {
247 65 : const auto psDestID = oTable.GetFieldValue(iDestID);
248 65 : if (psDestID && psDestID->String == osRelationshipGUID)
249 : {
250 9 : oTable.DeleteFeature(iCurFeat + 1);
251 : }
252 : }
253 : }
254 :
255 3 : return true;
256 : }
257 :
258 : /***********************************************************************/
259 : /* LinkDomainToTable() */
260 : /***********************************************************************/
261 :
262 6 : bool OGROpenFileGDBDataSource::LinkDomainToTable(
263 : const std::string &osDomainName, const std::string &osLayerGUID)
264 : {
265 12 : std::string osDomainUUID;
266 6 : if (!FindUUIDFromName(osDomainName, osDomainUUID))
267 0 : return false;
268 :
269 : // Check if the domain is already linked to this table
270 : {
271 6 : FileGDBTable oTable;
272 6 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), false))
273 0 : return false;
274 :
275 6 : FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID);
276 6 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
277 :
278 10 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
279 : ++iCurFeat)
280 : {
281 5 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
282 5 : if (iCurFeat < 0)
283 0 : break;
284 :
285 5 : const auto psOriginID = oTable.GetFieldValue(iOriginID);
286 5 : if (psOriginID && EQUAL(psOriginID->String, osLayerGUID.c_str()))
287 : {
288 4 : const auto psDestID = oTable.GetFieldValue(iDestID);
289 4 : if (psDestID && EQUAL(psDestID->String, osDomainUUID.c_str()))
290 : {
291 1 : return true;
292 : }
293 : }
294 : }
295 : }
296 :
297 10 : return RegisterInItemRelationships(osLayerGUID, osDomainUUID,
298 5 : pszDomainInDatasetUUID);
299 : }
300 :
301 : /***********************************************************************/
302 : /* UnlinkDomainToTable() */
303 : /***********************************************************************/
304 :
305 1 : bool OGROpenFileGDBDataSource::UnlinkDomainToTable(
306 : const std::string &osDomainName, const std::string &osLayerGUID)
307 : {
308 2 : std::string osDomainUUID;
309 1 : if (!FindUUIDFromName(osDomainName, osDomainUUID))
310 0 : return false;
311 :
312 2 : FileGDBTable oTable;
313 1 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
314 0 : return false;
315 :
316 1 : FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID);
317 1 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
318 :
319 1 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
320 : ++iCurFeat)
321 : {
322 1 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
323 1 : if (iCurFeat < 0)
324 0 : break;
325 :
326 1 : const auto psOriginID = oTable.GetFieldValue(iOriginID);
327 1 : if (psOriginID && EQUAL(psOriginID->String, osLayerGUID.c_str()))
328 : {
329 1 : const auto psDestID = oTable.GetFieldValue(iDestID);
330 1 : if (psDestID && EQUAL(psDestID->String, osDomainUUID.c_str()))
331 : {
332 1 : return oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync();
333 : }
334 : }
335 : }
336 :
337 0 : return true;
338 : }
339 :
340 : /***********************************************************************/
341 : /* UpdateXMLDefinition() */
342 : /***********************************************************************/
343 :
344 31 : bool OGROpenFileGDBDataSource::UpdateXMLDefinition(
345 : const std::string &osLayerName, const char *pszXMLDefinition)
346 : {
347 62 : FileGDBTable oTable;
348 31 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
349 0 : return false;
350 :
351 31 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
352 31 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
353 :
354 105 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
355 : ++iCurFeat)
356 : {
357 105 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
358 105 : if (iCurFeat < 0)
359 0 : break;
360 105 : const auto psName = oTable.GetFieldValue(iName);
361 105 : if (psName && psName->String == osLayerName)
362 : {
363 31 : auto asFields = oTable.GetAllFieldValues();
364 62 : if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
365 31 : !OGR_RawField_IsUnset(&asFields[iDefinition]))
366 : {
367 31 : CPLFree(asFields[iDefinition].String);
368 : }
369 31 : asFields[iDefinition].String = CPLStrdup(pszXMLDefinition);
370 31 : bool bRet = oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr);
371 31 : oTable.FreeAllFieldValues(asFields);
372 31 : return bRet;
373 : }
374 : }
375 :
376 0 : CPLError(CE_Failure, CPLE_AppDefined,
377 : "Cannot find record for Name=%s in GDB_Items table",
378 : osLayerName.c_str());
379 0 : return false;
380 : }
381 :
382 : /***********************************************************************/
383 : /* FindUUIDFromName() */
384 : /***********************************************************************/
385 :
386 22 : bool OGROpenFileGDBDataSource::FindUUIDFromName(const std::string &osName,
387 : std::string &osUUIDOut)
388 : {
389 44 : FileGDBTable oTable;
390 22 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
391 0 : return false;
392 :
393 22 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
394 22 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
395 :
396 153 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
397 : ++iCurFeat)
398 : {
399 152 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
400 152 : if (iCurFeat < 0)
401 0 : break;
402 152 : const auto psName = oTable.GetFieldValue(iName);
403 152 : if (psName && psName->String == osName)
404 : {
405 21 : const auto psUUID = oTable.GetFieldValue(iUUID);
406 21 : if (psUUID)
407 : {
408 21 : osUUIDOut = psUUID->String;
409 21 : return true;
410 : }
411 : }
412 : }
413 :
414 1 : return false;
415 : }
416 :
417 : /***********************************************************************/
418 : /* RegisterFeatureDatasetInItems() */
419 : /***********************************************************************/
420 :
421 3 : bool OGROpenFileGDBDataSource::RegisterFeatureDatasetInItems(
422 : const std::string &osFeatureDatasetGUID, const std::string &osName,
423 : const char *pszXMLDefinition)
424 : {
425 6 : FileGDBTable oTable;
426 3 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
427 0 : return false;
428 :
429 3 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
430 3 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
431 3 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
432 3 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
433 3 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
434 3 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
435 3 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
436 3 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
437 :
438 3 : std::vector<OGRField> fields(oTable.GetFieldCount(),
439 9 : FileGDBField::UNSET_FIELD);
440 3 : fields[iUUID].String = const_cast<char *>(osFeatureDatasetGUID.c_str());
441 3 : fields[iType].String = const_cast<char *>(pszFeatureDatasetTypeUUID);
442 3 : fields[iName].String = const_cast<char *>(osName.c_str());
443 6 : CPLString osUCName(osName);
444 3 : osUCName.toupper();
445 3 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
446 3 : std::string osPath("\\");
447 3 : osPath += osName;
448 3 : fields[iPath].String = const_cast<char *>(osPath.c_str());
449 3 : fields[iURL].String = const_cast<char *>("");
450 3 : fields[iDefinition].String = const_cast<char *>(pszXMLDefinition);
451 3 : fields[iProperties].Integer = 1;
452 3 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
453 : }
454 :
455 : /***********************************************************************/
456 : /* RegisterFeatureClassInItems() */
457 : /***********************************************************************/
458 :
459 195 : bool OGROpenFileGDBDataSource::RegisterFeatureClassInItems(
460 : const std::string &osLayerGUID, const std::string &osLayerName,
461 : const std::string &osPath, const FileGDBTable *poLyrTable,
462 : const char *pszXMLDefinition, const char *pszDocumentation)
463 : {
464 390 : FileGDBTable oTable;
465 195 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
466 0 : return false;
467 :
468 195 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
469 195 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
470 195 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
471 195 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
472 195 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
473 195 : FETCH_FIELD_IDX(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32);
474 195 : FETCH_FIELD_IDX(iDatasetSubtype2, "DatasetSubtype2", FGFT_INT32);
475 195 : FETCH_FIELD_IDX(iDatasetInfo1, "DatasetInfo1", FGFT_STRING);
476 195 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
477 195 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
478 195 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
479 195 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
480 :
481 195 : std::vector<OGRField> fields(oTable.GetFieldCount(),
482 585 : FileGDBField::UNSET_FIELD);
483 195 : fields[iUUID].String = const_cast<char *>(osLayerGUID.c_str());
484 195 : fields[iType].String = const_cast<char *>(pszFeatureClassTypeUUID);
485 195 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
486 195 : CPLString osUCName(osLayerName);
487 195 : osUCName.toupper();
488 195 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
489 195 : fields[iPath].String = const_cast<char *>(osPath.c_str());
490 195 : fields[iDatasetSubtype1].Integer = 1;
491 195 : fields[iDatasetSubtype2].Integer = poLyrTable->GetGeometryType();
492 195 : const auto poGeomFieldDefn = poLyrTable->GetGeomField();
493 195 : if (poGeomFieldDefn) // should always be true
494 194 : fields[iDatasetInfo1].String =
495 194 : const_cast<char *>(poGeomFieldDefn->GetName().c_str());
496 195 : fields[iURL].String = const_cast<char *>("");
497 195 : fields[iDefinition].String = const_cast<char *>(pszXMLDefinition);
498 195 : if (pszDocumentation && pszDocumentation[0])
499 1 : fields[iDocumentation].String = const_cast<char *>(pszDocumentation);
500 195 : fields[iProperties].Integer = 1;
501 195 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
502 : }
503 :
504 : /***********************************************************************/
505 : /* RegisterASpatialTableInItems() */
506 : /***********************************************************************/
507 :
508 73 : bool OGROpenFileGDBDataSource::RegisterASpatialTableInItems(
509 : const std::string &osLayerGUID, const std::string &osLayerName,
510 : const std::string &osPath, const char *pszXMLDefinition,
511 : const char *pszDocumentation)
512 : {
513 146 : FileGDBTable oTable;
514 73 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
515 0 : return false;
516 :
517 73 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
518 73 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
519 73 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
520 73 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
521 73 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
522 73 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
523 73 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
524 73 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
525 73 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
526 :
527 73 : std::vector<OGRField> fields(oTable.GetFieldCount(),
528 219 : FileGDBField::UNSET_FIELD);
529 73 : fields[iUUID].String = const_cast<char *>(osLayerGUID.c_str());
530 73 : fields[iType].String = const_cast<char *>(pszTableTypeUUID);
531 73 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
532 73 : CPLString osUCName(osLayerName);
533 73 : osUCName.toupper();
534 73 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
535 73 : fields[iPath].String = const_cast<char *>(osPath.c_str());
536 73 : fields[iURL].String = const_cast<char *>("");
537 73 : fields[iDefinition].String = const_cast<char *>(pszXMLDefinition);
538 73 : if (pszDocumentation && pszDocumentation[0])
539 0 : fields[iDocumentation].String = const_cast<char *>(pszDocumentation);
540 73 : fields[iProperties].Integer = 1;
541 73 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
542 : }
543 :
544 : /***********************************************************************/
545 : /* CreateGDBSystemCatalog() */
546 : /***********************************************************************/
547 :
548 228 : bool OGROpenFileGDBDataSource::CreateGDBSystemCatalog()
549 : {
550 : // Write GDB_SystemCatalog file
551 : m_osGDBSystemCatalogFilename =
552 228 : CPLFormFilename(m_osDirName.c_str(), "a00000001.gdbtable", nullptr);
553 456 : FileGDBTable oTable;
554 228 : if (!oTable.Create(m_osGDBSystemCatalogFilename.c_str(), 4, FGTGT_NONE,
555 456 : false, false) ||
556 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
557 228 : "ID", std::string(), FGFT_OBJECTID,
558 0 : /* bNullable = */ false,
559 0 : /* bRequired = */ true,
560 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
561 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
562 228 : "Name", std::string(), FGFT_STRING,
563 0 : /* bNullable = */ false,
564 0 : /* bRequired = */ false,
565 1140 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
566 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
567 228 : "FileFormat", std::string(), FGFT_INT32,
568 0 : /* bNullable = */ false,
569 0 : /* bRequired = */ false,
570 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
571 : {
572 0 : return false;
573 : }
574 :
575 228 : std::vector<OGRField> fields(oTable.GetFieldCount(),
576 684 : FileGDBField::UNSET_FIELD);
577 :
578 1824 : for (const auto &pair : std::vector<std::pair<const char *, int>>{
579 : {"GDB_SystemCatalog", 0},
580 : {"GDB_DBTune", 0},
581 : {"GDB_SpatialRefs", 0},
582 : {"GDB_Items", 0},
583 : {"GDB_ItemTypes", 0},
584 : {"GDB_ItemRelationships", 0},
585 : {"GDB_ItemRelationshipTypes", 0},
586 3876 : {"GDB_ReplicaLog", 2}})
587 : {
588 1824 : fields[1].String = const_cast<char *>(pair.first);
589 1824 : fields[2].Integer = pair.second;
590 1824 : if (!oTable.CreateFeature(fields, nullptr))
591 0 : return false;
592 : }
593 :
594 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
595 228 : this, m_osGDBSystemCatalogFilename.c_str(), "GDB_SystemCatalog", "", "",
596 456 : true));
597 :
598 228 : return oTable.Sync();
599 : }
600 :
601 : /***********************************************************************/
602 : /* CreateGDBDBTune() */
603 : /***********************************************************************/
604 :
605 228 : bool OGROpenFileGDBDataSource::CreateGDBDBTune()
606 : {
607 : // Write GDB_DBTune file
608 : const std::string osFilename(
609 456 : CPLFormFilename(m_osDirName.c_str(), "a00000002.gdbtable", nullptr));
610 456 : FileGDBTable oTable;
611 228 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
612 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
613 228 : "Keyword", std::string(), FGFT_STRING,
614 0 : /* bNullable = */ false,
615 0 : /* bRequired = */ false,
616 456 : /* bEditable = */ true, 32, FileGDBField::UNSET_FIELD)) ||
617 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
618 228 : "ParameterName", std::string(), FGFT_STRING,
619 0 : /* bNullable = */ false,
620 0 : /* bRequired = */ false,
621 1140 : /* bEditable = */ true, 32, FileGDBField::UNSET_FIELD)) ||
622 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
623 228 : "ConfigString", std::string(), FGFT_STRING,
624 0 : /* bNullable = */ true,
625 0 : /* bRequired = */ false,
626 456 : /* bEditable = */ true, 2048, FileGDBField::UNSET_FIELD)))
627 : {
628 0 : return false;
629 : }
630 :
631 228 : std::vector<OGRField> fields(oTable.GetFieldCount(),
632 684 : FileGDBField::UNSET_FIELD);
633 :
634 : static const struct
635 : {
636 : const char *pszKeyword;
637 : const char *pszParameterName;
638 : const char *pszConfigString;
639 : } apsData[] = {
640 : {"DEFAULTS", "UI_TEXT", "The default datafile configuration."},
641 : {"DEFAULTS", "CHARACTER_FORMAT", "UTF8"},
642 : {"DEFAULTS", "GEOMETRY_FORMAT", "Compressed"},
643 : {"DEFAULTS", "GEOMETRY_STORAGE", "InLine"},
644 : {"DEFAULTS", "BLOB_STORAGE", "InLine"},
645 : {"DEFAULTS", "MAX_FILE_SIZE", "1TB"},
646 : {"DEFAULTS", "RASTER_STORAGE", "InLine"},
647 : {"TEXT_UTF16", "UI_TEXT", "The UTF16 text format configuration."},
648 : {"TEXT_UTF16", "CHARACTER_FORMAT", "UTF16"},
649 : {"MAX_FILE_SIZE_4GB", "UI_TEXT",
650 : "The 4GB maximum datafile size configuration."},
651 : {"MAX_FILE_SIZE_4GB", "MAX_FILE_SIZE", "4GB"},
652 : {"MAX_FILE_SIZE_256TB", "UI_TEXT",
653 : "The 256TB maximum datafile size configuration."},
654 : {"MAX_FILE_SIZE_256TB", "MAX_FILE_SIZE", "256TB"},
655 : {"GEOMETRY_UNCOMPRESSED", "UI_TEXT",
656 : "The Uncompressed Geometry configuration."},
657 : {"GEOMETRY_UNCOMPRESSED", "GEOMETRY_FORMAT", "Uncompressed"},
658 : {"GEOMETRY_OUTOFLINE", "UI_TEXT",
659 : "The Outofline Geometry configuration."},
660 : {"GEOMETRY_OUTOFLINE", "GEOMETRY_STORAGE", "OutOfLine"},
661 : {"BLOB_OUTOFLINE", "UI_TEXT", "The Outofline Blob configuration."},
662 : {"BLOB_OUTOFLINE", "BLOB_STORAGE", "OutOfLine"},
663 : {"GEOMETRY_AND_BLOB_OUTOFLINE", "UI_TEXT",
664 : "The Outofline Geometry and Blob configuration."},
665 : {"GEOMETRY_AND_BLOB_OUTOFLINE", "GEOMETRY_STORAGE", "OutOfLine"},
666 : {"GEOMETRY_AND_BLOB_OUTOFLINE", "BLOB_STORAGE", "OutOfLine"},
667 : {"TERRAIN_DEFAULTS", "UI_TERRAIN_TEXT",
668 : "The terrains default configuration."},
669 : {"TERRAIN_DEFAULTS", "GEOMETRY_STORAGE", "OutOfLine"},
670 : {"TERRAIN_DEFAULTS", "BLOB_STORAGE", "OutOfLine"},
671 : {"MOSAICDATASET_DEFAULTS", "UI_MOSAIC_TEXT",
672 : "The Outofline Raster and Blob configuration."},
673 : {"MOSAICDATASET_DEFAULTS", "RASTER_STORAGE", "OutOfLine"},
674 : {"MOSAICDATASET_DEFAULTS", "BLOB_STORAGE", "OutOfLine"},
675 : {"MOSAICDATASET_INLINE", "UI_MOSAIC_TEXT",
676 : "The mosaic dataset inline configuration."},
677 : {"MOSAICDATASET_INLINE", "CHARACTER_FORMAT", "UTF8"},
678 : {"MOSAICDATASET_INLINE", "GEOMETRY_FORMAT", "Compressed"},
679 : {"MOSAICDATASET_INLINE", "GEOMETRY_STORAGE", "InLine"},
680 : {"MOSAICDATASET_INLINE", "BLOB_STORAGE", "InLine"},
681 : {"MOSAICDATASET_INLINE", "MAX_FILE_SIZE", "1TB"},
682 : {"MOSAICDATASET_INLINE", "RASTER_STORAGE", "InLine"}};
683 :
684 8208 : for (const auto &record : apsData)
685 : {
686 7980 : fields[0].String = const_cast<char *>(record.pszKeyword);
687 7980 : fields[1].String = const_cast<char *>(record.pszParameterName);
688 7980 : fields[2].String = const_cast<char *>(record.pszConfigString);
689 7980 : if (!oTable.CreateFeature(fields, nullptr))
690 0 : return false;
691 : }
692 :
693 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
694 228 : this, osFilename.c_str(), "GDB_DBTune", "", "", true));
695 :
696 228 : return oTable.Sync();
697 : }
698 :
699 : /***********************************************************************/
700 : /* CreateGDBSpatialRefs() */
701 : /***********************************************************************/
702 :
703 228 : bool OGROpenFileGDBDataSource::CreateGDBSpatialRefs()
704 : {
705 : // Write GDB_SpatialRefs file
706 : m_osGDBSpatialRefsFilename =
707 228 : CPLFormFilename(m_osDirName.c_str(), "a00000003.gdbtable", nullptr);
708 456 : FileGDBTable oTable;
709 228 : if (!oTable.Create(m_osGDBSpatialRefsFilename.c_str(), 4, FGTGT_NONE, false,
710 456 : false) ||
711 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
712 228 : "ID", std::string(), FGFT_OBJECTID,
713 0 : /* bNullable = */ false,
714 0 : /* bRequired = */ true,
715 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
716 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
717 228 : "SRTEXT", std::string(), FGFT_STRING,
718 0 : /* bNullable = */ false,
719 0 : /* bRequired = */ false,
720 456 : /* bEditable = */ true, 2048, FileGDBField::UNSET_FIELD)) ||
721 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
722 228 : "FalseX", std::string(), FGFT_FLOAT64,
723 0 : /* bNullable = */ true,
724 0 : /* bRequired = */ false,
725 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
726 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
727 228 : "FalseY", std::string(), FGFT_FLOAT64,
728 0 : /* bNullable = */ true,
729 0 : /* bRequired = */ false,
730 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
731 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
732 228 : "XYUnits", std::string(), FGFT_FLOAT64,
733 0 : /* bNullable = */ true,
734 0 : /* bRequired = */ false,
735 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
736 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
737 228 : "FalseZ", std::string(), FGFT_FLOAT64,
738 0 : /* bNullable = */ true,
739 0 : /* bRequired = */ false,
740 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
741 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
742 228 : "ZUnits", std::string(), FGFT_FLOAT64,
743 0 : /* bNullable = */ true,
744 0 : /* bRequired = */ false,
745 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
746 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
747 228 : "FalseM", std::string(), FGFT_FLOAT64,
748 0 : /* bNullable = */ true,
749 0 : /* bRequired = */ false,
750 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
751 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
752 228 : "MUnits", std::string(), FGFT_FLOAT64,
753 0 : /* bNullable = */ true,
754 0 : /* bRequired = */ false,
755 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
756 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
757 228 : "XYTolerance", std::string(), FGFT_FLOAT64,
758 0 : /* bNullable = */ true,
759 0 : /* bRequired = */ false,
760 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
761 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
762 228 : "ZTolerance", std::string(), FGFT_FLOAT64,
763 0 : /* bNullable = */ true,
764 0 : /* bRequired = */ false,
765 1140 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
766 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
767 228 : "MTolerance", std::string(), FGFT_FLOAT64,
768 0 : /* bNullable = */ true,
769 0 : /* bRequired = */ false,
770 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
771 : {
772 0 : return false;
773 : }
774 :
775 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
776 228 : this, m_osGDBSpatialRefsFilename.c_str(), "GDB_SpatialRefs", "", "",
777 456 : true));
778 :
779 228 : return oTable.Sync();
780 : }
781 :
782 : /***********************************************************************/
783 : /* CreateGDBItems() */
784 : /***********************************************************************/
785 :
786 228 : bool OGROpenFileGDBDataSource::CreateGDBItems()
787 : {
788 : // Write GDB_Items file
789 228 : const char *ESRI_WKT_WGS84 =
790 : "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_1984\","
791 : "6378137.0,298.257223563]],PRIMEM[\"Greenwich\",0.0],UNIT[\"Degree\",0."
792 : "0174532925199433]]";
793 : auto poGeomField = std::unique_ptr<FileGDBGeomField>(
794 : new FileGDBGeomField("Shape", "", true, ESRI_WKT_WGS84, -180, -90,
795 684 : 1000000, 0.000002, {0.012, 0.4, 12.0}));
796 228 : poGeomField->SetZOriginScaleTolerance(-100000, 10000, 0.001);
797 228 : poGeomField->SetMOriginScaleTolerance(-100000, 10000, 0.001);
798 :
799 228 : if (!AddNewSpatialRef(poGeomField->GetWKT(), poGeomField->GetXOrigin(),
800 : poGeomField->GetYOrigin(), poGeomField->GetXYScale(),
801 : poGeomField->GetZOrigin(), poGeomField->GetZScale(),
802 : poGeomField->GetMOrigin(), poGeomField->GetMScale(),
803 : poGeomField->GetXYTolerance(),
804 : poGeomField->GetZTolerance(),
805 : poGeomField->GetMTolerance()))
806 : {
807 0 : return false;
808 : }
809 :
810 : m_osGDBItemsFilename =
811 228 : CPLFormFilename(m_osDirName.c_str(), "a00000004.gdbtable", nullptr);
812 456 : FileGDBTable oTable;
813 228 : if (!oTable.Create(m_osGDBItemsFilename.c_str(), 4, FGTGT_POLYGON, false,
814 456 : false) ||
815 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
816 228 : "ObjectID", std::string(), FGFT_OBJECTID,
817 0 : /* bNullable = */ false,
818 0 : /* bRequired = */ true,
819 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
820 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
821 228 : "UUID", std::string(), FGFT_GLOBALID,
822 0 : /* bNullable = */ false,
823 0 : /* bRequired = */ true,
824 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
825 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
826 228 : "Type", std::string(), FGFT_GUID,
827 0 : /* bNullable = */ false,
828 0 : /* bRequired = */ false,
829 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
830 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
831 228 : "Name", std::string(), FGFT_STRING,
832 0 : /* bNullable = */ true,
833 0 : /* bRequired = */ false,
834 456 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
835 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
836 228 : "PhysicalName", std::string(), FGFT_STRING,
837 0 : /* bNullable = */ true,
838 0 : /* bRequired = */ false,
839 456 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
840 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
841 228 : "Path", std::string(), FGFT_STRING,
842 0 : /* bNullable = */ true,
843 0 : /* bRequired = */ false,
844 456 : /* bEditable = */ true, 260, FileGDBField::UNSET_FIELD)) ||
845 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
846 228 : "DatasetSubtype1", std::string(), FGFT_INT32,
847 0 : /* bNullable = */ true,
848 0 : /* bRequired = */ false,
849 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
850 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
851 228 : "DatasetSubtype2", std::string(), FGFT_INT32,
852 0 : /* bNullable = */ true,
853 0 : /* bRequired = */ false,
854 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
855 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
856 228 : "DatasetInfo1", std::string(), FGFT_STRING,
857 0 : /* bNullable = */ true,
858 0 : /* bRequired = */ false,
859 456 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
860 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
861 228 : "DatasetInfo2", std::string(), FGFT_STRING,
862 0 : /* bNullable = */ true,
863 0 : /* bRequired = */ false,
864 456 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
865 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
866 228 : "URL", std::string(), FGFT_STRING,
867 0 : /* bNullable = */ true,
868 0 : /* bRequired = */ false,
869 456 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
870 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
871 228 : "Definition", std::string(), FGFT_XML,
872 0 : /* bNullable = */ true,
873 0 : /* bRequired = */ false,
874 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
875 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
876 228 : "Documentation", std::string(), FGFT_XML,
877 0 : /* bNullable = */ true,
878 0 : /* bRequired = */ false,
879 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
880 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
881 228 : "ItemInfo", std::string(), FGFT_XML,
882 0 : /* bNullable = */ true,
883 0 : /* bRequired = */ false,
884 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
885 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
886 228 : "Properties", std::string(), FGFT_INT32,
887 0 : /* bNullable = */ true,
888 0 : /* bRequired = */ false,
889 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
890 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
891 228 : "Defaults", std::string(), FGFT_BINARY,
892 0 : /* bNullable = */ true,
893 0 : /* bRequired = */ false,
894 1140 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
895 456 : !oTable.CreateField(std::move(poGeomField)))
896 : {
897 0 : return false;
898 : }
899 :
900 228 : std::vector<OGRField> fields(oTable.GetFieldCount(),
901 684 : FileGDBField::UNSET_FIELD);
902 228 : m_osRootGUID = OFGDBGenerateUUID();
903 228 : fields[1].String = const_cast<char *>(m_osRootGUID.c_str());
904 228 : fields[2].String = const_cast<char *>(pszFolderTypeUUID);
905 228 : fields[3].String = const_cast<char *>("");
906 228 : fields[4].String = const_cast<char *>("");
907 228 : fields[5].String = const_cast<char *>("\\");
908 228 : fields[10].String = const_cast<char *>("");
909 228 : fields[14].Integer = 1;
910 228 : if (!oTable.CreateFeature(fields, nullptr))
911 0 : return false;
912 :
913 228 : const std::string osWorkspaceUUID(OFGDBGenerateUUID());
914 228 : fields[1].String = const_cast<char *>(osWorkspaceUUID.c_str());
915 228 : fields[2].String = const_cast<char *>(pszWorkspaceTypeUUID);
916 228 : fields[3].String = const_cast<char *>("Workspace");
917 228 : fields[4].String = const_cast<char *>("WORKSPACE");
918 228 : fields[5].String = const_cast<char *>(""); // Path
919 228 : fields[10].String = const_cast<char *>(""); // URL
920 228 : fields[11].String = const_cast<char *>(
921 : "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
922 : "<DEWorkspace xmlns:typens=\"http://www.esri.com/schemas/ArcGIS/10.3\" "
923 : "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
924 : "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
925 : "xsi:type=\"typens:DEWorkspace\">\n"
926 : " <CatalogPath>\\</CatalogPath>\n"
927 : " <Name/>\n"
928 : " <ChildrenExpanded>false</ChildrenExpanded>\n"
929 : " <WorkspaceType>esriLocalDatabaseWorkspace</WorkspaceType>\n"
930 : " <WorkspaceFactoryProgID/>\n"
931 : " <ConnectionString/>\n"
932 : " <ConnectionInfo xsi:nil=\"true\"/>\n"
933 : " <Domains xsi:type=\"typens:ArrayOfDomain\"/>\n"
934 : " <MajorVersion>3</MajorVersion>\n"
935 : " <MinorVersion>0</MinorVersion>\n"
936 : " <BugfixVersion>0</BugfixVersion>\n"
937 : "</DEWorkspace>");
938 228 : fields[14].Integer = 0;
939 :
940 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
941 228 : this, m_osGDBItemsFilename.c_str(), "GDB_Items", "", "", true));
942 :
943 228 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
944 : }
945 :
946 : /***********************************************************************/
947 : /* CreateGDBItemTypes() */
948 : /***********************************************************************/
949 :
950 228 : bool OGROpenFileGDBDataSource::CreateGDBItemTypes()
951 : {
952 : // Write GDB_ItemTypes file
953 : const std::string osFilename(
954 456 : CPLFormFilename(m_osDirName.c_str(), "a00000005.gdbtable", nullptr));
955 456 : FileGDBTable oTable;
956 228 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
957 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
958 228 : "ObjectID", std::string(), FGFT_OBJECTID,
959 0 : /* bNullable = */ false,
960 0 : /* bRequired = */ true,
961 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
962 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
963 228 : "UUID", std::string(), FGFT_GUID,
964 0 : /* bNullable = */ false,
965 0 : /* bRequired = */ false,
966 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
967 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
968 228 : "ParentTypeID", std::string(), FGFT_GUID,
969 0 : /* bNullable = */ false,
970 0 : /* bRequired = */ false,
971 1140 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
972 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
973 228 : "Name", std::string(), FGFT_STRING,
974 0 : /* bNullable = */ false,
975 0 : /* bRequired = */ false,
976 456 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)))
977 : {
978 0 : return false;
979 : }
980 :
981 228 : std::vector<OGRField> fields(oTable.GetFieldCount(),
982 684 : FileGDBField::UNSET_FIELD);
983 :
984 : static const struct
985 : {
986 : const char *pszUUID;
987 : const char *pszParentTypeID;
988 : const char *pszName;
989 : } apsData[] = {
990 : {"{8405add5-8df8-4227-8fac-3fcade073386}",
991 : "{00000000-0000-0000-0000-000000000000}", "Item"},
992 : {pszFolderTypeUUID, "{8405add5-8df8-4227-8fac-3fcade073386}", "Folder"},
993 : {"{ffd09c28-fe70-4e25-907c-af8e8a5ec5f3}",
994 : "{8405add5-8df8-4227-8fac-3fcade073386}", "Resource"},
995 : {"{28da9e89-ff80-4d6d-8926-4ee2b161677d}",
996 : "{ffd09c28-fe70-4e25-907c-af8e8a5ec5f3}", "Dataset"},
997 : {"{fbdd7dd6-4a25-40b7-9a1a-ecc3d1172447}",
998 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Tin"},
999 : {"{d4912162-3413-476e-9da4-2aefbbc16939}",
1000 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "AbstractTable"},
1001 : {"{b606a7e1-fa5b-439c-849c-6e9c2481537b}",
1002 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Relationship Class"},
1003 : {pszFeatureDatasetTypeUUID, "{28da9e89-ff80-4d6d-8926-4ee2b161677d}",
1004 : "Feature Dataset"},
1005 : {"{73718a66-afb9-4b88-a551-cffa0ae12620}",
1006 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Geometric Network"},
1007 : {"{767152d3-ed66-4325-8774-420d46674e07}",
1008 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Topology"},
1009 : {"{e6302665-416b-44fa-be33-4e15916ba101}",
1010 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Survey Dataset"},
1011 : {"{d5a40288-029e-4766-8c81-de3f61129371}",
1012 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Schematic Dataset"},
1013 : {"{db1b697a-3bb6-426a-98a2-6ee7a4c6aed3}",
1014 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Toolbox"},
1015 : {pszWorkspaceTypeUUID, "{28da9e89-ff80-4d6d-8926-4ee2b161677d}",
1016 : "Workspace"},
1017 : {"{dc9ef677-1aa3-45a7-8acd-303a5202d0dc}",
1018 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Workspace Extension"},
1019 : {"{77292603-930f-475d-ae4f-b8970f42f394}",
1020 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Extension Dataset"},
1021 : {"{8637f1ed-8c04-4866-a44a-1cb8288b3c63}",
1022 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Domain"},
1023 : {"{4ed4a58e-621f-4043-95ed-850fba45fcbc}",
1024 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Replica"},
1025 : {"{d98421eb-d582-4713-9484-43304d0810f6}",
1026 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Replica Dataset"},
1027 : {"{dc64b6e4-dc0f-43bd-b4f5-f22385dcf055}",
1028 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "Historical Marker"},
1029 : {pszTableTypeUUID, "{d4912162-3413-476e-9da4-2aefbbc16939}", "Table"},
1030 : {pszFeatureClassTypeUUID, "{d4912162-3413-476e-9da4-2aefbbc16939}",
1031 : "Feature Class"},
1032 : {"{5ed667a3-9ca9-44a2-8029-d95bf23704b9}",
1033 : "{d4912162-3413-476e-9da4-2aefbbc16939}", "Raster Dataset"},
1034 : {"{35b601f7-45ce-4aff-adb7-7702d3839b12}",
1035 : "{d4912162-3413-476e-9da4-2aefbbc16939}", "Raster Catalog"},
1036 : {"{7771fc7d-a38b-4fd3-8225-639d17e9a131}",
1037 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Network Dataset"},
1038 : {"{76357537-3364-48af-a4be-783c7c28b5cb}",
1039 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Terrain"},
1040 : {"{a3803369-5fc2-4963-bae0-13effc09dd73}",
1041 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Parcel Fabric"},
1042 : {"{a300008d-0cea-4f6a-9dfa-46af829a3df2}",
1043 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Representation Class"},
1044 : {"{787bea35-4a86-494f-bb48-500b96145b58}",
1045 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Catalog Dataset"},
1046 : {"{f8413dcb-2248-4935-bfe9-315f397e5110}",
1047 : "{77292603-930f-475d-ae4f-b8970f42f394}", "Mosaic Dataset"},
1048 : {pszRangeDomainTypeUUID, "{8637f1ed-8c04-4866-a44a-1cb8288b3c63}",
1049 : "Range Domain"},
1050 : {pszCodedDomainTypeUUID, "{8637f1ed-8c04-4866-a44a-1cb8288b3c63}",
1051 : "Coded Value Domain"}};
1052 :
1053 7524 : for (const auto &record : apsData)
1054 : {
1055 7296 : fields[1].String = const_cast<char *>(record.pszUUID);
1056 7296 : fields[2].String = const_cast<char *>(record.pszParentTypeID);
1057 7296 : fields[3].String = const_cast<char *>(record.pszName);
1058 7296 : if (!oTable.CreateFeature(fields, nullptr))
1059 0 : return false;
1060 : }
1061 :
1062 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1063 228 : this, osFilename.c_str(), "GDB_ItemTypes", "", "", true));
1064 :
1065 228 : return oTable.Sync();
1066 : }
1067 :
1068 : /***********************************************************************/
1069 : /* CreateGDBItemRelationships() */
1070 : /***********************************************************************/
1071 :
1072 228 : bool OGROpenFileGDBDataSource::CreateGDBItemRelationships()
1073 : {
1074 : // Write GDB_ItemRelationships file
1075 : m_osGDBItemRelationshipsFilename =
1076 228 : CPLFormFilename(m_osDirName.c_str(), "a00000006.gdbtable", nullptr);
1077 456 : FileGDBTable oTable;
1078 228 : if (!oTable.Create(m_osGDBItemRelationshipsFilename.c_str(), 4, FGTGT_NONE,
1079 456 : false, false) ||
1080 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1081 228 : "ObjectID", std::string(), FGFT_OBJECTID,
1082 0 : /* bNullable = */ false,
1083 0 : /* bRequired = */ true,
1084 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1085 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1086 228 : "UUID", std::string(), FGFT_GLOBALID,
1087 0 : /* bNullable = */ false,
1088 0 : /* bRequired = */ true,
1089 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1090 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1091 228 : "OriginID", std::string(), FGFT_GUID,
1092 0 : /* bNullable = */ false,
1093 0 : /* bRequired = */ false,
1094 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1095 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1096 228 : "DestID", std::string(), FGFT_GUID,
1097 0 : /* bNullable = */ false,
1098 0 : /* bRequired = */ false,
1099 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1100 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1101 228 : "Type", std::string(), FGFT_GUID,
1102 0 : /* bNullable = */ false,
1103 0 : /* bRequired = */ false,
1104 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1105 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1106 228 : "Attributes", std::string(), FGFT_XML,
1107 0 : /* bNullable = */ true,
1108 0 : /* bRequired = */ false,
1109 1140 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1110 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1111 228 : "Properties", std::string(), FGFT_INT32,
1112 0 : /* bNullable = */ true,
1113 0 : /* bRequired = */ false,
1114 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
1115 : {
1116 0 : return false;
1117 : }
1118 :
1119 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1120 228 : this, m_osGDBItemRelationshipsFilename.c_str(), "GDB_ItemRelationships",
1121 456 : "", "", true));
1122 :
1123 228 : return oTable.Sync();
1124 : }
1125 :
1126 : /***********************************************************************/
1127 : /* CreateGDBItemRelationshipTypes() */
1128 : /***********************************************************************/
1129 :
1130 228 : bool OGROpenFileGDBDataSource::CreateGDBItemRelationshipTypes()
1131 : {
1132 : // Write GDB_ItemRelationshipTypes file
1133 : const std::string osFilename(
1134 456 : CPLFormFilename(m_osDirName.c_str(), "a00000007.gdbtable", nullptr));
1135 456 : FileGDBTable oTable;
1136 228 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
1137 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1138 228 : "ObjectID", std::string(), FGFT_OBJECTID,
1139 0 : /* bNullable = */ false,
1140 0 : /* bRequired = */ true,
1141 456 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1142 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1143 228 : "UUID", std::string(), FGFT_GUID,
1144 0 : /* bNullable = */ false,
1145 0 : /* bRequired = */ false,
1146 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1147 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1148 228 : "OrigItemTypeID", std::string(), FGFT_GUID,
1149 0 : /* bNullable = */ false,
1150 0 : /* bRequired = */ false,
1151 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1152 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1153 228 : "DestItemTypeID", std::string(), FGFT_GUID,
1154 0 : /* bNullable = */ false,
1155 0 : /* bRequired = */ false,
1156 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1157 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1158 228 : "Name", std::string(), FGFT_STRING,
1159 0 : /* bNullable = */ true,
1160 0 : /* bRequired = */ false,
1161 456 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
1162 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1163 228 : "ForwardLabel", std::string(), FGFT_STRING,
1164 0 : /* bNullable = */ true,
1165 0 : /* bRequired = */ false,
1166 456 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
1167 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1168 228 : "BackwardLabel", std::string(), FGFT_STRING,
1169 0 : /* bNullable = */ true,
1170 0 : /* bRequired = */ false,
1171 1140 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
1172 456 : !oTable.CreateField(std::make_unique<FileGDBField>(
1173 228 : "IsContainment", std::string(), FGFT_INT16,
1174 0 : /* bNullable = */ true,
1175 0 : /* bRequired = */ false,
1176 456 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
1177 : {
1178 0 : return false;
1179 : }
1180 :
1181 : static const struct
1182 : {
1183 : const char *pszUUID;
1184 : const char *pszOrigItemTypeID;
1185 : const char *pszDestItemTypeID;
1186 : const char *pszName;
1187 : const char *pszForwardLabel;
1188 : const char *pszBackwardLabel;
1189 : int IsContainment;
1190 : } apsData[] = {
1191 : {"{0d10b3a7-2f64-45e6-b7ac-2fc27bf2133c}", pszFolderTypeUUID,
1192 : pszFolderTypeUUID, "FolderInFolder", "Parent Folder Of",
1193 : "Child Folder Of", 1},
1194 : {"{5dd0c1af-cb3d-4fea-8c51-cb3ba8d77cdb}", pszFolderTypeUUID,
1195 : "{8405add5-8df8-4227-8fac-3fcade073386}", "ItemInFolder",
1196 : "Contains Item", "Contained In Folder", 1},
1197 : {pszDatasetInFeatureDatasetUUID, pszFeatureDatasetTypeUUID,
1198 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "DatasetInFeatureDataset",
1199 : "Contains Dataset", "Contained In FeatureDataset", 1},
1200 : {pszDatasetInFolderUUID, pszFolderTypeUUID,
1201 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "DatasetInFolder",
1202 : "Contains Dataset", "Contained in Dataset", 1},
1203 : {pszDomainInDatasetUUID, "{28da9e89-ff80-4d6d-8926-4ee2b161677d}",
1204 : "{8637f1ed-8c04-4866-a44a-1cb8288b3c63}", "DomainInDataset",
1205 : "Contains Domain", "Contained in Dataset", 0},
1206 : {"{725badab-3452-491b-a795-55f32d67229c}",
1207 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}",
1208 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "DatasetsRelatedThrough",
1209 : "Origin Of", "Destination Of", 0},
1210 : {"{d088b110-190b-4229-bdf7-89fddd14d1ea}",
1211 : "{767152d3-ed66-4325-8774-420d46674e07}", pszFeatureClassTypeUUID,
1212 : "FeatureClassInTopology", "Spatially Manages Feature Class",
1213 : "Participates In Topology", 0},
1214 : {"{dc739a70-9b71-41e8-868c-008cf46f16d7}",
1215 : "{73718a66-afb9-4b88-a551-cffa0ae12620}", pszFeatureClassTypeUUID,
1216 : "FeatureClassInGeometricNetwork", "Spatially Manages Feature Class",
1217 : "Participates In Geometric Network", 0},
1218 : {"{b32b8563-0b96-4d32-92c4-086423ae9962}",
1219 : "{7771fc7d-a38b-4fd3-8225-639d17e9a131}", pszFeatureClassTypeUUID,
1220 : "FeatureClassInNetworkDataset", "Spatially Manages Feature Class",
1221 : "Participates In Network Dataset", 0},
1222 : {"{908a4670-1111-48c6-8269-134fdd3fe617}",
1223 : "{7771fc7d-a38b-4fd3-8225-639d17e9a131}", pszTableTypeUUID,
1224 : "TableInNetworkDataset", "Manages Table",
1225 : "Participates In Network Dataset", 0},
1226 : {"{55d2f4dc-cb17-4e32-a8c7-47591e8c71de}",
1227 : "{76357537-3364-48af-a4be-783c7c28b5cb}", pszFeatureClassTypeUUID,
1228 : "FeatureClassInTerrain", "Spatially Manages Feature Class",
1229 : "Participates In Terrain", 0},
1230 : {"{583a5baa-3551-41ae-8aa8-1185719f3889}",
1231 : "{a3803369-5fc2-4963-bae0-13effc09dd73}", pszFeatureClassTypeUUID,
1232 : "FeatureClassInParcelFabric", "Spatially Manages Feature Class",
1233 : "Participates In Parcel Fabric", 0},
1234 : {"{5f9085e0-788f-4354-ae3c-34c83a7ea784}",
1235 : "{a3803369-5fc2-4963-bae0-13effc09dd73}", pszTableTypeUUID,
1236 : "TableInParcelFabric", "Manages Table",
1237 : "Participates In Parcel Fabric", 0},
1238 : {"{e79b44e3-f833-4b12-90a1-364ec4ddc43e}", pszFeatureClassTypeUUID,
1239 : "{a300008d-0cea-4f6a-9dfa-46af829a3df2}",
1240 : "RepresentationOfFeatureClass", "Feature Class Representation",
1241 : "Represented Feature Class", 0},
1242 : {"{8db31af1-df7c-4632-aa10-3cc44b0c6914}",
1243 : "{4ed4a58e-621f-4043-95ed-850fba45fcbc}",
1244 : "{d98421eb-d582-4713-9484-43304d0810f6}", "ReplicaDatasetInReplica",
1245 : "Replicated Dataset", "Participates In Replica", 1},
1246 : {"{d022de33-45bd-424c-88bf-5b1b6b957bd3}",
1247 : "{d98421eb-d582-4713-9484-43304d0810f6}",
1248 : "{28da9e89-ff80-4d6d-8926-4ee2b161677d}", "DatasetOfReplicaDataset",
1249 : "Replicated Dataset", "Dataset of Replicated Dataset", 0},
1250 : };
1251 :
1252 228 : std::vector<OGRField> fields(oTable.GetFieldCount(),
1253 684 : FileGDBField::UNSET_FIELD);
1254 3876 : for (const auto &record : apsData)
1255 : {
1256 3648 : fields[1].String = const_cast<char *>(record.pszUUID);
1257 3648 : fields[2].String = const_cast<char *>(record.pszOrigItemTypeID);
1258 3648 : fields[3].String = const_cast<char *>(record.pszDestItemTypeID);
1259 3648 : fields[4].String = const_cast<char *>(record.pszName);
1260 3648 : fields[5].String = const_cast<char *>(record.pszForwardLabel);
1261 3648 : fields[6].String = const_cast<char *>(record.pszBackwardLabel);
1262 3648 : fields[7].Integer = record.IsContainment;
1263 3648 : if (!oTable.CreateFeature(fields, nullptr))
1264 0 : return false;
1265 : }
1266 :
1267 228 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1268 228 : this, osFilename.c_str(), "GDB_ItemRelationshipTypes", "", "", true));
1269 :
1270 228 : return oTable.Sync();
1271 : }
1272 :
1273 : /***********************************************************************/
1274 : /* Create() */
1275 : /***********************************************************************/
1276 :
1277 231 : bool OGROpenFileGDBDataSource::Create(const char *pszName)
1278 : {
1279 :
1280 231 : if (!EQUAL(CPLGetExtension(pszName), "gdb"))
1281 : {
1282 1 : CPLError(CE_Failure, CPLE_NotSupported,
1283 : "Extension of the directory should be gdb");
1284 1 : return false;
1285 : }
1286 :
1287 : /* Don't try to create on top of something already there */
1288 : VSIStatBufL sStat;
1289 230 : if (VSIStatL(pszName, &sStat) == 0)
1290 : {
1291 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already exists.", pszName);
1292 0 : return false;
1293 : }
1294 :
1295 230 : if (VSIMkdir(pszName, 0755) != 0)
1296 : {
1297 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create directory %s.",
1298 : pszName);
1299 2 : return false;
1300 : }
1301 :
1302 228 : CPL_IGNORE_RET_VAL(OFGDBGenerateUUID(/* bInit = */ true));
1303 :
1304 228 : m_osDirName = pszName;
1305 228 : eAccess = GA_Update;
1306 :
1307 : {
1308 : // Write "gdb" file
1309 228 : const std::string osFilename(CPLFormFilename(pszName, "gdb", nullptr));
1310 228 : VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
1311 228 : if (!fp)
1312 0 : return false;
1313 : // Write what the FileGDB SDK writes...
1314 228 : VSIFWriteL("\x05\x00\x00\x00\xDE\xAD\xBE\xEF", 1, 8, fp);
1315 228 : VSIFCloseL(fp);
1316 : }
1317 :
1318 : {
1319 : // Write "timestamps" file
1320 : const std::string osFilename(
1321 228 : CPLFormFilename(pszName, "timestamps", nullptr));
1322 228 : VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
1323 228 : if (!fp)
1324 0 : return false;
1325 : // Write what the FileGDB SDK writes...
1326 456 : std::vector<GByte> values(400, 0xFF);
1327 228 : VSIFWriteL(values.data(), 1, values.size(), fp);
1328 228 : VSIFCloseL(fp);
1329 : }
1330 :
1331 456 : return CreateGDBSystemCatalog() && CreateGDBDBTune() &&
1332 228 : CreateGDBSpatialRefs() && CreateGDBItems() && CreateGDBItemTypes() &&
1333 456 : CreateGDBItemRelationships() && CreateGDBItemRelationshipTypes();
1334 : // GDB_ReplicaLog can be omitted.
1335 : }
1336 :
1337 : /************************************************************************/
1338 : /* ICreateLayer() */
1339 : /************************************************************************/
1340 :
1341 : OGRLayer *
1342 270 : OGROpenFileGDBDataSource::ICreateLayer(const char *pszLayerName,
1343 : const OGRGeomFieldDefn *poGeomFieldDefn,
1344 : CSLConstList papszOptions)
1345 : {
1346 270 : if (eAccess != GA_Update)
1347 0 : return nullptr;
1348 :
1349 270 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1350 0 : return nullptr;
1351 :
1352 270 : if (m_osRootGUID.empty())
1353 : {
1354 0 : CPLError(CE_Failure, CPLE_AppDefined, "Root UUID missing");
1355 0 : return nullptr;
1356 : }
1357 :
1358 270 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1359 :
1360 540 : FileGDBTable oTable;
1361 540 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), false) ||
1362 270 : oTable.GetTotalRecordCount() >= INT32_MAX)
1363 0 : return nullptr;
1364 270 : const int nTableNum = static_cast<int>(1 + oTable.GetTotalRecordCount());
1365 270 : oTable.Close();
1366 :
1367 : const std::string osFilename(CPLFormFilename(
1368 540 : m_osDirName.c_str(), CPLSPrintf("a%08x.gdbtable", nTableNum), nullptr));
1369 :
1370 270 : if (wkbFlatten(eType) == wkbLineString)
1371 16 : eType = OGR_GT_SetModifier(wkbMultiLineString, OGR_GT_HasZ(eType),
1372 : OGR_GT_HasM(eType));
1373 254 : else if (wkbFlatten(eType) == wkbPolygon)
1374 17 : eType = OGR_GT_SetModifier(wkbMultiPolygon, OGR_GT_HasZ(eType),
1375 : OGR_GT_HasM(eType));
1376 :
1377 : auto poLayer = std::make_unique<OGROpenFileGDBLayer>(
1378 540 : this, osFilename.c_str(), pszLayerName, eType, papszOptions);
1379 270 : if (!poLayer->Create(poGeomFieldDefn))
1380 3 : return nullptr;
1381 267 : if (m_bInTransaction)
1382 : {
1383 4 : if (!poLayer->BeginEmulatedTransaction())
1384 0 : return nullptr;
1385 4 : m_oSetLayersCreatedInTransaction.insert(poLayer.get());
1386 : }
1387 267 : m_apoLayers.emplace_back(std::move(poLayer));
1388 :
1389 267 : return m_apoLayers.back().get();
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* DeleteLayer() */
1394 : /************************************************************************/
1395 :
1396 19 : OGRErr OGROpenFileGDBDataSource::DeleteLayer(int iLayer)
1397 : {
1398 19 : if (eAccess != GA_Update)
1399 0 : return OGRERR_FAILURE;
1400 :
1401 19 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1402 2 : return OGRERR_FAILURE;
1403 :
1404 17 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1405 0 : return false;
1406 :
1407 17 : auto poLayer = m_apoLayers[iLayer].get();
1408 :
1409 : // Remove from GDB_SystemCatalog
1410 : {
1411 17 : FileGDBTable oTable;
1412 17 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), true))
1413 0 : return OGRERR_FAILURE;
1414 :
1415 17 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
1416 :
1417 153 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1418 : ++iCurFeat)
1419 : {
1420 153 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1421 153 : if (iCurFeat < 0)
1422 0 : break;
1423 153 : const auto psName = oTable.GetFieldValue(iName);
1424 153 : if (psName && strcmp(psName->String, poLayer->GetName()) == 0)
1425 : {
1426 17 : oTable.DeleteFeature(iCurFeat + 1);
1427 17 : break;
1428 : }
1429 : }
1430 : }
1431 :
1432 : // Remove from GDB_Items
1433 34 : std::string osUUID;
1434 : {
1435 17 : FileGDBTable oTable;
1436 17 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1437 0 : return OGRERR_FAILURE;
1438 :
1439 17 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, OGRERR_FAILURE);
1440 17 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
1441 :
1442 51 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1443 : ++iCurFeat)
1444 : {
1445 37 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1446 37 : if (iCurFeat < 0)
1447 0 : break;
1448 37 : const auto psName = oTable.GetFieldValue(iName);
1449 37 : if (psName && strcmp(psName->String, poLayer->GetName()) == 0)
1450 : {
1451 3 : const auto psUUID = oTable.GetFieldValue(iUUID);
1452 3 : if (psUUID)
1453 : {
1454 3 : osUUID = psUUID->String;
1455 : }
1456 :
1457 3 : oTable.DeleteFeature(iCurFeat + 1);
1458 3 : break;
1459 : }
1460 : }
1461 : }
1462 :
1463 : // Remove from GDB_ItemRelationships
1464 17 : if (!osUUID.empty())
1465 : {
1466 3 : FileGDBTable oTable;
1467 3 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
1468 0 : return OGRERR_FAILURE;
1469 :
1470 3 : FETCH_FIELD_IDX_WITH_RET(iOriginID, "OriginID", FGFT_GUID,
1471 : OGRERR_FAILURE);
1472 3 : FETCH_FIELD_IDX_WITH_RET(iDestID, "DestID", FGFT_GUID, OGRERR_FAILURE);
1473 :
1474 7 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1475 : ++iCurFeat)
1476 : {
1477 4 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1478 4 : if (iCurFeat < 0)
1479 0 : break;
1480 :
1481 4 : const auto psOriginID = oTable.GetFieldValue(iOriginID);
1482 4 : if (psOriginID && psOriginID->String == osUUID)
1483 : {
1484 0 : oTable.DeleteFeature(iCurFeat + 1);
1485 : }
1486 : else
1487 : {
1488 4 : const auto psDestID = oTable.GetFieldValue(iDestID);
1489 4 : if (psDestID && psDestID->String == osUUID)
1490 : {
1491 3 : oTable.DeleteFeature(iCurFeat + 1);
1492 : }
1493 : }
1494 : }
1495 : }
1496 :
1497 34 : const std::string osDirname = CPLGetPath(poLayer->GetFilename().c_str());
1498 : const std::string osFilenameBase =
1499 17 : CPLGetBasename(poLayer->GetFilename().c_str());
1500 :
1501 17 : if (m_bInTransaction)
1502 : {
1503 : auto oIter =
1504 2 : m_oSetLayersCreatedInTransaction.find(m_apoLayers[iLayer].get());
1505 2 : if (oIter != m_oSetLayersCreatedInTransaction.end())
1506 : {
1507 1 : m_oSetLayersCreatedInTransaction.erase(oIter);
1508 : }
1509 : else
1510 : {
1511 1 : poLayer->BeginEmulatedTransaction();
1512 1 : poLayer->Close();
1513 : m_oSetLayersDeletedInTransaction.insert(
1514 1 : std::move(m_apoLayers[iLayer]));
1515 : }
1516 : }
1517 :
1518 : // Delete OGR layer
1519 17 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1520 :
1521 : // Remove files associated with the layer
1522 17 : char **papszFiles = VSIReadDir(osDirname.c_str());
1523 408 : for (char **papszIter = papszFiles; papszIter && *papszIter; ++papszIter)
1524 : {
1525 391 : if (STARTS_WITH(*papszIter, osFilenameBase.c_str()))
1526 : {
1527 59 : VSIUnlink(CPLFormFilename(osDirname.c_str(), *papszIter, nullptr));
1528 : }
1529 : }
1530 17 : CSLDestroy(papszFiles);
1531 :
1532 17 : return OGRERR_NONE;
1533 : }
1534 :
1535 : /************************************************************************/
1536 : /* FlushCache() */
1537 : /************************************************************************/
1538 :
1539 851 : CPLErr OGROpenFileGDBDataSource::FlushCache(bool /*bAtClosing*/)
1540 : {
1541 851 : if (eAccess != GA_Update)
1542 559 : return CE_None;
1543 :
1544 292 : CPLErr eErr = CE_None;
1545 688 : for (auto &poLayer : m_apoLayers)
1546 : {
1547 396 : if (poLayer->SyncToDisk() != OGRERR_NONE)
1548 0 : eErr = CE_Failure;
1549 : }
1550 292 : return eErr;
1551 : }
1552 :
1553 : /************************************************************************/
1554 : /* AddFieldDomain() */
1555 : /************************************************************************/
1556 :
1557 6 : bool OGROpenFileGDBDataSource::AddFieldDomain(
1558 : std::unique_ptr<OGRFieldDomain> &&domain, std::string &failureReason)
1559 : {
1560 12 : const std::string domainName(domain->GetName());
1561 6 : if (eAccess != GA_Update)
1562 : {
1563 0 : CPLError(CE_Failure, CPLE_NotSupported,
1564 : "AddFieldDomain() not supported on read-only dataset");
1565 0 : return false;
1566 : }
1567 :
1568 6 : if (GetFieldDomain(domainName) != nullptr)
1569 : {
1570 0 : failureReason = "A domain of identical name already exists";
1571 0 : return false;
1572 : }
1573 :
1574 6 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1575 0 : return false;
1576 :
1577 : std::string osXML =
1578 12 : BuildXMLFieldDomainDef(domain.get(), false, failureReason);
1579 6 : if (osXML.empty())
1580 : {
1581 0 : return false;
1582 : }
1583 :
1584 12 : const std::string osThisGUID = OFGDBGenerateUUID();
1585 :
1586 12 : FileGDBTable oTable;
1587 6 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1588 0 : return false;
1589 :
1590 6 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
1591 6 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1592 6 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1593 6 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
1594 6 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
1595 6 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
1596 6 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
1597 6 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
1598 :
1599 6 : std::vector<OGRField> fields(oTable.GetFieldCount(),
1600 18 : FileGDBField::UNSET_FIELD);
1601 6 : fields[iUUID].String = const_cast<char *>(osThisGUID.c_str());
1602 6 : switch (domain->GetDomainType())
1603 : {
1604 4 : case OFDT_CODED:
1605 4 : fields[iType].String = const_cast<char *>(pszCodedDomainTypeUUID);
1606 4 : break;
1607 :
1608 2 : case OFDT_RANGE:
1609 2 : fields[iType].String = const_cast<char *>(pszRangeDomainTypeUUID);
1610 2 : break;
1611 :
1612 0 : case OFDT_GLOB:
1613 0 : CPLAssert(false);
1614 : break;
1615 : }
1616 6 : fields[iName].String = const_cast<char *>(domainName.c_str());
1617 12 : CPLString osUCName(domainName);
1618 6 : osUCName.toupper();
1619 6 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
1620 6 : fields[iPath].String = const_cast<char *>("");
1621 6 : fields[iURL].String = const_cast<char *>("");
1622 6 : fields[iDefinition].String = const_cast<char *>(osXML.c_str());
1623 6 : fields[iProperties].Integer = 1;
1624 :
1625 6 : if (!(oTable.CreateFeature(fields, nullptr) && oTable.Sync()))
1626 0 : return false;
1627 :
1628 6 : m_oMapFieldDomains[domainName] = std::move(domain);
1629 :
1630 6 : return true;
1631 : }
1632 :
1633 : /************************************************************************/
1634 : /* DeleteFieldDomain() */
1635 : /************************************************************************/
1636 :
1637 2 : bool OGROpenFileGDBDataSource::DeleteFieldDomain(
1638 : const std::string &name, std::string & /*failureReason*/)
1639 : {
1640 2 : if (eAccess != GA_Update)
1641 : {
1642 0 : CPLError(CE_Failure, CPLE_NotSupported,
1643 : "DeleteFieldDomain() not supported on read-only dataset");
1644 0 : return false;
1645 : }
1646 :
1647 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1648 0 : return false;
1649 :
1650 : // Remove object from GDB_Items
1651 4 : std::string osUUID;
1652 : {
1653 2 : FileGDBTable oTable;
1654 2 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1655 0 : return false;
1656 :
1657 2 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
1658 2 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1659 2 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1660 :
1661 14 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1662 : ++iCurFeat)
1663 : {
1664 13 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1665 13 : if (iCurFeat < 0)
1666 0 : break;
1667 13 : const auto psName = oTable.GetFieldValue(iName);
1668 13 : if (psName && psName->String == name)
1669 : {
1670 1 : const auto psType = oTable.GetFieldValue(iType);
1671 1 : if (psType && (EQUAL(psType->String, pszRangeDomainTypeUUID) ||
1672 1 : EQUAL(psType->String, pszCodedDomainTypeUUID)))
1673 : {
1674 1 : const auto psUUID = oTable.GetFieldValue(iUUID);
1675 1 : if (psUUID)
1676 : {
1677 1 : osUUID = psUUID->String;
1678 : }
1679 :
1680 1 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
1681 : {
1682 0 : return false;
1683 : }
1684 1 : break;
1685 : }
1686 : }
1687 : }
1688 : }
1689 2 : if (osUUID.empty())
1690 1 : return false;
1691 :
1692 : // Remove links from layers to domain, into GDB_ItemRelationships
1693 : {
1694 1 : FileGDBTable oTable;
1695 1 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
1696 0 : return false;
1697 :
1698 1 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
1699 1 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1700 :
1701 5 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1702 : ++iCurFeat)
1703 : {
1704 4 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1705 4 : if (iCurFeat < 0)
1706 0 : break;
1707 :
1708 4 : const auto psType = oTable.GetFieldValue(iType);
1709 4 : if (psType && EQUAL(psType->String, pszDomainInDatasetUUID))
1710 : {
1711 3 : const auto psDestID = oTable.GetFieldValue(iDestID);
1712 3 : if (psDestID && EQUAL(psDestID->String, osUUID.c_str()))
1713 : {
1714 0 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
1715 : {
1716 0 : return false;
1717 : }
1718 : }
1719 : }
1720 : }
1721 :
1722 1 : if (!oTable.Sync())
1723 : {
1724 0 : return false;
1725 : }
1726 : }
1727 :
1728 1 : m_oMapFieldDomains.erase(name);
1729 :
1730 1 : return true;
1731 : }
1732 :
1733 : /************************************************************************/
1734 : /* UpdateFieldDomain() */
1735 : /************************************************************************/
1736 :
1737 1 : bool OGROpenFileGDBDataSource::UpdateFieldDomain(
1738 : std::unique_ptr<OGRFieldDomain> &&domain, std::string &failureReason)
1739 : {
1740 2 : const std::string domainName(domain->GetName());
1741 1 : if (eAccess != GA_Update)
1742 : {
1743 0 : CPLError(CE_Failure, CPLE_NotSupported,
1744 : "UpdateFieldDomain() not supported on read-only dataset");
1745 0 : return false;
1746 : }
1747 :
1748 1 : if (GetFieldDomain(domainName) == nullptr)
1749 : {
1750 0 : failureReason = "The domain should already exist to be updated";
1751 0 : return false;
1752 : }
1753 :
1754 1 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1755 0 : return false;
1756 :
1757 : std::string osXML =
1758 2 : BuildXMLFieldDomainDef(domain.get(), false, failureReason);
1759 1 : if (osXML.empty())
1760 : {
1761 0 : return false;
1762 : }
1763 :
1764 2 : FileGDBTable oTable;
1765 1 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1766 0 : return false;
1767 :
1768 1 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1769 1 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1770 1 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
1771 :
1772 1 : bool bMatchFound = false;
1773 3 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1774 : ++iCurFeat)
1775 : {
1776 3 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1777 3 : if (iCurFeat < 0)
1778 0 : break;
1779 3 : const auto psName = oTable.GetFieldValue(iName);
1780 3 : if (psName && psName->String == domainName)
1781 : {
1782 1 : const auto psType = oTable.GetFieldValue(iType);
1783 1 : if (psType && (EQUAL(psType->String, pszRangeDomainTypeUUID) ||
1784 0 : EQUAL(psType->String, pszCodedDomainTypeUUID)))
1785 : {
1786 1 : auto asFields = oTable.GetAllFieldValues();
1787 :
1788 2 : if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
1789 1 : !OGR_RawField_IsUnset(&asFields[iDefinition]))
1790 : {
1791 1 : CPLFree(asFields[iDefinition].String);
1792 : }
1793 1 : asFields[iDefinition].String = CPLStrdup(osXML.c_str());
1794 :
1795 1 : const char *pszNewTypeUUID = "";
1796 1 : switch (domain->GetDomainType())
1797 : {
1798 0 : case OFDT_CODED:
1799 0 : pszNewTypeUUID = pszCodedDomainTypeUUID;
1800 0 : break;
1801 :
1802 1 : case OFDT_RANGE:
1803 1 : pszNewTypeUUID = pszRangeDomainTypeUUID;
1804 1 : break;
1805 :
1806 0 : case OFDT_GLOB:
1807 0 : CPLAssert(false);
1808 : break;
1809 : }
1810 :
1811 2 : if (!OGR_RawField_IsNull(&asFields[iType]) &&
1812 1 : !OGR_RawField_IsUnset(&asFields[iType]))
1813 : {
1814 1 : CPLFree(asFields[iType].String);
1815 : }
1816 1 : asFields[iType].String = CPLStrdup(pszNewTypeUUID);
1817 :
1818 : bool bRet =
1819 1 : oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr);
1820 1 : oTable.FreeAllFieldValues(asFields);
1821 1 : if (!bRet)
1822 0 : return false;
1823 1 : bMatchFound = true;
1824 1 : break;
1825 : }
1826 : }
1827 :
1828 2 : if (!oTable.Sync())
1829 : {
1830 0 : return false;
1831 : }
1832 : }
1833 :
1834 1 : if (!bMatchFound)
1835 0 : return false;
1836 :
1837 1 : m_oMapFieldDomains[domainName] = std::move(domain);
1838 :
1839 1 : return true;
1840 : }
1841 :
1842 : /************************************************************************/
1843 : /* GetRelationshipNames() */
1844 : /************************************************************************/
1845 :
1846 17 : std::vector<std::string> OGROpenFileGDBDataSource::GetRelationshipNames(
1847 : CPL_UNUSED CSLConstList papszOptions) const
1848 :
1849 : {
1850 17 : std::vector<std::string> oasNames;
1851 17 : oasNames.reserve(m_osMapRelationships.size());
1852 77 : for (auto it = m_osMapRelationships.begin();
1853 137 : it != m_osMapRelationships.end(); ++it)
1854 : {
1855 60 : oasNames.emplace_back(it->first);
1856 : }
1857 17 : return oasNames;
1858 : }
1859 :
1860 : /************************************************************************/
1861 : /* GetRelationship() */
1862 : /************************************************************************/
1863 :
1864 : const GDALRelationship *
1865 46 : OGROpenFileGDBDataSource::GetRelationship(const std::string &name) const
1866 :
1867 : {
1868 46 : auto it = m_osMapRelationships.find(name);
1869 46 : if (it == m_osMapRelationships.end())
1870 8 : return nullptr;
1871 :
1872 38 : return it->second.get();
1873 : }
1874 :
1875 : /************************************************************************/
1876 : /* AddRelationship() */
1877 : /************************************************************************/
1878 :
1879 7 : bool OGROpenFileGDBDataSource::AddRelationship(
1880 : std::unique_ptr<GDALRelationship> &&relationship,
1881 : std::string &failureReason)
1882 : {
1883 14 : const std::string relationshipName(relationship->GetName());
1884 7 : if (eAccess != GA_Update)
1885 : {
1886 0 : CPLError(CE_Failure, CPLE_NotSupported,
1887 : "AddRelationship() not supported on read-only dataset");
1888 0 : return false;
1889 : }
1890 :
1891 7 : if (GetRelationship(relationshipName) != nullptr)
1892 : {
1893 1 : failureReason = "A relationship of identical name already exists";
1894 1 : return false;
1895 : }
1896 :
1897 6 : if (relationship->GetCardinality() ==
1898 : GDALRelationshipCardinality::GRC_MANY_TO_ONE)
1899 : {
1900 0 : failureReason = "Many to one relationships are not supported";
1901 0 : return false;
1902 : }
1903 6 : else if (relationship->GetCardinality() ==
1904 3 : GDALRelationshipCardinality::GRC_MANY_TO_MANY &&
1905 8 : !relationship->GetMappingTableName().empty() &&
1906 2 : relationship->GetName() != relationship->GetMappingTableName())
1907 : {
1908 : failureReason = "Mapping table name must match relationship name for "
1909 1 : "many-to-many relationships";
1910 1 : return false;
1911 : }
1912 :
1913 5 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1914 0 : return false;
1915 :
1916 10 : const std::string osThisGUID = OFGDBGenerateUUID();
1917 :
1918 10 : FileGDBTable oTable;
1919 10 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) ||
1920 5 : oTable.GetTotalRecordCount() >= INT32_MAX)
1921 0 : return false;
1922 :
1923 : // hopefully this just needs to be a unique value. Seems to autoincrement
1924 : // when created from ArcMap at least!
1925 5 : const int iDsId = static_cast<int>(oTable.GetTotalRecordCount() + 1);
1926 :
1927 10 : std::string osMappingTableOidName;
1928 5 : if (relationship->GetCardinality() ==
1929 : GDALRelationshipCardinality::GRC_MANY_TO_MANY)
1930 : {
1931 2 : if (!relationship->GetMappingTableName().empty())
1932 : {
1933 : auto poLayer =
1934 1 : GetLayerByName(relationship->GetMappingTableName().c_str());
1935 1 : if (poLayer)
1936 : {
1937 1 : osMappingTableOidName = poLayer->GetFIDColumn();
1938 : }
1939 : }
1940 : else
1941 : {
1942 : // auto create mapping table
1943 1 : CPLStringList aosOptions;
1944 1 : aosOptions.SetNameValue("FID", "RID");
1945 1 : OGRLayer *poMappingTable = ICreateLayer(
1946 1 : relationship->GetName().c_str(), nullptr, aosOptions.List());
1947 1 : if (!poMappingTable)
1948 : {
1949 0 : failureReason = "Could not create mapping table " +
1950 0 : relationship->GetMappingTableName();
1951 0 : return false;
1952 : }
1953 :
1954 1 : OGRFieldDefn oOriginFkFieldDefn("origin_fk", OFTString);
1955 1 : if (poMappingTable->CreateField(&oOriginFkFieldDefn) != OGRERR_NONE)
1956 : {
1957 : failureReason =
1958 0 : "Could not create origin_fk field in mapping table " +
1959 0 : relationship->GetMappingTableName();
1960 0 : return false;
1961 : }
1962 :
1963 1 : OGRFieldDefn oDestinationFkFieldDefn("destination_fk", OFTString);
1964 1 : if (poMappingTable->CreateField(&oDestinationFkFieldDefn) !=
1965 : OGRERR_NONE)
1966 : {
1967 : failureReason =
1968 0 : "Could not create destination_fk field in mapping table " +
1969 0 : relationship->GetMappingTableName();
1970 0 : return false;
1971 : }
1972 :
1973 1 : osMappingTableOidName = "RID";
1974 1 : relationship->SetMappingTableName(relationship->GetName());
1975 2 : relationship->SetLeftMappingTableFields({"origin_fk"});
1976 2 : relationship->SetRightMappingTableFields({"destination_fk"});
1977 : }
1978 : }
1979 :
1980 : std::string osXML = BuildXMLRelationshipDef(
1981 10 : relationship.get(), iDsId, osMappingTableOidName, failureReason);
1982 5 : if (osXML.empty())
1983 : {
1984 0 : return false;
1985 : }
1986 :
1987 : std::string osItemInfoXML =
1988 10 : BuildXMLRelationshipItemInfo(relationship.get(), failureReason);
1989 5 : if (osItemInfoXML.empty())
1990 : {
1991 0 : return false;
1992 : }
1993 :
1994 : std::string osDocumentationXML =
1995 10 : BuildXMLRelationshipDocumentation(relationship.get(), failureReason);
1996 5 : if (osDocumentationXML.empty())
1997 : {
1998 0 : return false;
1999 : }
2000 :
2001 10 : std::string osOriginUUID;
2002 5 : if (!FindUUIDFromName(relationship->GetLeftTableName(), osOriginUUID))
2003 : {
2004 2 : failureReason = ("Left table " + relationship->GetLeftTableName() +
2005 : " is not an existing layer in the dataset")
2006 1 : .c_str();
2007 1 : return false;
2008 : }
2009 8 : std::string osDestinationUUID;
2010 4 : if (!FindUUIDFromName(relationship->GetRightTableName(), osDestinationUUID))
2011 : {
2012 0 : failureReason = ("Right table " + relationship->GetRightTableName() +
2013 : " is not an existing layer in the dataset")
2014 0 : .c_str();
2015 0 : return false;
2016 : }
2017 :
2018 4 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
2019 4 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
2020 4 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
2021 4 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
2022 4 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
2023 4 : FETCH_FIELD_IDX(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32);
2024 4 : FETCH_FIELD_IDX(iDatasetSubtype2, "DatasetSubtype2", FGFT_INT32);
2025 4 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
2026 4 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
2027 4 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
2028 4 : FETCH_FIELD_IDX(iItemInfo, "ItemInfo", FGFT_XML);
2029 4 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
2030 :
2031 4 : std::vector<OGRField> fields(oTable.GetFieldCount(),
2032 12 : FileGDBField::UNSET_FIELD);
2033 4 : fields[iUUID].String = const_cast<char *>(osThisGUID.c_str());
2034 4 : fields[iType].String = const_cast<char *>(pszRelationshipTypeUUID);
2035 4 : fields[iName].String = const_cast<char *>(relationshipName.c_str());
2036 8 : CPLString osUCName(relationshipName);
2037 4 : osUCName.toupper();
2038 4 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
2039 8 : const std::string osPath = "\\" + relationshipName;
2040 4 : fields[iPath].String = const_cast<char *>(osPath.c_str());
2041 4 : switch (relationship->GetCardinality())
2042 : {
2043 1 : case GDALRelationshipCardinality::GRC_ONE_TO_ONE:
2044 1 : fields[iDatasetSubtype1].Integer = 1;
2045 1 : break;
2046 1 : case GDALRelationshipCardinality::GRC_ONE_TO_MANY:
2047 1 : fields[iDatasetSubtype1].Integer = 2;
2048 1 : break;
2049 2 : case GDALRelationshipCardinality::GRC_MANY_TO_MANY:
2050 2 : fields[iDatasetSubtype1].Integer = 3;
2051 2 : break;
2052 0 : case GDALRelationshipCardinality::GRC_MANY_TO_ONE:
2053 : // unreachable
2054 0 : break;
2055 : }
2056 4 : fields[iDatasetSubtype2].Integer = 0;
2057 4 : fields[iURL].String = const_cast<char *>("");
2058 4 : fields[iDefinition].String = const_cast<char *>(osXML.c_str());
2059 4 : fields[iDocumentation].String =
2060 4 : const_cast<char *>(osDocumentationXML.c_str());
2061 4 : fields[iItemInfo].String = const_cast<char *>(osItemInfoXML.c_str());
2062 4 : fields[iProperties].Integer = 1;
2063 :
2064 4 : if (!(oTable.CreateFeature(fields, nullptr) && oTable.Sync()))
2065 0 : return false;
2066 :
2067 4 : if (!RegisterRelationshipInItemRelationships(osThisGUID, osOriginUUID,
2068 : osDestinationUUID))
2069 0 : return false;
2070 :
2071 4 : m_osMapRelationships[relationshipName] = std::move(relationship);
2072 :
2073 4 : return true;
2074 : }
2075 :
2076 : /************************************************************************/
2077 : /* DeleteRelationship() */
2078 : /************************************************************************/
2079 :
2080 2 : bool OGROpenFileGDBDataSource::DeleteRelationship(const std::string &name,
2081 : std::string &failureReason)
2082 : {
2083 2 : if (eAccess != GA_Update)
2084 : {
2085 0 : CPLError(CE_Failure, CPLE_NotSupported,
2086 : "DeleteRelationship() not supported on read-only dataset");
2087 0 : return false;
2088 : }
2089 :
2090 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
2091 0 : return false;
2092 :
2093 : // Remove from GDB_Items
2094 4 : std::string osUUID;
2095 : {
2096 2 : FileGDBTable oTable;
2097 2 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
2098 0 : return false;
2099 :
2100 2 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, false);
2101 2 : FETCH_FIELD_IDX_WITH_RET(iType, "Type", FGFT_GUID, false);
2102 2 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, false);
2103 :
2104 36 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
2105 : ++iCurFeat)
2106 : {
2107 34 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
2108 34 : if (iCurFeat < 0)
2109 0 : break;
2110 :
2111 34 : const auto psType = oTable.GetFieldValue(iType);
2112 34 : if (!psType || !EQUAL(psType->String, pszRelationshipTypeUUID))
2113 : {
2114 26 : continue;
2115 : }
2116 :
2117 8 : const auto psName = oTable.GetFieldValue(iName);
2118 8 : if (psName && strcmp(psName->String, name.c_str()) != 0)
2119 : {
2120 7 : continue;
2121 : }
2122 :
2123 1 : const auto psUUID = oTable.GetFieldValue(iUUID);
2124 1 : if (psUUID)
2125 : {
2126 1 : osUUID = psUUID->String;
2127 1 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
2128 : {
2129 : failureReason =
2130 0 : "Could not delete relationship from GDB_Items table";
2131 0 : return false;
2132 : }
2133 : }
2134 : }
2135 : }
2136 :
2137 2 : if (osUUID.empty())
2138 : {
2139 1 : failureReason = "Could not find relationship with name " + name;
2140 1 : return false;
2141 : }
2142 :
2143 1 : if (!RemoveRelationshipFromItemRelationships(osUUID))
2144 : {
2145 : failureReason =
2146 0 : "Could not remove relationship from GDB_ItemRelationships";
2147 0 : return false;
2148 : }
2149 :
2150 1 : m_osMapRelationships.erase(name);
2151 1 : return true;
2152 : }
2153 :
2154 : /************************************************************************/
2155 : /* UpdateRelationship() */
2156 : /************************************************************************/
2157 :
2158 3 : bool OGROpenFileGDBDataSource::UpdateRelationship(
2159 : std::unique_ptr<GDALRelationship> &&relationship,
2160 : std::string &failureReason)
2161 : {
2162 6 : const std::string relationshipName(relationship->GetName());
2163 3 : if (eAccess != GA_Update)
2164 : {
2165 0 : CPLError(CE_Failure, CPLE_NotSupported,
2166 : "UpdateRelationship() not supported on read-only dataset");
2167 0 : return false;
2168 : }
2169 :
2170 3 : if (GetRelationship(relationshipName) == nullptr)
2171 : {
2172 1 : failureReason = "The relationship should already exist to be updated";
2173 1 : return false;
2174 : }
2175 :
2176 2 : if (relationship->GetCardinality() ==
2177 : GDALRelationshipCardinality::GRC_MANY_TO_ONE)
2178 : {
2179 0 : failureReason = "Many to one relationships are not supported";
2180 0 : return false;
2181 : }
2182 :
2183 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
2184 0 : return false;
2185 :
2186 4 : std::string osOriginUUID;
2187 2 : if (!FindUUIDFromName(relationship->GetLeftTableName(), osOriginUUID))
2188 : {
2189 0 : failureReason = ("Left table " + relationship->GetLeftTableName() +
2190 : " is not an existing layer in the dataset")
2191 0 : .c_str();
2192 0 : return false;
2193 : }
2194 4 : std::string osDestinationUUID;
2195 2 : if (!FindUUIDFromName(relationship->GetRightTableName(), osDestinationUUID))
2196 : {
2197 0 : failureReason = ("Right table " + relationship->GetRightTableName() +
2198 : " is not an existing layer in the dataset")
2199 0 : .c_str();
2200 0 : return false;
2201 : }
2202 :
2203 4 : FileGDBTable oTable;
2204 4 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) ||
2205 2 : oTable.GetTotalRecordCount() >= INT32_MAX)
2206 : {
2207 0 : return false;
2208 : }
2209 :
2210 : // hopefully this just needs to be a unique value. Seems to autoincrement
2211 : // when created from ArcMap at least!
2212 2 : const int iDsId = static_cast<int>(oTable.GetTotalRecordCount()) + 1;
2213 :
2214 4 : std::string osMappingTableOidName;
2215 2 : if (relationship->GetCardinality() ==
2216 : GDALRelationshipCardinality::GRC_MANY_TO_MANY)
2217 : {
2218 0 : if (!relationship->GetMappingTableName().empty())
2219 : {
2220 : auto poLayer =
2221 0 : GetLayerByName(relationship->GetMappingTableName().c_str());
2222 0 : if (poLayer)
2223 : {
2224 0 : osMappingTableOidName = poLayer->GetFIDColumn();
2225 : }
2226 : }
2227 0 : if (osMappingTableOidName.empty())
2228 : {
2229 0 : failureReason = "Relationship mapping table does not exist";
2230 0 : return false;
2231 : }
2232 : }
2233 :
2234 : std::string osXML = BuildXMLRelationshipDef(
2235 4 : relationship.get(), iDsId, osMappingTableOidName, failureReason);
2236 2 : if (osXML.empty())
2237 : {
2238 0 : return false;
2239 : }
2240 :
2241 2 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, false);
2242 2 : FETCH_FIELD_IDX_WITH_RET(iType, "Type", FGFT_GUID, false);
2243 2 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, false);
2244 2 : FETCH_FIELD_IDX_WITH_RET(iDefinition, "Definition", FGFT_XML, false);
2245 2 : FETCH_FIELD_IDX_WITH_RET(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32,
2246 : false);
2247 :
2248 2 : bool bMatchFound = false;
2249 4 : std::string osUUID;
2250 16 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
2251 : ++iCurFeat)
2252 : {
2253 16 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
2254 16 : if (iCurFeat < 0)
2255 0 : break;
2256 16 : const auto psName = oTable.GetFieldValue(iName);
2257 16 : if (psName && psName->String == relationshipName)
2258 : {
2259 2 : const auto psType = oTable.GetFieldValue(iType);
2260 2 : if (psType && EQUAL(psType->String, pszRelationshipTypeUUID))
2261 : {
2262 2 : const auto psUUID = oTable.GetFieldValue(iUUID);
2263 2 : if (psUUID)
2264 : {
2265 2 : osUUID = psUUID->String;
2266 : }
2267 :
2268 2 : auto asFields = oTable.GetAllFieldValues();
2269 :
2270 4 : if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
2271 2 : !OGR_RawField_IsUnset(&asFields[iDefinition]))
2272 : {
2273 2 : CPLFree(asFields[iDefinition].String);
2274 : }
2275 2 : asFields[iDefinition].String = CPLStrdup(osXML.c_str());
2276 :
2277 2 : switch (relationship->GetCardinality())
2278 : {
2279 0 : case GDALRelationshipCardinality::GRC_ONE_TO_ONE:
2280 0 : asFields[iDatasetSubtype1].Integer = 1;
2281 0 : break;
2282 2 : case GDALRelationshipCardinality::GRC_ONE_TO_MANY:
2283 2 : asFields[iDatasetSubtype1].Integer = 2;
2284 2 : break;
2285 0 : case GDALRelationshipCardinality::GRC_MANY_TO_MANY:
2286 0 : asFields[iDatasetSubtype1].Integer = 3;
2287 0 : break;
2288 0 : case GDALRelationshipCardinality::GRC_MANY_TO_ONE:
2289 : // unreachable
2290 0 : break;
2291 : }
2292 :
2293 : bool bRet =
2294 2 : oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr);
2295 2 : oTable.FreeAllFieldValues(asFields);
2296 2 : if (!bRet)
2297 0 : return false;
2298 2 : bMatchFound = true;
2299 2 : break;
2300 : }
2301 : }
2302 :
2303 14 : if (!oTable.Sync())
2304 : {
2305 0 : return false;
2306 : }
2307 : }
2308 :
2309 2 : if (!bMatchFound)
2310 0 : return false;
2311 :
2312 : // First delete all existing item relationships for the item, and then we'll
2313 : // rebuild them again.
2314 2 : if (!RemoveRelationshipFromItemRelationships(osUUID))
2315 : {
2316 : failureReason =
2317 0 : "Could not remove relationship from GDB_ItemRelationships";
2318 0 : return false;
2319 : }
2320 2 : if (!RegisterRelationshipInItemRelationships(osUUID, osOriginUUID,
2321 : osDestinationUUID))
2322 : {
2323 : failureReason =
2324 0 : "Could not register relationship in GDB_ItemRelationships";
2325 0 : return false;
2326 : }
2327 :
2328 2 : m_osMapRelationships[relationshipName] = std::move(relationship);
2329 :
2330 2 : return true;
2331 : }
2332 :
2333 : /************************************************************************/
2334 : /* StartTransaction() */
2335 : /************************************************************************/
2336 :
2337 16 : OGRErr OGROpenFileGDBDataSource::StartTransaction(int bForce)
2338 : {
2339 16 : if (!bForce)
2340 : {
2341 0 : CPLError(CE_Failure, CPLE_NotSupported,
2342 : "Transactions only supported in forced mode");
2343 0 : return OGRERR_UNSUPPORTED_OPERATION;
2344 : }
2345 :
2346 16 : if (eAccess != GA_Update)
2347 1 : return OGRERR_FAILURE;
2348 :
2349 15 : if (m_bInTransaction)
2350 : {
2351 1 : CPLError(CE_Failure, CPLE_AppDefined,
2352 : "Transaction is already in progress");
2353 1 : return OGRERR_FAILURE;
2354 : }
2355 :
2356 : m_osTransactionBackupDirname =
2357 14 : CPLFormFilename(m_osDirName.c_str(), ".ogrtransaction_backup", nullptr);
2358 : VSIStatBufL sStat;
2359 14 : if (VSIStatL(m_osTransactionBackupDirname.c_str(), &sStat) == 0)
2360 : {
2361 1 : CPLError(CE_Failure, CPLE_AppDefined,
2362 : "A previous backup directory %s already exists, which means "
2363 : "that a previous transaction was not cleanly committed or "
2364 : "rolled back.\n"
2365 : "Either manually restore the previous state from that "
2366 : "directory or remove it, before creating a new transaction.",
2367 : m_osTransactionBackupDirname.c_str());
2368 1 : return OGRERR_FAILURE;
2369 : }
2370 13 : else if (VSIMkdir(m_osTransactionBackupDirname.c_str(), 0755) != 0)
2371 : {
2372 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create directory %s",
2373 : m_osTransactionBackupDirname.c_str());
2374 0 : return OGRERR_FAILURE;
2375 : }
2376 :
2377 13 : m_bInTransaction = true;
2378 13 : return OGRERR_NONE;
2379 : }
2380 :
2381 : /************************************************************************/
2382 : /* BackupSystemTablesForTransaction() */
2383 : /************************************************************************/
2384 :
2385 10 : bool OGROpenFileGDBDataSource::BackupSystemTablesForTransaction()
2386 : {
2387 10 : if (m_bSystemTablesBackedup)
2388 5 : return true;
2389 :
2390 5 : char **papszFiles = VSIReadDir(m_osDirName.c_str());
2391 106 : for (char **papszIter = papszFiles;
2392 106 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2393 : {
2394 101 : const std::string osBasename = CPLGetBasename(*papszIter);
2395 101 : if (osBasename.size() == strlen("a00000001") &&
2396 179 : osBasename.compare(0, 8, "a0000000") == 0 && osBasename[8] >= '1' &&
2397 78 : osBasename[8] <= '8')
2398 : {
2399 : std::string osDestFilename = CPLFormFilename(
2400 72 : m_osTransactionBackupDirname.c_str(), *papszIter, nullptr);
2401 : std::string osSourceFilename =
2402 72 : CPLFormFilename(m_osDirName.c_str(), *papszIter, nullptr);
2403 72 : if (CPLCopyFile(osDestFilename.c_str(), osSourceFilename.c_str()) !=
2404 : 0)
2405 : {
2406 0 : CSLDestroy(papszFiles);
2407 0 : return false;
2408 : }
2409 : }
2410 : }
2411 :
2412 5 : CSLDestroy(papszFiles);
2413 5 : m_bSystemTablesBackedup = true;
2414 5 : return true;
2415 : }
2416 :
2417 : /************************************************************************/
2418 : /* CommitTransaction() */
2419 : /************************************************************************/
2420 :
2421 5 : OGRErr OGROpenFileGDBDataSource::CommitTransaction()
2422 : {
2423 5 : if (!m_bInTransaction)
2424 : {
2425 1 : CPLError(CE_Failure, CPLE_AppDefined, "No transaction in progress");
2426 1 : return OGRERR_FAILURE;
2427 : }
2428 :
2429 7 : for (auto &poLayer : m_apoLayers)
2430 3 : poLayer->CommitEmulatedTransaction();
2431 :
2432 4 : VSIRmdirRecursive(m_osTransactionBackupDirname.c_str());
2433 :
2434 4 : m_bInTransaction = false;
2435 4 : m_bSystemTablesBackedup = false;
2436 4 : m_oSetLayersCreatedInTransaction.clear();
2437 4 : m_oSetLayersDeletedInTransaction.clear();
2438 :
2439 4 : return OGRERR_NONE;
2440 : }
2441 :
2442 : /************************************************************************/
2443 : /* RollbackTransaction() */
2444 : /************************************************************************/
2445 :
2446 11 : OGRErr OGROpenFileGDBDataSource::RollbackTransaction()
2447 : {
2448 11 : if (!m_bInTransaction)
2449 : {
2450 1 : CPLError(CE_Failure, CPLE_AppDefined, "No transaction in progress");
2451 1 : return OGRERR_FAILURE;
2452 : }
2453 :
2454 10 : OGRErr eErr = OGRERR_NONE;
2455 :
2456 : // Restore system tables
2457 : {
2458 10 : char **papszFiles = VSIReadDir(m_osTransactionBackupDirname.c_str());
2459 10 : if (papszFiles == nullptr)
2460 : {
2461 2 : CPLError(CE_Failure, CPLE_AppDefined,
2462 : "Backup directory %s no longer found! Original database "
2463 : "cannot be restored",
2464 : m_osTransactionBackupDirname.c_str());
2465 2 : return OGRERR_FAILURE;
2466 : }
2467 78 : for (char **papszIter = papszFiles;
2468 78 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2469 : {
2470 140 : const std::string osBasename = CPLGetBasename(*papszIter);
2471 70 : if (osBasename.size() == strlen("a00000001") &&
2472 56 : osBasename.compare(0, 8, "a0000000") == 0 &&
2473 126 : osBasename[8] >= '1' && osBasename[8] <= '8')
2474 : {
2475 : std::string osDestFilename =
2476 88 : CPLFormFilename(m_osDirName.c_str(), *papszIter, nullptr);
2477 : std::string osSourceFilename = CPLFormFilename(
2478 88 : m_osTransactionBackupDirname.c_str(), *papszIter, nullptr);
2479 44 : if (CPLCopyFile(osDestFilename.c_str(),
2480 44 : osSourceFilename.c_str()) != 0)
2481 : {
2482 0 : eErr = OGRERR_FAILURE;
2483 : }
2484 : }
2485 : }
2486 8 : CSLDestroy(papszFiles);
2487 : }
2488 :
2489 : // Restore layers in their original state
2490 13 : for (auto &poLayer : m_apoLayers)
2491 5 : poLayer->RollbackEmulatedTransaction();
2492 9 : for (auto &poLayer : m_oSetLayersDeletedInTransaction)
2493 1 : poLayer->RollbackEmulatedTransaction();
2494 :
2495 : // Remove layers created during transaction
2496 9 : for (auto poLayer : m_oSetLayersCreatedInTransaction)
2497 : {
2498 : const std::string osThisBasename =
2499 2 : CPLGetBasename(poLayer->GetFilename().c_str());
2500 1 : poLayer->Close();
2501 :
2502 1 : char **papszFiles = VSIReadDir(m_osDirName.c_str());
2503 23 : for (char **papszIter = papszFiles;
2504 23 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2505 : {
2506 44 : const std::string osBasename = CPLGetBasename(*papszIter);
2507 22 : if (osBasename == osThisBasename)
2508 : {
2509 : std::string osDestFilename =
2510 6 : CPLFormFilename(m_osDirName.c_str(), *papszIter, nullptr);
2511 3 : VSIUnlink(osDestFilename.c_str());
2512 : }
2513 : }
2514 1 : CSLDestroy(papszFiles);
2515 : }
2516 :
2517 8 : if (eErr == OGRERR_NONE)
2518 : {
2519 8 : if (VSIRmdirRecursive(m_osTransactionBackupDirname.c_str()) != 0)
2520 : {
2521 0 : CPLError(
2522 : CE_Warning, CPLE_AppDefined,
2523 : "Backup directory %s could not be destroyed. But original "
2524 : "dataset "
2525 : "should have been properly restored. You will need to manually "
2526 : "remove the backup directory.",
2527 : m_osTransactionBackupDirname.c_str());
2528 : }
2529 : }
2530 : else
2531 : {
2532 0 : CPLError(CE_Failure, CPLE_AppDefined,
2533 : "Backup directory %s could not be properly restored onto "
2534 : "live database. Corruption is likely!",
2535 : m_osTransactionBackupDirname.c_str());
2536 : }
2537 :
2538 8 : m_bInTransaction = false;
2539 8 : m_bSystemTablesBackedup = false;
2540 8 : m_oSetLayersCreatedInTransaction.clear();
2541 8 : m_oSetLayersDeletedInTransaction.clear();
2542 :
2543 8 : return eErr;
2544 : }
|