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 195 : 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 390 : FileGDBTable oTable;
49 195 : if (!oTable.Open(m_osGDBSpatialRefsFilename.c_str(), false))
50 0 : return false;
51 :
52 195 : FETCH_FIELD_IDX(iSRTEXT, "SRTEXT", FGFT_STRING);
53 195 : FETCH_FIELD_IDX(iFalseX, "FalseX", FGFT_FLOAT64);
54 195 : FETCH_FIELD_IDX(iFalseY, "FalseY", FGFT_FLOAT64);
55 195 : FETCH_FIELD_IDX(iXYUnits, "XYUnits", FGFT_FLOAT64);
56 195 : FETCH_FIELD_IDX(iFalseZ, "FalseZ", FGFT_FLOAT64);
57 195 : FETCH_FIELD_IDX(iZUnits, "ZUnits", FGFT_FLOAT64);
58 195 : FETCH_FIELD_IDX(iFalseM, "FalseM", FGFT_FLOAT64);
59 195 : FETCH_FIELD_IDX(iMUnits, "MUnits", FGFT_FLOAT64);
60 195 : FETCH_FIELD_IDX(iXYTolerance, "XYTolerance", FGFT_FLOAT64);
61 195 : FETCH_FIELD_IDX(iZTolerance, "ZTolerance", FGFT_FLOAT64);
62 195 : FETCH_FIELD_IDX(iMTolerance, "MTolerance", FGFT_FLOAT64);
63 :
64 195 : int64_t iCurFeat = 0;
65 391 : while (iCurFeat < oTable.GetTotalRecordCount())
66 : {
67 220 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
68 220 : if (iCurFeat < 0)
69 0 : break;
70 220 : iCurFeat++;
71 220 : const auto psSRTEXT = oTable.GetFieldValue(iSRTEXT);
72 220 : 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 171 : return false;
95 : }
96 :
97 : /***********************************************************************/
98 : /* AddNewSpatialRef() */
99 : /***********************************************************************/
100 :
101 400 : 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 800 : FileGDBTable oTable;
108 400 : if (!oTable.Open(m_osGDBSpatialRefsFilename.c_str(), true))
109 0 : return false;
110 :
111 400 : FETCH_FIELD_IDX(iSRTEXT, "SRTEXT", FGFT_STRING);
112 400 : FETCH_FIELD_IDX(iFalseX, "FalseX", FGFT_FLOAT64);
113 400 : FETCH_FIELD_IDX(iFalseY, "FalseY", FGFT_FLOAT64);
114 400 : FETCH_FIELD_IDX(iXYUnits, "XYUnits", FGFT_FLOAT64);
115 400 : FETCH_FIELD_IDX(iFalseZ, "FalseZ", FGFT_FLOAT64);
116 400 : FETCH_FIELD_IDX(iZUnits, "ZUnits", FGFT_FLOAT64);
117 400 : FETCH_FIELD_IDX(iFalseM, "FalseM", FGFT_FLOAT64);
118 400 : FETCH_FIELD_IDX(iMUnits, "MUnits", FGFT_FLOAT64);
119 400 : FETCH_FIELD_IDX(iXYTolerance, "XYTolerance", FGFT_FLOAT64);
120 400 : FETCH_FIELD_IDX(iZTolerance, "ZTolerance", FGFT_FLOAT64);
121 400 : FETCH_FIELD_IDX(iMTolerance, "MTolerance", FGFT_FLOAT64);
122 :
123 400 : std::vector<OGRField> fields(oTable.GetFieldCount(),
124 800 : FileGDBField::UNSET_FIELD);
125 400 : fields[iSRTEXT].String = const_cast<char *>(osWKT.c_str());
126 400 : fields[iFalseX].Real = dfXOrigin;
127 400 : fields[iFalseY].Real = dfYOrigin;
128 400 : fields[iXYUnits].Real = dfXYScale;
129 400 : fields[iFalseZ].Real = dfZOrigin;
130 400 : fields[iZUnits].Real = dfZScale;
131 400 : fields[iFalseM].Real = dfMOrigin;
132 400 : fields[iMUnits].Real = dfMScale;
133 400 : fields[iXYTolerance].Real = dfXYTolerance;
134 400 : fields[iZTolerance].Real = dfZTolerance;
135 400 : fields[iMTolerance].Real = dfMTolerance;
136 :
137 400 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
138 : }
139 :
140 : /***********************************************************************/
141 : /* RegisterLayerInSystemCatalog() */
142 : /***********************************************************************/
143 :
144 280 : bool OGROpenFileGDBDataSource::RegisterLayerInSystemCatalog(
145 : const std::string &osLayerName)
146 : {
147 560 : FileGDBTable oTable;
148 280 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), true))
149 0 : return false;
150 :
151 280 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
152 280 : FETCH_FIELD_IDX(iFileFormat, "FileFormat", FGFT_INT32);
153 :
154 280 : std::vector<OGRField> fields(oTable.GetFieldCount(),
155 560 : FileGDBField::UNSET_FIELD);
156 280 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
157 280 : fields[iFileFormat].Integer = 0;
158 280 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
159 : }
160 :
161 : /***********************************************************************/
162 : /* RegisterInItemRelationships() */
163 : /***********************************************************************/
164 :
165 337 : bool OGROpenFileGDBDataSource::RegisterInItemRelationships(
166 : const std::string &osOriginGUID, const std::string &osDestGUID,
167 : const std::string &osTypeGUID)
168 : {
169 674 : FileGDBTable oTable;
170 337 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
171 0 : return false;
172 :
173 337 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
174 337 : FETCH_FIELD_IDX(iOriginID, "OriginID", FGFT_GUID);
175 337 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
176 337 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
177 337 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
178 :
179 337 : std::vector<OGRField> fields(oTable.GetFieldCount(),
180 1011 : FileGDBField::UNSET_FIELD);
181 337 : const std::string osGUID = OFGDBGenerateUUID();
182 337 : fields[iUUID].String = const_cast<char *>(osGUID.c_str());
183 337 : fields[iOriginID].String = const_cast<char *>(osOriginGUID.c_str());
184 337 : fields[iDestID].String = const_cast<char *>(osDestGUID.c_str());
185 337 : fields[iType].String = const_cast<char *>(osTypeGUID.c_str());
186 337 : fields[iProperties].Integer = 1;
187 337 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
188 : }
189 :
190 : /***********************************************************************/
191 : /* RegisterRelationshipInItemRelationships() */
192 : /***********************************************************************/
193 :
194 16 : 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 16 : if (!RegisterInItemRelationships(osOriginGUID, osRelationshipGUID,
203 : pszDatasetsRelatedThroughUUID))
204 0 : return false;
205 16 : if (!RegisterInItemRelationships(osDestGUID, osRelationshipGUID,
206 : pszDatasetsRelatedThroughUUID))
207 0 : return false;
208 16 : if (!RegisterInItemRelationships(m_osRootGUID, osRelationshipGUID,
209 : pszDatasetInFolderUUID))
210 0 : return false;
211 :
212 16 : 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 42 : bool OGROpenFileGDBDataSource::FindUUIDFromName(const std::string &osName,
387 : std::string &osUUIDOut)
388 : {
389 84 : FileGDBTable oTable;
390 42 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
391 0 : return false;
392 :
393 42 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
394 42 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
395 :
396 296 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
397 : ++iCurFeat)
398 : {
399 295 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
400 295 : if (iCurFeat < 0)
401 0 : break;
402 295 : const auto psName = oTable.GetFieldValue(iName);
403 295 : if (psName && psName->String == osName)
404 : {
405 41 : const auto psUUID = oTable.GetFieldValue(iUUID);
406 41 : if (psUUID)
407 : {
408 41 : osUUIDOut = psUUID->String;
409 41 : 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 196 : 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 392 : FileGDBTable oTable;
465 196 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
466 0 : return false;
467 :
468 196 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
469 196 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
470 196 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
471 196 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
472 196 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
473 196 : FETCH_FIELD_IDX(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32);
474 196 : FETCH_FIELD_IDX(iDatasetSubtype2, "DatasetSubtype2", FGFT_INT32);
475 196 : FETCH_FIELD_IDX(iDatasetInfo1, "DatasetInfo1", FGFT_STRING);
476 196 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
477 196 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
478 196 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
479 196 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
480 :
481 196 : std::vector<OGRField> fields(oTable.GetFieldCount(),
482 588 : FileGDBField::UNSET_FIELD);
483 196 : fields[iUUID].String = const_cast<char *>(osLayerGUID.c_str());
484 196 : fields[iType].String = const_cast<char *>(pszFeatureClassTypeUUID);
485 196 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
486 196 : CPLString osUCName(osLayerName);
487 196 : osUCName.toupper();
488 196 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
489 196 : fields[iPath].String = const_cast<char *>(osPath.c_str());
490 196 : fields[iDatasetSubtype1].Integer = 1;
491 196 : fields[iDatasetSubtype2].Integer = poLyrTable->GetGeometryType();
492 196 : const auto poGeomFieldDefn = poLyrTable->GetGeomField();
493 196 : if (poGeomFieldDefn) // should always be true
494 195 : fields[iDatasetInfo1].String =
495 195 : const_cast<char *>(poGeomFieldDefn->GetName().c_str());
496 196 : fields[iURL].String = const_cast<char *>("");
497 196 : fields[iDefinition].String = const_cast<char *>(pszXMLDefinition);
498 196 : if (pszDocumentation && pszDocumentation[0])
499 1 : fields[iDocumentation].String = const_cast<char *>(pszDocumentation);
500 196 : fields[iProperties].Integer = 1;
501 196 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
502 : }
503 :
504 : /***********************************************************************/
505 : /* RegisterASpatialTableInItems() */
506 : /***********************************************************************/
507 :
508 85 : 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 170 : FileGDBTable oTable;
514 85 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
515 0 : return false;
516 :
517 85 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
518 85 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
519 85 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
520 85 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
521 85 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
522 85 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
523 85 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
524 85 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
525 85 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
526 :
527 85 : std::vector<OGRField> fields(oTable.GetFieldCount(),
528 255 : FileGDBField::UNSET_FIELD);
529 85 : fields[iUUID].String = const_cast<char *>(osLayerGUID.c_str());
530 85 : fields[iType].String = const_cast<char *>(pszTableTypeUUID);
531 85 : fields[iName].String = const_cast<char *>(osLayerName.c_str());
532 85 : CPLString osUCName(osLayerName);
533 85 : osUCName.toupper();
534 85 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
535 85 : fields[iPath].String = const_cast<char *>(osPath.c_str());
536 85 : fields[iURL].String = const_cast<char *>("");
537 85 : fields[iDefinition].String = const_cast<char *>(pszXMLDefinition);
538 85 : if (pszDocumentation && pszDocumentation[0])
539 0 : fields[iDocumentation].String = const_cast<char *>(pszDocumentation);
540 85 : fields[iProperties].Integer = 1;
541 85 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
542 : }
543 :
544 : /***********************************************************************/
545 : /* CreateGDBSystemCatalog() */
546 : /***********************************************************************/
547 :
548 229 : bool OGROpenFileGDBDataSource::CreateGDBSystemCatalog()
549 : {
550 : // Write GDB_SystemCatalog file
551 : m_osGDBSystemCatalogFilename =
552 229 : CPLFormFilenameSafe(m_osDirName.c_str(), "a00000001.gdbtable", nullptr);
553 458 : FileGDBTable oTable;
554 229 : if (!oTable.Create(m_osGDBSystemCatalogFilename.c_str(), 4, FGTGT_NONE,
555 458 : false, false) ||
556 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
557 229 : "ID", std::string(), FGFT_OBJECTID,
558 0 : /* bNullable = */ false,
559 0 : /* bRequired = */ true,
560 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
561 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
562 229 : "Name", std::string(), FGFT_STRING,
563 0 : /* bNullable = */ false,
564 0 : /* bRequired = */ false,
565 1145 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
566 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
567 229 : "FileFormat", std::string(), FGFT_INT32,
568 0 : /* bNullable = */ false,
569 0 : /* bRequired = */ false,
570 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
571 : {
572 0 : return false;
573 : }
574 :
575 229 : std::vector<OGRField> fields(oTable.GetFieldCount(),
576 687 : FileGDBField::UNSET_FIELD);
577 :
578 1832 : 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 3893 : {"GDB_ReplicaLog", 2}})
587 : {
588 1832 : fields[1].String = const_cast<char *>(pair.first);
589 1832 : fields[2].Integer = pair.second;
590 1832 : if (!oTable.CreateFeature(fields, nullptr))
591 0 : return false;
592 : }
593 :
594 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
595 229 : this, m_osGDBSystemCatalogFilename.c_str(), "GDB_SystemCatalog", "", "",
596 458 : true));
597 :
598 229 : return oTable.Sync();
599 : }
600 :
601 : /***********************************************************************/
602 : /* CreateGDBDBTune() */
603 : /***********************************************************************/
604 :
605 229 : bool OGROpenFileGDBDataSource::CreateGDBDBTune()
606 : {
607 : // Write GDB_DBTune file
608 : const std::string osFilename(CPLFormFilenameSafe(
609 458 : m_osDirName.c_str(), "a00000002.gdbtable", nullptr));
610 458 : FileGDBTable oTable;
611 229 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
612 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
613 229 : "Keyword", std::string(), FGFT_STRING,
614 0 : /* bNullable = */ false,
615 0 : /* bRequired = */ false,
616 458 : /* bEditable = */ true, 32, FileGDBField::UNSET_FIELD)) ||
617 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
618 229 : "ParameterName", std::string(), FGFT_STRING,
619 0 : /* bNullable = */ false,
620 0 : /* bRequired = */ false,
621 1145 : /* bEditable = */ true, 32, FileGDBField::UNSET_FIELD)) ||
622 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
623 229 : "ConfigString", std::string(), FGFT_STRING,
624 0 : /* bNullable = */ true,
625 0 : /* bRequired = */ false,
626 458 : /* bEditable = */ true, 2048, FileGDBField::UNSET_FIELD)))
627 : {
628 0 : return false;
629 : }
630 :
631 229 : std::vector<OGRField> fields(oTable.GetFieldCount(),
632 687 : 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 8244 : for (const auto &record : apsData)
685 : {
686 8015 : fields[0].String = const_cast<char *>(record.pszKeyword);
687 8015 : fields[1].String = const_cast<char *>(record.pszParameterName);
688 8015 : fields[2].String = const_cast<char *>(record.pszConfigString);
689 8015 : if (!oTable.CreateFeature(fields, nullptr))
690 0 : return false;
691 : }
692 :
693 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
694 229 : this, osFilename.c_str(), "GDB_DBTune", "", "", true));
695 :
696 229 : return oTable.Sync();
697 : }
698 :
699 : /***********************************************************************/
700 : /* CreateGDBSpatialRefs() */
701 : /***********************************************************************/
702 :
703 229 : bool OGROpenFileGDBDataSource::CreateGDBSpatialRefs()
704 : {
705 : // Write GDB_SpatialRefs file
706 : m_osGDBSpatialRefsFilename =
707 229 : CPLFormFilenameSafe(m_osDirName.c_str(), "a00000003.gdbtable", nullptr);
708 458 : FileGDBTable oTable;
709 229 : if (!oTable.Create(m_osGDBSpatialRefsFilename.c_str(), 4, FGTGT_NONE, false,
710 458 : false) ||
711 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
712 229 : "ID", std::string(), FGFT_OBJECTID,
713 0 : /* bNullable = */ false,
714 0 : /* bRequired = */ true,
715 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
716 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
717 229 : "SRTEXT", std::string(), FGFT_STRING,
718 0 : /* bNullable = */ false,
719 0 : /* bRequired = */ false,
720 458 : /* bEditable = */ true, 2048, FileGDBField::UNSET_FIELD)) ||
721 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
722 229 : "FalseX", std::string(), FGFT_FLOAT64,
723 0 : /* bNullable = */ true,
724 0 : /* bRequired = */ false,
725 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
726 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
727 229 : "FalseY", std::string(), FGFT_FLOAT64,
728 0 : /* bNullable = */ true,
729 0 : /* bRequired = */ false,
730 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
731 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
732 229 : "XYUnits", std::string(), FGFT_FLOAT64,
733 0 : /* bNullable = */ true,
734 0 : /* bRequired = */ false,
735 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
736 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
737 229 : "FalseZ", std::string(), FGFT_FLOAT64,
738 0 : /* bNullable = */ true,
739 0 : /* bRequired = */ false,
740 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
741 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
742 229 : "ZUnits", std::string(), FGFT_FLOAT64,
743 0 : /* bNullable = */ true,
744 0 : /* bRequired = */ false,
745 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
746 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
747 229 : "FalseM", std::string(), FGFT_FLOAT64,
748 0 : /* bNullable = */ true,
749 0 : /* bRequired = */ false,
750 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
751 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
752 229 : "MUnits", std::string(), FGFT_FLOAT64,
753 0 : /* bNullable = */ true,
754 0 : /* bRequired = */ false,
755 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
756 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
757 229 : "XYTolerance", std::string(), FGFT_FLOAT64,
758 0 : /* bNullable = */ true,
759 0 : /* bRequired = */ false,
760 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
761 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
762 229 : "ZTolerance", std::string(), FGFT_FLOAT64,
763 0 : /* bNullable = */ true,
764 0 : /* bRequired = */ false,
765 1145 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
766 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
767 229 : "MTolerance", std::string(), FGFT_FLOAT64,
768 0 : /* bNullable = */ true,
769 0 : /* bRequired = */ false,
770 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
771 : {
772 0 : return false;
773 : }
774 :
775 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
776 229 : this, m_osGDBSpatialRefsFilename.c_str(), "GDB_SpatialRefs", "", "",
777 458 : true));
778 :
779 229 : return oTable.Sync();
780 : }
781 :
782 : /***********************************************************************/
783 : /* CreateGDBItems() */
784 : /***********************************************************************/
785 :
786 229 : bool OGROpenFileGDBDataSource::CreateGDBItems()
787 : {
788 : // Write GDB_Items file
789 229 : 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 687 : 1000000, 0.000002, {0.012, 0.4, 12.0}));
796 229 : poGeomField->SetZOriginScaleTolerance(-100000, 10000, 0.001);
797 229 : poGeomField->SetMOriginScaleTolerance(-100000, 10000, 0.001);
798 :
799 229 : 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 229 : CPLFormFilenameSafe(m_osDirName.c_str(), "a00000004.gdbtable", nullptr);
812 458 : FileGDBTable oTable;
813 229 : if (!oTable.Create(m_osGDBItemsFilename.c_str(), 4, FGTGT_POLYGON, false,
814 458 : false) ||
815 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
816 229 : "ObjectID", std::string(), FGFT_OBJECTID,
817 0 : /* bNullable = */ false,
818 0 : /* bRequired = */ true,
819 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
820 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
821 229 : "UUID", std::string(), FGFT_GLOBALID,
822 0 : /* bNullable = */ false,
823 0 : /* bRequired = */ true,
824 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
825 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
826 229 : "Type", std::string(), FGFT_GUID,
827 0 : /* bNullable = */ false,
828 0 : /* bRequired = */ false,
829 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
830 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
831 229 : "Name", std::string(), FGFT_STRING,
832 0 : /* bNullable = */ true,
833 0 : /* bRequired = */ false,
834 458 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
835 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
836 229 : "PhysicalName", std::string(), FGFT_STRING,
837 0 : /* bNullable = */ true,
838 0 : /* bRequired = */ false,
839 458 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
840 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
841 229 : "Path", std::string(), FGFT_STRING,
842 0 : /* bNullable = */ true,
843 0 : /* bRequired = */ false,
844 458 : /* bEditable = */ true, 260, FileGDBField::UNSET_FIELD)) ||
845 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
846 229 : "DatasetSubtype1", std::string(), FGFT_INT32,
847 0 : /* bNullable = */ true,
848 0 : /* bRequired = */ false,
849 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
850 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
851 229 : "DatasetSubtype2", std::string(), FGFT_INT32,
852 0 : /* bNullable = */ true,
853 0 : /* bRequired = */ false,
854 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
855 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
856 229 : "DatasetInfo1", std::string(), FGFT_STRING,
857 0 : /* bNullable = */ true,
858 0 : /* bRequired = */ false,
859 458 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
860 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
861 229 : "DatasetInfo2", std::string(), FGFT_STRING,
862 0 : /* bNullable = */ true,
863 0 : /* bRequired = */ false,
864 458 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
865 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
866 229 : "URL", std::string(), FGFT_STRING,
867 0 : /* bNullable = */ true,
868 0 : /* bRequired = */ false,
869 458 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
870 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
871 229 : "Definition", std::string(), FGFT_XML,
872 0 : /* bNullable = */ true,
873 0 : /* bRequired = */ false,
874 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
875 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
876 229 : "Documentation", std::string(), FGFT_XML,
877 0 : /* bNullable = */ true,
878 0 : /* bRequired = */ false,
879 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
880 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
881 229 : "ItemInfo", std::string(), FGFT_XML,
882 0 : /* bNullable = */ true,
883 0 : /* bRequired = */ false,
884 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
885 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
886 229 : "Properties", std::string(), FGFT_INT32,
887 0 : /* bNullable = */ true,
888 0 : /* bRequired = */ false,
889 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
890 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
891 229 : "Defaults", std::string(), FGFT_BINARY,
892 0 : /* bNullable = */ true,
893 0 : /* bRequired = */ false,
894 1145 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
895 458 : !oTable.CreateField(std::move(poGeomField)))
896 : {
897 0 : return false;
898 : }
899 :
900 229 : std::vector<OGRField> fields(oTable.GetFieldCount(),
901 687 : FileGDBField::UNSET_FIELD);
902 229 : m_osRootGUID = OFGDBGenerateUUID();
903 229 : fields[1].String = const_cast<char *>(m_osRootGUID.c_str());
904 229 : fields[2].String = const_cast<char *>(pszFolderTypeUUID);
905 229 : fields[3].String = const_cast<char *>("");
906 229 : fields[4].String = const_cast<char *>("");
907 229 : fields[5].String = const_cast<char *>("\\");
908 229 : fields[10].String = const_cast<char *>("");
909 229 : fields[14].Integer = 1;
910 229 : if (!oTable.CreateFeature(fields, nullptr))
911 0 : return false;
912 :
913 229 : const std::string osWorkspaceUUID(OFGDBGenerateUUID());
914 229 : fields[1].String = const_cast<char *>(osWorkspaceUUID.c_str());
915 229 : fields[2].String = const_cast<char *>(pszWorkspaceTypeUUID);
916 229 : fields[3].String = const_cast<char *>("Workspace");
917 229 : fields[4].String = const_cast<char *>("WORKSPACE");
918 229 : fields[5].String = const_cast<char *>(""); // Path
919 229 : fields[10].String = const_cast<char *>(""); // URL
920 229 : 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 229 : fields[14].Integer = 0;
939 :
940 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
941 229 : this, m_osGDBItemsFilename.c_str(), "GDB_Items", "", "", true));
942 :
943 229 : return oTable.CreateFeature(fields, nullptr) && oTable.Sync();
944 : }
945 :
946 : /***********************************************************************/
947 : /* CreateGDBItemTypes() */
948 : /***********************************************************************/
949 :
950 229 : bool OGROpenFileGDBDataSource::CreateGDBItemTypes()
951 : {
952 : // Write GDB_ItemTypes file
953 : const std::string osFilename(CPLFormFilenameSafe(
954 458 : m_osDirName.c_str(), "a00000005.gdbtable", nullptr));
955 458 : FileGDBTable oTable;
956 229 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
957 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
958 229 : "ObjectID", std::string(), FGFT_OBJECTID,
959 0 : /* bNullable = */ false,
960 0 : /* bRequired = */ true,
961 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
962 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
963 229 : "UUID", std::string(), FGFT_GUID,
964 0 : /* bNullable = */ false,
965 0 : /* bRequired = */ false,
966 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
967 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
968 229 : "ParentTypeID", std::string(), FGFT_GUID,
969 0 : /* bNullable = */ false,
970 0 : /* bRequired = */ false,
971 1145 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
972 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
973 229 : "Name", std::string(), FGFT_STRING,
974 0 : /* bNullable = */ false,
975 0 : /* bRequired = */ false,
976 458 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)))
977 : {
978 0 : return false;
979 : }
980 :
981 229 : std::vector<OGRField> fields(oTable.GetFieldCount(),
982 687 : 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 7557 : for (const auto &record : apsData)
1054 : {
1055 7328 : fields[1].String = const_cast<char *>(record.pszUUID);
1056 7328 : fields[2].String = const_cast<char *>(record.pszParentTypeID);
1057 7328 : fields[3].String = const_cast<char *>(record.pszName);
1058 7328 : if (!oTable.CreateFeature(fields, nullptr))
1059 0 : return false;
1060 : }
1061 :
1062 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1063 229 : this, osFilename.c_str(), "GDB_ItemTypes", "", "", true));
1064 :
1065 229 : return oTable.Sync();
1066 : }
1067 :
1068 : /***********************************************************************/
1069 : /* CreateGDBItemRelationships() */
1070 : /***********************************************************************/
1071 :
1072 229 : bool OGROpenFileGDBDataSource::CreateGDBItemRelationships()
1073 : {
1074 : // Write GDB_ItemRelationships file
1075 : m_osGDBItemRelationshipsFilename =
1076 229 : CPLFormFilenameSafe(m_osDirName.c_str(), "a00000006.gdbtable", nullptr);
1077 458 : FileGDBTable oTable;
1078 229 : if (!oTable.Create(m_osGDBItemRelationshipsFilename.c_str(), 4, FGTGT_NONE,
1079 458 : false, false) ||
1080 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1081 229 : "ObjectID", std::string(), FGFT_OBJECTID,
1082 0 : /* bNullable = */ false,
1083 0 : /* bRequired = */ true,
1084 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1085 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1086 229 : "UUID", std::string(), FGFT_GLOBALID,
1087 0 : /* bNullable = */ false,
1088 0 : /* bRequired = */ true,
1089 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1090 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1091 229 : "OriginID", std::string(), FGFT_GUID,
1092 0 : /* bNullable = */ false,
1093 0 : /* bRequired = */ false,
1094 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1095 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1096 229 : "DestID", std::string(), FGFT_GUID,
1097 0 : /* bNullable = */ false,
1098 0 : /* bRequired = */ false,
1099 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1100 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1101 229 : "Type", std::string(), FGFT_GUID,
1102 0 : /* bNullable = */ false,
1103 0 : /* bRequired = */ false,
1104 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1105 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1106 229 : "Attributes", std::string(), FGFT_XML,
1107 0 : /* bNullable = */ true,
1108 0 : /* bRequired = */ false,
1109 1145 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1110 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1111 229 : "Properties", std::string(), FGFT_INT32,
1112 0 : /* bNullable = */ true,
1113 0 : /* bRequired = */ false,
1114 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)))
1115 : {
1116 0 : return false;
1117 : }
1118 :
1119 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1120 229 : this, m_osGDBItemRelationshipsFilename.c_str(), "GDB_ItemRelationships",
1121 458 : "", "", true));
1122 :
1123 229 : return oTable.Sync();
1124 : }
1125 :
1126 : /***********************************************************************/
1127 : /* CreateGDBItemRelationshipTypes() */
1128 : /***********************************************************************/
1129 :
1130 229 : bool OGROpenFileGDBDataSource::CreateGDBItemRelationshipTypes()
1131 : {
1132 : // Write GDB_ItemRelationshipTypes file
1133 : const std::string osFilename(CPLFormFilenameSafe(
1134 458 : m_osDirName.c_str(), "a00000007.gdbtable", nullptr));
1135 458 : FileGDBTable oTable;
1136 229 : if (!oTable.Create(osFilename.c_str(), 4, FGTGT_NONE, false, false) ||
1137 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1138 229 : "ObjectID", std::string(), FGFT_OBJECTID,
1139 0 : /* bNullable = */ false,
1140 0 : /* bRequired = */ true,
1141 458 : /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)) ||
1142 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1143 229 : "UUID", std::string(), FGFT_GUID,
1144 0 : /* bNullable = */ false,
1145 0 : /* bRequired = */ false,
1146 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1147 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1148 229 : "OrigItemTypeID", std::string(), FGFT_GUID,
1149 0 : /* bNullable = */ false,
1150 0 : /* bRequired = */ false,
1151 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1152 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1153 229 : "DestItemTypeID", std::string(), FGFT_GUID,
1154 0 : /* bNullable = */ false,
1155 0 : /* bRequired = */ false,
1156 458 : /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD)) ||
1157 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1158 229 : "Name", std::string(), FGFT_STRING,
1159 0 : /* bNullable = */ true,
1160 0 : /* bRequired = */ false,
1161 458 : /* bEditable = */ true, 160, FileGDBField::UNSET_FIELD)) ||
1162 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1163 229 : "ForwardLabel", std::string(), FGFT_STRING,
1164 0 : /* bNullable = */ true,
1165 0 : /* bRequired = */ false,
1166 458 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
1167 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1168 229 : "BackwardLabel", std::string(), FGFT_STRING,
1169 0 : /* bNullable = */ true,
1170 0 : /* bRequired = */ false,
1171 1145 : /* bEditable = */ true, 255, FileGDBField::UNSET_FIELD)) ||
1172 458 : !oTable.CreateField(std::make_unique<FileGDBField>(
1173 229 : "IsContainment", std::string(), FGFT_INT16,
1174 0 : /* bNullable = */ true,
1175 0 : /* bRequired = */ false,
1176 458 : /* 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 229 : std::vector<OGRField> fields(oTable.GetFieldCount(),
1253 687 : FileGDBField::UNSET_FIELD);
1254 3893 : for (const auto &record : apsData)
1255 : {
1256 3664 : fields[1].String = const_cast<char *>(record.pszUUID);
1257 3664 : fields[2].String = const_cast<char *>(record.pszOrigItemTypeID);
1258 3664 : fields[3].String = const_cast<char *>(record.pszDestItemTypeID);
1259 3664 : fields[4].String = const_cast<char *>(record.pszName);
1260 3664 : fields[5].String = const_cast<char *>(record.pszForwardLabel);
1261 3664 : fields[6].String = const_cast<char *>(record.pszBackwardLabel);
1262 3664 : fields[7].Integer = record.IsContainment;
1263 3664 : if (!oTable.CreateFeature(fields, nullptr))
1264 0 : return false;
1265 : }
1266 :
1267 229 : m_apoHiddenLayers.emplace_back(std::make_unique<OGROpenFileGDBLayer>(
1268 229 : this, osFilename.c_str(), "GDB_ItemRelationshipTypes", "", "", true));
1269 :
1270 229 : return oTable.Sync();
1271 : }
1272 :
1273 : /***********************************************************************/
1274 : /* Create() */
1275 : /***********************************************************************/
1276 :
1277 232 : bool OGROpenFileGDBDataSource::Create(const char *pszName)
1278 : {
1279 :
1280 232 : if (!EQUAL(CPLGetExtensionSafe(pszName).c_str(), "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 231 : if (VSIStatL(pszName, &sStat) == 0)
1290 : {
1291 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already exists.", pszName);
1292 0 : return false;
1293 : }
1294 :
1295 231 : 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 229 : CPL_IGNORE_RET_VAL(OFGDBGenerateUUID(/* bInit = */ true));
1303 :
1304 229 : m_osDirName = pszName;
1305 229 : eAccess = GA_Update;
1306 :
1307 : {
1308 : // Write "gdb" file
1309 : const std::string osFilename(
1310 229 : CPLFormFilenameSafe(pszName, "gdb", nullptr));
1311 229 : VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
1312 229 : if (!fp)
1313 0 : return false;
1314 : // Write what the FileGDB SDK writes...
1315 229 : VSIFWriteL("\x05\x00\x00\x00\xDE\xAD\xBE\xEF", 1, 8, fp);
1316 229 : VSIFCloseL(fp);
1317 : }
1318 :
1319 : {
1320 : // Write "timestamps" file
1321 : const std::string osFilename(
1322 229 : CPLFormFilenameSafe(pszName, "timestamps", nullptr));
1323 229 : VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
1324 229 : if (!fp)
1325 0 : return false;
1326 : // Write what the FileGDB SDK writes...
1327 458 : std::vector<GByte> values(400, 0xFF);
1328 229 : VSIFWriteL(values.data(), 1, values.size(), fp);
1329 229 : VSIFCloseL(fp);
1330 : }
1331 :
1332 458 : return CreateGDBSystemCatalog() && CreateGDBDBTune() &&
1333 229 : CreateGDBSpatialRefs() && CreateGDBItems() && CreateGDBItemTypes() &&
1334 458 : CreateGDBItemRelationships() && CreateGDBItemRelationshipTypes();
1335 : // GDB_ReplicaLog can be omitted.
1336 : }
1337 :
1338 : /************************************************************************/
1339 : /* ICreateLayer() */
1340 : /************************************************************************/
1341 :
1342 : OGRLayer *
1343 283 : OGROpenFileGDBDataSource::ICreateLayer(const char *pszLayerName,
1344 : const OGRGeomFieldDefn *poGeomFieldDefn,
1345 : CSLConstList papszOptions)
1346 : {
1347 283 : if (eAccess != GA_Update)
1348 0 : return nullptr;
1349 :
1350 283 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1351 0 : return nullptr;
1352 :
1353 283 : if (m_osRootGUID.empty())
1354 : {
1355 0 : CPLError(CE_Failure, CPLE_AppDefined, "Root UUID missing");
1356 0 : return nullptr;
1357 : }
1358 :
1359 283 : auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
1360 :
1361 566 : FileGDBTable oTable;
1362 566 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), false) ||
1363 283 : oTable.GetTotalRecordCount() >= INT32_MAX)
1364 0 : return nullptr;
1365 283 : const int nTableNum = static_cast<int>(1 + oTable.GetTotalRecordCount());
1366 283 : oTable.Close();
1367 :
1368 : const std::string osFilename(CPLFormFilenameSafe(
1369 566 : m_osDirName.c_str(), CPLSPrintf("a%08x.gdbtable", nTableNum), nullptr));
1370 :
1371 283 : if (wkbFlatten(eType) == wkbLineString)
1372 16 : eType = OGR_GT_SetModifier(wkbMultiLineString, OGR_GT_HasZ(eType),
1373 : OGR_GT_HasM(eType));
1374 267 : else if (wkbFlatten(eType) == wkbPolygon)
1375 17 : eType = OGR_GT_SetModifier(wkbMultiPolygon, OGR_GT_HasZ(eType),
1376 : OGR_GT_HasM(eType));
1377 :
1378 : auto poLayer = std::make_unique<OGROpenFileGDBLayer>(
1379 566 : this, osFilename.c_str(), pszLayerName, eType, papszOptions);
1380 283 : if (!poLayer->Create(poGeomFieldDefn))
1381 3 : return nullptr;
1382 280 : if (m_bInTransaction)
1383 : {
1384 4 : if (!poLayer->BeginEmulatedTransaction())
1385 0 : return nullptr;
1386 4 : m_oSetLayersCreatedInTransaction.insert(poLayer.get());
1387 : }
1388 280 : m_apoLayers.emplace_back(std::move(poLayer));
1389 :
1390 280 : return m_apoLayers.back().get();
1391 : }
1392 :
1393 : /************************************************************************/
1394 : /* DeleteLayer() */
1395 : /************************************************************************/
1396 :
1397 19 : OGRErr OGROpenFileGDBDataSource::DeleteLayer(int iLayer)
1398 : {
1399 19 : if (eAccess != GA_Update)
1400 0 : return OGRERR_FAILURE;
1401 :
1402 19 : if (iLayer < 0 || iLayer >= static_cast<int>(m_apoLayers.size()))
1403 2 : return OGRERR_FAILURE;
1404 :
1405 17 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1406 0 : return false;
1407 :
1408 17 : auto poLayer = m_apoLayers[iLayer].get();
1409 :
1410 : // Remove from GDB_SystemCatalog
1411 : {
1412 17 : FileGDBTable oTable;
1413 17 : if (!oTable.Open(m_osGDBSystemCatalogFilename.c_str(), true))
1414 0 : return OGRERR_FAILURE;
1415 :
1416 17 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
1417 :
1418 153 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1419 : ++iCurFeat)
1420 : {
1421 153 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1422 153 : if (iCurFeat < 0)
1423 0 : break;
1424 153 : const auto psName = oTable.GetFieldValue(iName);
1425 153 : if (psName && strcmp(psName->String, poLayer->GetName()) == 0)
1426 : {
1427 17 : oTable.DeleteFeature(iCurFeat + 1);
1428 17 : break;
1429 : }
1430 : }
1431 : }
1432 :
1433 : // Remove from GDB_Items
1434 34 : std::string osUUID;
1435 : {
1436 17 : FileGDBTable oTable;
1437 17 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1438 0 : return OGRERR_FAILURE;
1439 :
1440 17 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, OGRERR_FAILURE);
1441 17 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
1442 :
1443 51 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1444 : ++iCurFeat)
1445 : {
1446 37 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1447 37 : if (iCurFeat < 0)
1448 0 : break;
1449 37 : const auto psName = oTable.GetFieldValue(iName);
1450 37 : if (psName && strcmp(psName->String, poLayer->GetName()) == 0)
1451 : {
1452 3 : const auto psUUID = oTable.GetFieldValue(iUUID);
1453 3 : if (psUUID)
1454 : {
1455 3 : osUUID = psUUID->String;
1456 : }
1457 :
1458 3 : oTable.DeleteFeature(iCurFeat + 1);
1459 3 : break;
1460 : }
1461 : }
1462 : }
1463 :
1464 : // Remove from GDB_ItemRelationships
1465 17 : if (!osUUID.empty())
1466 : {
1467 3 : FileGDBTable oTable;
1468 3 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
1469 0 : return OGRERR_FAILURE;
1470 :
1471 3 : FETCH_FIELD_IDX_WITH_RET(iOriginID, "OriginID", FGFT_GUID,
1472 : OGRERR_FAILURE);
1473 3 : FETCH_FIELD_IDX_WITH_RET(iDestID, "DestID", FGFT_GUID, OGRERR_FAILURE);
1474 :
1475 7 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1476 : ++iCurFeat)
1477 : {
1478 4 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1479 4 : if (iCurFeat < 0)
1480 0 : break;
1481 :
1482 4 : const auto psOriginID = oTable.GetFieldValue(iOriginID);
1483 4 : if (psOriginID && psOriginID->String == osUUID)
1484 : {
1485 0 : oTable.DeleteFeature(iCurFeat + 1);
1486 : }
1487 : else
1488 : {
1489 4 : const auto psDestID = oTable.GetFieldValue(iDestID);
1490 4 : if (psDestID && psDestID->String == osUUID)
1491 : {
1492 3 : oTable.DeleteFeature(iCurFeat + 1);
1493 : }
1494 : }
1495 : }
1496 : }
1497 :
1498 : const std::string osDirname =
1499 34 : CPLGetPathSafe(poLayer->GetFilename().c_str());
1500 : const std::string osFilenameBase =
1501 17 : CPLGetBasenameSafe(poLayer->GetFilename().c_str());
1502 :
1503 17 : if (m_bInTransaction)
1504 : {
1505 : auto oIter =
1506 2 : m_oSetLayersCreatedInTransaction.find(m_apoLayers[iLayer].get());
1507 2 : if (oIter != m_oSetLayersCreatedInTransaction.end())
1508 : {
1509 1 : m_oSetLayersCreatedInTransaction.erase(oIter);
1510 : }
1511 : else
1512 : {
1513 1 : poLayer->BeginEmulatedTransaction();
1514 1 : poLayer->Close();
1515 : m_oSetLayersDeletedInTransaction.insert(
1516 1 : std::move(m_apoLayers[iLayer]));
1517 : }
1518 : }
1519 :
1520 : // Delete OGR layer
1521 17 : m_apoLayers.erase(m_apoLayers.begin() + iLayer);
1522 :
1523 : // Remove files associated with the layer
1524 17 : char **papszFiles = VSIReadDir(osDirname.c_str());
1525 408 : for (char **papszIter = papszFiles; papszIter && *papszIter; ++papszIter)
1526 : {
1527 391 : if (STARTS_WITH(*papszIter, osFilenameBase.c_str()))
1528 : {
1529 59 : VSIUnlink(
1530 118 : CPLFormFilenameSafe(osDirname.c_str(), *papszIter, nullptr)
1531 : .c_str());
1532 : }
1533 : }
1534 17 : CSLDestroy(papszFiles);
1535 :
1536 17 : return OGRERR_NONE;
1537 : }
1538 :
1539 : /************************************************************************/
1540 : /* FlushCache() */
1541 : /************************************************************************/
1542 :
1543 873 : CPLErr OGROpenFileGDBDataSource::FlushCache(bool /*bAtClosing*/)
1544 : {
1545 873 : if (eAccess != GA_Update)
1546 562 : return CE_None;
1547 :
1548 311 : CPLErr eErr = CE_None;
1549 899 : for (auto &poLayer : m_apoLayers)
1550 : {
1551 588 : if (poLayer->SyncToDisk() != OGRERR_NONE)
1552 0 : eErr = CE_Failure;
1553 : }
1554 311 : return eErr;
1555 : }
1556 :
1557 : /************************************************************************/
1558 : /* AddFieldDomain() */
1559 : /************************************************************************/
1560 :
1561 6 : bool OGROpenFileGDBDataSource::AddFieldDomain(
1562 : std::unique_ptr<OGRFieldDomain> &&domain, std::string &failureReason)
1563 : {
1564 12 : const std::string domainName(domain->GetName());
1565 6 : if (eAccess != GA_Update)
1566 : {
1567 0 : CPLError(CE_Failure, CPLE_NotSupported,
1568 : "AddFieldDomain() not supported on read-only dataset");
1569 0 : return false;
1570 : }
1571 :
1572 6 : if (GetFieldDomain(domainName) != nullptr)
1573 : {
1574 0 : failureReason = "A domain of identical name already exists";
1575 0 : return false;
1576 : }
1577 :
1578 6 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1579 0 : return false;
1580 :
1581 : std::string osXML =
1582 12 : BuildXMLFieldDomainDef(domain.get(), false, failureReason);
1583 6 : if (osXML.empty())
1584 : {
1585 0 : return false;
1586 : }
1587 :
1588 12 : const std::string osThisGUID = OFGDBGenerateUUID();
1589 :
1590 12 : FileGDBTable oTable;
1591 6 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1592 0 : return false;
1593 :
1594 6 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
1595 6 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1596 6 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1597 6 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
1598 6 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
1599 6 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
1600 6 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
1601 6 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
1602 :
1603 6 : std::vector<OGRField> fields(oTable.GetFieldCount(),
1604 18 : FileGDBField::UNSET_FIELD);
1605 6 : fields[iUUID].String = const_cast<char *>(osThisGUID.c_str());
1606 6 : switch (domain->GetDomainType())
1607 : {
1608 4 : case OFDT_CODED:
1609 4 : fields[iType].String = const_cast<char *>(pszCodedDomainTypeUUID);
1610 4 : break;
1611 :
1612 2 : case OFDT_RANGE:
1613 2 : fields[iType].String = const_cast<char *>(pszRangeDomainTypeUUID);
1614 2 : break;
1615 :
1616 0 : case OFDT_GLOB:
1617 0 : CPLAssert(false);
1618 : break;
1619 : }
1620 6 : fields[iName].String = const_cast<char *>(domainName.c_str());
1621 12 : CPLString osUCName(domainName);
1622 6 : osUCName.toupper();
1623 6 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
1624 6 : fields[iPath].String = const_cast<char *>("");
1625 6 : fields[iURL].String = const_cast<char *>("");
1626 6 : fields[iDefinition].String = const_cast<char *>(osXML.c_str());
1627 6 : fields[iProperties].Integer = 1;
1628 :
1629 6 : if (!(oTable.CreateFeature(fields, nullptr) && oTable.Sync()))
1630 0 : return false;
1631 :
1632 6 : m_oMapFieldDomains[domainName] = std::move(domain);
1633 :
1634 6 : return true;
1635 : }
1636 :
1637 : /************************************************************************/
1638 : /* DeleteFieldDomain() */
1639 : /************************************************************************/
1640 :
1641 2 : bool OGROpenFileGDBDataSource::DeleteFieldDomain(
1642 : const std::string &name, std::string & /*failureReason*/)
1643 : {
1644 2 : if (eAccess != GA_Update)
1645 : {
1646 0 : CPLError(CE_Failure, CPLE_NotSupported,
1647 : "DeleteFieldDomain() not supported on read-only dataset");
1648 0 : return false;
1649 : }
1650 :
1651 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1652 0 : return false;
1653 :
1654 : // Remove object from GDB_Items
1655 4 : std::string osUUID;
1656 : {
1657 2 : FileGDBTable oTable;
1658 2 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1659 0 : return false;
1660 :
1661 2 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
1662 2 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1663 2 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1664 :
1665 14 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1666 : ++iCurFeat)
1667 : {
1668 13 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1669 13 : if (iCurFeat < 0)
1670 0 : break;
1671 13 : const auto psName = oTable.GetFieldValue(iName);
1672 13 : if (psName && psName->String == name)
1673 : {
1674 1 : const auto psType = oTable.GetFieldValue(iType);
1675 1 : if (psType && (EQUAL(psType->String, pszRangeDomainTypeUUID) ||
1676 1 : EQUAL(psType->String, pszCodedDomainTypeUUID)))
1677 : {
1678 1 : const auto psUUID = oTable.GetFieldValue(iUUID);
1679 1 : if (psUUID)
1680 : {
1681 1 : osUUID = psUUID->String;
1682 : }
1683 :
1684 1 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
1685 : {
1686 0 : return false;
1687 : }
1688 1 : break;
1689 : }
1690 : }
1691 : }
1692 : }
1693 2 : if (osUUID.empty())
1694 1 : return false;
1695 :
1696 : // Remove links from layers to domain, into GDB_ItemRelationships
1697 : {
1698 1 : FileGDBTable oTable;
1699 1 : if (!oTable.Open(m_osGDBItemRelationshipsFilename.c_str(), true))
1700 0 : return false;
1701 :
1702 1 : FETCH_FIELD_IDX(iDestID, "DestID", FGFT_GUID);
1703 1 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1704 :
1705 5 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1706 : ++iCurFeat)
1707 : {
1708 4 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1709 4 : if (iCurFeat < 0)
1710 0 : break;
1711 :
1712 4 : const auto psType = oTable.GetFieldValue(iType);
1713 4 : if (psType && EQUAL(psType->String, pszDomainInDatasetUUID))
1714 : {
1715 3 : const auto psDestID = oTable.GetFieldValue(iDestID);
1716 3 : if (psDestID && EQUAL(psDestID->String, osUUID.c_str()))
1717 : {
1718 0 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
1719 : {
1720 0 : return false;
1721 : }
1722 : }
1723 : }
1724 : }
1725 :
1726 1 : if (!oTable.Sync())
1727 : {
1728 0 : return false;
1729 : }
1730 : }
1731 :
1732 1 : m_oMapFieldDomains.erase(name);
1733 :
1734 1 : return true;
1735 : }
1736 :
1737 : /************************************************************************/
1738 : /* UpdateFieldDomain() */
1739 : /************************************************************************/
1740 :
1741 1 : bool OGROpenFileGDBDataSource::UpdateFieldDomain(
1742 : std::unique_ptr<OGRFieldDomain> &&domain, std::string &failureReason)
1743 : {
1744 2 : const std::string domainName(domain->GetName());
1745 1 : if (eAccess != GA_Update)
1746 : {
1747 0 : CPLError(CE_Failure, CPLE_NotSupported,
1748 : "UpdateFieldDomain() not supported on read-only dataset");
1749 0 : return false;
1750 : }
1751 :
1752 1 : if (GetFieldDomain(domainName) == nullptr)
1753 : {
1754 0 : failureReason = "The domain should already exist to be updated";
1755 0 : return false;
1756 : }
1757 :
1758 1 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1759 0 : return false;
1760 :
1761 : std::string osXML =
1762 2 : BuildXMLFieldDomainDef(domain.get(), false, failureReason);
1763 1 : if (osXML.empty())
1764 : {
1765 0 : return false;
1766 : }
1767 :
1768 2 : FileGDBTable oTable;
1769 1 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
1770 0 : return false;
1771 :
1772 1 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
1773 1 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
1774 1 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
1775 :
1776 1 : bool bMatchFound = false;
1777 3 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
1778 : ++iCurFeat)
1779 : {
1780 3 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
1781 3 : if (iCurFeat < 0)
1782 0 : break;
1783 3 : const auto psName = oTable.GetFieldValue(iName);
1784 3 : if (psName && psName->String == domainName)
1785 : {
1786 1 : const auto psType = oTable.GetFieldValue(iType);
1787 1 : if (psType && (EQUAL(psType->String, pszRangeDomainTypeUUID) ||
1788 0 : EQUAL(psType->String, pszCodedDomainTypeUUID)))
1789 : {
1790 1 : auto asFields = oTable.GetAllFieldValues();
1791 :
1792 2 : if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
1793 1 : !OGR_RawField_IsUnset(&asFields[iDefinition]))
1794 : {
1795 1 : CPLFree(asFields[iDefinition].String);
1796 : }
1797 1 : asFields[iDefinition].String = CPLStrdup(osXML.c_str());
1798 :
1799 1 : const char *pszNewTypeUUID = "";
1800 1 : CPL_IGNORE_RET_VAL(pszNewTypeUUID); // Make CSA happy
1801 1 : switch (domain->GetDomainType())
1802 : {
1803 0 : case OFDT_CODED:
1804 0 : pszNewTypeUUID = pszCodedDomainTypeUUID;
1805 0 : break;
1806 :
1807 1 : case OFDT_RANGE:
1808 1 : pszNewTypeUUID = pszRangeDomainTypeUUID;
1809 1 : break;
1810 :
1811 0 : case OFDT_GLOB:
1812 0 : CPLAssert(false);
1813 : break;
1814 : }
1815 :
1816 2 : if (!OGR_RawField_IsNull(&asFields[iType]) &&
1817 1 : !OGR_RawField_IsUnset(&asFields[iType]))
1818 : {
1819 1 : CPLFree(asFields[iType].String);
1820 : }
1821 1 : asFields[iType].String = CPLStrdup(pszNewTypeUUID);
1822 :
1823 : bool bRet =
1824 1 : oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr);
1825 1 : oTable.FreeAllFieldValues(asFields);
1826 1 : if (!bRet)
1827 0 : return false;
1828 1 : bMatchFound = true;
1829 1 : break;
1830 : }
1831 : }
1832 :
1833 2 : if (!oTable.Sync())
1834 : {
1835 0 : return false;
1836 : }
1837 : }
1838 :
1839 1 : if (!bMatchFound)
1840 0 : return false;
1841 :
1842 1 : m_oMapFieldDomains[domainName] = std::move(domain);
1843 :
1844 1 : return true;
1845 : }
1846 :
1847 : /************************************************************************/
1848 : /* GetRelationshipNames() */
1849 : /************************************************************************/
1850 :
1851 24 : std::vector<std::string> OGROpenFileGDBDataSource::GetRelationshipNames(
1852 : CPL_UNUSED CSLConstList papszOptions) const
1853 :
1854 : {
1855 24 : std::vector<std::string> oasNames;
1856 24 : oasNames.reserve(m_osMapRelationships.size());
1857 105 : for (auto it = m_osMapRelationships.begin();
1858 186 : it != m_osMapRelationships.end(); ++it)
1859 : {
1860 81 : oasNames.emplace_back(it->first);
1861 : }
1862 24 : return oasNames;
1863 : }
1864 :
1865 : /************************************************************************/
1866 : /* GetRelationship() */
1867 : /************************************************************************/
1868 :
1869 : const GDALRelationship *
1870 79 : OGROpenFileGDBDataSource::GetRelationship(const std::string &name) const
1871 :
1872 : {
1873 79 : auto it = m_osMapRelationships.find(name);
1874 79 : if (it == m_osMapRelationships.end())
1875 29 : return nullptr;
1876 :
1877 50 : return it->second.get();
1878 : }
1879 :
1880 : /************************************************************************/
1881 : /* AddRelationship() */
1882 : /************************************************************************/
1883 :
1884 17 : bool OGROpenFileGDBDataSource::AddRelationship(
1885 : std::unique_ptr<GDALRelationship> &&relationship,
1886 : std::string &failureReason)
1887 : {
1888 17 : if (FlushCache(false) != CE_None)
1889 0 : return false;
1890 :
1891 34 : const std::string relationshipName(relationship->GetName());
1892 17 : if (eAccess != GA_Update)
1893 : {
1894 0 : CPLError(CE_Failure, CPLE_NotSupported,
1895 : "AddRelationship() not supported on read-only dataset");
1896 0 : return false;
1897 : }
1898 :
1899 17 : if (GetRelationship(relationshipName) != nullptr)
1900 : {
1901 1 : failureReason = "A relationship of identical name already exists";
1902 1 : return false;
1903 : }
1904 :
1905 16 : if (relationship->GetCardinality() ==
1906 : GDALRelationshipCardinality::GRC_MANY_TO_ONE)
1907 : {
1908 0 : failureReason = "Many to one relationships are not supported";
1909 0 : return false;
1910 : }
1911 16 : else if (relationship->GetCardinality() ==
1912 5 : GDALRelationshipCardinality::GRC_MANY_TO_MANY &&
1913 20 : !relationship->GetMappingTableName().empty() &&
1914 4 : relationship->GetName() != relationship->GetMappingTableName())
1915 : {
1916 : failureReason = "Mapping table name must match relationship name for "
1917 1 : "many-to-many relationships";
1918 1 : return false;
1919 : }
1920 :
1921 15 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
1922 0 : return false;
1923 :
1924 30 : const std::string osThisGUID = OFGDBGenerateUUID();
1925 :
1926 30 : FileGDBTable oTable;
1927 30 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) ||
1928 15 : oTable.GetTotalRecordCount() >= INT32_MAX)
1929 0 : return false;
1930 :
1931 : // hopefully this just needs to be a unique value. Seems to autoincrement
1932 : // when created from ArcMap at least!
1933 15 : const int iDsId = static_cast<int>(oTable.GetTotalRecordCount() + 1);
1934 :
1935 30 : std::string osMappingTableOidName;
1936 15 : if (relationship->GetCardinality() ==
1937 : GDALRelationshipCardinality::GRC_MANY_TO_MANY)
1938 : {
1939 4 : if (!relationship->GetMappingTableName().empty())
1940 : {
1941 : auto poLayer =
1942 3 : GetLayerByName(relationship->GetMappingTableName().c_str());
1943 3 : if (poLayer)
1944 : {
1945 3 : osMappingTableOidName = poLayer->GetFIDColumn();
1946 : }
1947 : }
1948 : else
1949 : {
1950 : // auto create mapping table
1951 1 : CPLStringList aosOptions;
1952 1 : aosOptions.SetNameValue("FID", "RID");
1953 1 : OGRLayer *poMappingTable = ICreateLayer(
1954 1 : relationship->GetName().c_str(), nullptr, aosOptions.List());
1955 1 : if (!poMappingTable)
1956 : {
1957 0 : failureReason = "Could not create mapping table " +
1958 0 : relationship->GetMappingTableName();
1959 0 : return false;
1960 : }
1961 :
1962 1 : OGRFieldDefn oOriginFkFieldDefn("origin_fk", OFTString);
1963 1 : if (poMappingTable->CreateField(&oOriginFkFieldDefn) != OGRERR_NONE)
1964 : {
1965 : failureReason =
1966 0 : "Could not create origin_fk field in mapping table " +
1967 0 : relationship->GetMappingTableName();
1968 0 : return false;
1969 : }
1970 :
1971 1 : OGRFieldDefn oDestinationFkFieldDefn("destination_fk", OFTString);
1972 1 : if (poMappingTable->CreateField(&oDestinationFkFieldDefn) !=
1973 : OGRERR_NONE)
1974 : {
1975 : failureReason =
1976 0 : "Could not create destination_fk field in mapping table " +
1977 0 : relationship->GetMappingTableName();
1978 0 : return false;
1979 : }
1980 :
1981 1 : osMappingTableOidName = "RID";
1982 1 : relationship->SetMappingTableName(relationship->GetName());
1983 2 : relationship->SetLeftMappingTableFields({"origin_fk"});
1984 2 : relationship->SetRightMappingTableFields({"destination_fk"});
1985 : }
1986 : }
1987 :
1988 : std::string osXML = BuildXMLRelationshipDef(
1989 30 : relationship.get(), iDsId, osMappingTableOidName, failureReason);
1990 15 : if (osXML.empty())
1991 : {
1992 0 : return false;
1993 : }
1994 :
1995 : std::string osItemInfoXML =
1996 30 : BuildXMLRelationshipItemInfo(relationship.get(), failureReason);
1997 15 : if (osItemInfoXML.empty())
1998 : {
1999 0 : return false;
2000 : }
2001 :
2002 : std::string osDocumentationXML =
2003 30 : BuildXMLRelationshipDocumentation(relationship.get(), failureReason);
2004 15 : if (osDocumentationXML.empty())
2005 : {
2006 0 : return false;
2007 : }
2008 :
2009 30 : std::string osOriginUUID;
2010 15 : if (!FindUUIDFromName(relationship->GetLeftTableName(), osOriginUUID))
2011 : {
2012 2 : failureReason = ("Left table " + relationship->GetLeftTableName() +
2013 : " is not an existing layer in the dataset")
2014 1 : .c_str();
2015 1 : return false;
2016 : }
2017 28 : std::string osDestinationUUID;
2018 14 : if (!FindUUIDFromName(relationship->GetRightTableName(), osDestinationUUID))
2019 : {
2020 0 : failureReason = ("Right table " + relationship->GetRightTableName() +
2021 : " is not an existing layer in the dataset")
2022 0 : .c_str();
2023 0 : return false;
2024 : }
2025 :
2026 14 : FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
2027 14 : FETCH_FIELD_IDX(iType, "Type", FGFT_GUID);
2028 14 : FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
2029 14 : FETCH_FIELD_IDX(iPhysicalName, "PhysicalName", FGFT_STRING);
2030 14 : FETCH_FIELD_IDX(iPath, "Path", FGFT_STRING);
2031 14 : FETCH_FIELD_IDX(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32);
2032 14 : FETCH_FIELD_IDX(iDatasetSubtype2, "DatasetSubtype2", FGFT_INT32);
2033 14 : FETCH_FIELD_IDX(iURL, "URL", FGFT_STRING);
2034 14 : FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
2035 14 : FETCH_FIELD_IDX(iDocumentation, "Documentation", FGFT_XML);
2036 14 : FETCH_FIELD_IDX(iItemInfo, "ItemInfo", FGFT_XML);
2037 14 : FETCH_FIELD_IDX(iProperties, "Properties", FGFT_INT32);
2038 :
2039 14 : std::vector<OGRField> fields(oTable.GetFieldCount(),
2040 42 : FileGDBField::UNSET_FIELD);
2041 14 : fields[iUUID].String = const_cast<char *>(osThisGUID.c_str());
2042 14 : fields[iType].String = const_cast<char *>(pszRelationshipTypeUUID);
2043 14 : fields[iName].String = const_cast<char *>(relationshipName.c_str());
2044 28 : CPLString osUCName(relationshipName);
2045 14 : osUCName.toupper();
2046 14 : fields[iPhysicalName].String = const_cast<char *>(osUCName.c_str());
2047 28 : const std::string osPath = "\\" + relationshipName;
2048 14 : fields[iPath].String = const_cast<char *>(osPath.c_str());
2049 14 : switch (relationship->GetCardinality())
2050 : {
2051 7 : case GDALRelationshipCardinality::GRC_ONE_TO_ONE:
2052 7 : fields[iDatasetSubtype1].Integer = 1;
2053 7 : break;
2054 3 : case GDALRelationshipCardinality::GRC_ONE_TO_MANY:
2055 3 : fields[iDatasetSubtype1].Integer = 2;
2056 3 : break;
2057 4 : case GDALRelationshipCardinality::GRC_MANY_TO_MANY:
2058 4 : fields[iDatasetSubtype1].Integer = 3;
2059 4 : break;
2060 0 : case GDALRelationshipCardinality::GRC_MANY_TO_ONE:
2061 : // unreachable
2062 0 : break;
2063 : }
2064 14 : fields[iDatasetSubtype2].Integer = 0;
2065 14 : fields[iURL].String = const_cast<char *>("");
2066 14 : fields[iDefinition].String = const_cast<char *>(osXML.c_str());
2067 14 : fields[iDocumentation].String =
2068 14 : const_cast<char *>(osDocumentationXML.c_str());
2069 14 : fields[iItemInfo].String = const_cast<char *>(osItemInfoXML.c_str());
2070 14 : fields[iProperties].Integer = 1;
2071 :
2072 14 : if (!(oTable.CreateFeature(fields, nullptr) && oTable.Sync()))
2073 0 : return false;
2074 :
2075 14 : if (!RegisterRelationshipInItemRelationships(osThisGUID, osOriginUUID,
2076 : osDestinationUUID))
2077 0 : return false;
2078 :
2079 14 : m_osMapRelationships[relationshipName] = std::move(relationship);
2080 :
2081 14 : return true;
2082 : }
2083 :
2084 : /************************************************************************/
2085 : /* DeleteRelationship() */
2086 : /************************************************************************/
2087 :
2088 2 : bool OGROpenFileGDBDataSource::DeleteRelationship(const std::string &name,
2089 : std::string &failureReason)
2090 : {
2091 2 : if (eAccess != GA_Update)
2092 : {
2093 0 : CPLError(CE_Failure, CPLE_NotSupported,
2094 : "DeleteRelationship() not supported on read-only dataset");
2095 0 : return false;
2096 : }
2097 :
2098 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
2099 0 : return false;
2100 :
2101 : // Remove from GDB_Items
2102 4 : std::string osUUID;
2103 : {
2104 2 : FileGDBTable oTable;
2105 2 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true))
2106 0 : return false;
2107 :
2108 2 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, false);
2109 2 : FETCH_FIELD_IDX_WITH_RET(iType, "Type", FGFT_GUID, false);
2110 2 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, false);
2111 :
2112 36 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
2113 : ++iCurFeat)
2114 : {
2115 34 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
2116 34 : if (iCurFeat < 0)
2117 0 : break;
2118 :
2119 34 : const auto psType = oTable.GetFieldValue(iType);
2120 34 : if (!psType || !EQUAL(psType->String, pszRelationshipTypeUUID))
2121 : {
2122 26 : continue;
2123 : }
2124 :
2125 8 : const auto psName = oTable.GetFieldValue(iName);
2126 8 : if (psName && strcmp(psName->String, name.c_str()) != 0)
2127 : {
2128 7 : continue;
2129 : }
2130 :
2131 1 : const auto psUUID = oTable.GetFieldValue(iUUID);
2132 1 : if (psUUID)
2133 : {
2134 1 : osUUID = psUUID->String;
2135 1 : if (!(oTable.DeleteFeature(iCurFeat + 1) && oTable.Sync()))
2136 : {
2137 : failureReason =
2138 0 : "Could not delete relationship from GDB_Items table";
2139 0 : return false;
2140 : }
2141 : }
2142 : }
2143 : }
2144 :
2145 2 : if (osUUID.empty())
2146 : {
2147 1 : failureReason = "Could not find relationship with name " + name;
2148 1 : return false;
2149 : }
2150 :
2151 1 : if (!RemoveRelationshipFromItemRelationships(osUUID))
2152 : {
2153 : failureReason =
2154 0 : "Could not remove relationship from GDB_ItemRelationships";
2155 0 : return false;
2156 : }
2157 :
2158 1 : m_osMapRelationships.erase(name);
2159 1 : return true;
2160 : }
2161 :
2162 : /************************************************************************/
2163 : /* UpdateRelationship() */
2164 : /************************************************************************/
2165 :
2166 3 : bool OGROpenFileGDBDataSource::UpdateRelationship(
2167 : std::unique_ptr<GDALRelationship> &&relationship,
2168 : std::string &failureReason)
2169 : {
2170 6 : const std::string relationshipName(relationship->GetName());
2171 3 : if (eAccess != GA_Update)
2172 : {
2173 0 : CPLError(CE_Failure, CPLE_NotSupported,
2174 : "UpdateRelationship() not supported on read-only dataset");
2175 0 : return false;
2176 : }
2177 :
2178 3 : if (GetRelationship(relationshipName) == nullptr)
2179 : {
2180 1 : failureReason = "The relationship should already exist to be updated";
2181 1 : return false;
2182 : }
2183 :
2184 2 : if (relationship->GetCardinality() ==
2185 : GDALRelationshipCardinality::GRC_MANY_TO_ONE)
2186 : {
2187 0 : failureReason = "Many to one relationships are not supported";
2188 0 : return false;
2189 : }
2190 :
2191 2 : if (m_bInTransaction && !BackupSystemTablesForTransaction())
2192 0 : return false;
2193 :
2194 4 : std::string osOriginUUID;
2195 2 : if (!FindUUIDFromName(relationship->GetLeftTableName(), osOriginUUID))
2196 : {
2197 0 : failureReason = ("Left table " + relationship->GetLeftTableName() +
2198 : " is not an existing layer in the dataset")
2199 0 : .c_str();
2200 0 : return false;
2201 : }
2202 4 : std::string osDestinationUUID;
2203 2 : if (!FindUUIDFromName(relationship->GetRightTableName(), osDestinationUUID))
2204 : {
2205 0 : failureReason = ("Right table " + relationship->GetRightTableName() +
2206 : " is not an existing layer in the dataset")
2207 0 : .c_str();
2208 0 : return false;
2209 : }
2210 :
2211 4 : FileGDBTable oTable;
2212 4 : if (!oTable.Open(m_osGDBItemsFilename.c_str(), true) ||
2213 2 : oTable.GetTotalRecordCount() >= INT32_MAX)
2214 : {
2215 0 : return false;
2216 : }
2217 :
2218 : // hopefully this just needs to be a unique value. Seems to autoincrement
2219 : // when created from ArcMap at least!
2220 2 : const int iDsId = static_cast<int>(oTable.GetTotalRecordCount()) + 1;
2221 :
2222 4 : std::string osMappingTableOidName;
2223 2 : if (relationship->GetCardinality() ==
2224 : GDALRelationshipCardinality::GRC_MANY_TO_MANY)
2225 : {
2226 0 : if (!relationship->GetMappingTableName().empty())
2227 : {
2228 : auto poLayer =
2229 0 : GetLayerByName(relationship->GetMappingTableName().c_str());
2230 0 : if (poLayer)
2231 : {
2232 0 : osMappingTableOidName = poLayer->GetFIDColumn();
2233 : }
2234 : }
2235 0 : if (osMappingTableOidName.empty())
2236 : {
2237 0 : failureReason = "Relationship mapping table does not exist";
2238 0 : return false;
2239 : }
2240 : }
2241 :
2242 : std::string osXML = BuildXMLRelationshipDef(
2243 4 : relationship.get(), iDsId, osMappingTableOidName, failureReason);
2244 2 : if (osXML.empty())
2245 : {
2246 0 : return false;
2247 : }
2248 :
2249 2 : FETCH_FIELD_IDX_WITH_RET(iUUID, "UUID", FGFT_GLOBALID, false);
2250 2 : FETCH_FIELD_IDX_WITH_RET(iType, "Type", FGFT_GUID, false);
2251 2 : FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, false);
2252 2 : FETCH_FIELD_IDX_WITH_RET(iDefinition, "Definition", FGFT_XML, false);
2253 2 : FETCH_FIELD_IDX_WITH_RET(iDatasetSubtype1, "DatasetSubtype1", FGFT_INT32,
2254 : false);
2255 :
2256 2 : bool bMatchFound = false;
2257 4 : std::string osUUID;
2258 16 : for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
2259 : ++iCurFeat)
2260 : {
2261 16 : iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
2262 16 : if (iCurFeat < 0)
2263 0 : break;
2264 16 : const auto psName = oTable.GetFieldValue(iName);
2265 16 : if (psName && psName->String == relationshipName)
2266 : {
2267 2 : const auto psType = oTable.GetFieldValue(iType);
2268 2 : if (psType && EQUAL(psType->String, pszRelationshipTypeUUID))
2269 : {
2270 2 : const auto psUUID = oTable.GetFieldValue(iUUID);
2271 2 : if (psUUID)
2272 : {
2273 2 : osUUID = psUUID->String;
2274 : }
2275 :
2276 2 : auto asFields = oTable.GetAllFieldValues();
2277 :
2278 4 : if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
2279 2 : !OGR_RawField_IsUnset(&asFields[iDefinition]))
2280 : {
2281 2 : CPLFree(asFields[iDefinition].String);
2282 : }
2283 2 : asFields[iDefinition].String = CPLStrdup(osXML.c_str());
2284 :
2285 2 : switch (relationship->GetCardinality())
2286 : {
2287 0 : case GDALRelationshipCardinality::GRC_ONE_TO_ONE:
2288 0 : asFields[iDatasetSubtype1].Integer = 1;
2289 0 : break;
2290 2 : case GDALRelationshipCardinality::GRC_ONE_TO_MANY:
2291 2 : asFields[iDatasetSubtype1].Integer = 2;
2292 2 : break;
2293 0 : case GDALRelationshipCardinality::GRC_MANY_TO_MANY:
2294 0 : asFields[iDatasetSubtype1].Integer = 3;
2295 0 : break;
2296 0 : case GDALRelationshipCardinality::GRC_MANY_TO_ONE:
2297 : // unreachable
2298 0 : break;
2299 : }
2300 :
2301 : bool bRet =
2302 2 : oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr);
2303 2 : oTable.FreeAllFieldValues(asFields);
2304 2 : if (!bRet)
2305 0 : return false;
2306 2 : bMatchFound = true;
2307 2 : break;
2308 : }
2309 : }
2310 :
2311 14 : if (!oTable.Sync())
2312 : {
2313 0 : return false;
2314 : }
2315 : }
2316 :
2317 2 : if (!bMatchFound)
2318 0 : return false;
2319 :
2320 : // First delete all existing item relationships for the item, and then we'll
2321 : // rebuild them again.
2322 2 : if (!RemoveRelationshipFromItemRelationships(osUUID))
2323 : {
2324 : failureReason =
2325 0 : "Could not remove relationship from GDB_ItemRelationships";
2326 0 : return false;
2327 : }
2328 2 : if (!RegisterRelationshipInItemRelationships(osUUID, osOriginUUID,
2329 : osDestinationUUID))
2330 : {
2331 : failureReason =
2332 0 : "Could not register relationship in GDB_ItemRelationships";
2333 0 : return false;
2334 : }
2335 :
2336 2 : m_osMapRelationships[relationshipName] = std::move(relationship);
2337 :
2338 2 : return true;
2339 : }
2340 :
2341 : /************************************************************************/
2342 : /* StartTransaction() */
2343 : /************************************************************************/
2344 :
2345 16 : OGRErr OGROpenFileGDBDataSource::StartTransaction(int bForce)
2346 : {
2347 16 : if (!bForce)
2348 : {
2349 0 : CPLError(CE_Failure, CPLE_NotSupported,
2350 : "Transactions only supported in forced mode");
2351 0 : return OGRERR_UNSUPPORTED_OPERATION;
2352 : }
2353 :
2354 16 : if (eAccess != GA_Update)
2355 1 : return OGRERR_FAILURE;
2356 :
2357 15 : if (m_bInTransaction)
2358 : {
2359 1 : CPLError(CE_Failure, CPLE_AppDefined,
2360 : "Transaction is already in progress");
2361 1 : return OGRERR_FAILURE;
2362 : }
2363 :
2364 28 : m_osTransactionBackupDirname = CPLFormFilenameSafe(
2365 14 : m_osDirName.c_str(), ".ogrtransaction_backup", nullptr);
2366 : VSIStatBufL sStat;
2367 14 : if (VSIStatL(m_osTransactionBackupDirname.c_str(), &sStat) == 0)
2368 : {
2369 1 : CPLError(CE_Failure, CPLE_AppDefined,
2370 : "A previous backup directory %s already exists, which means "
2371 : "that a previous transaction was not cleanly committed or "
2372 : "rolled back.\n"
2373 : "Either manually restore the previous state from that "
2374 : "directory or remove it, before creating a new transaction.",
2375 : m_osTransactionBackupDirname.c_str());
2376 1 : return OGRERR_FAILURE;
2377 : }
2378 13 : else if (VSIMkdir(m_osTransactionBackupDirname.c_str(), 0755) != 0)
2379 : {
2380 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot create directory %s",
2381 : m_osTransactionBackupDirname.c_str());
2382 0 : return OGRERR_FAILURE;
2383 : }
2384 :
2385 13 : m_bInTransaction = true;
2386 13 : return OGRERR_NONE;
2387 : }
2388 :
2389 : /************************************************************************/
2390 : /* BackupSystemTablesForTransaction() */
2391 : /************************************************************************/
2392 :
2393 10 : bool OGROpenFileGDBDataSource::BackupSystemTablesForTransaction()
2394 : {
2395 10 : if (m_bSystemTablesBackedup)
2396 5 : return true;
2397 :
2398 5 : char **papszFiles = VSIReadDir(m_osDirName.c_str());
2399 106 : for (char **papszIter = papszFiles;
2400 106 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2401 : {
2402 101 : const std::string osBasename = CPLGetBasenameSafe(*papszIter);
2403 101 : if (osBasename.size() == strlen("a00000001") &&
2404 179 : osBasename.compare(0, 8, "a0000000") == 0 && osBasename[8] >= '1' &&
2405 78 : osBasename[8] <= '8')
2406 : {
2407 : const std::string osDestFilename = CPLFormFilenameSafe(
2408 72 : m_osTransactionBackupDirname.c_str(), *papszIter, nullptr);
2409 : const std::string osSourceFilename =
2410 72 : CPLFormFilenameSafe(m_osDirName.c_str(), *papszIter, nullptr);
2411 72 : if (CPLCopyFile(osDestFilename.c_str(), osSourceFilename.c_str()) !=
2412 : 0)
2413 : {
2414 0 : CSLDestroy(papszFiles);
2415 0 : return false;
2416 : }
2417 : }
2418 : }
2419 :
2420 5 : CSLDestroy(papszFiles);
2421 5 : m_bSystemTablesBackedup = true;
2422 5 : return true;
2423 : }
2424 :
2425 : /************************************************************************/
2426 : /* CommitTransaction() */
2427 : /************************************************************************/
2428 :
2429 5 : OGRErr OGROpenFileGDBDataSource::CommitTransaction()
2430 : {
2431 5 : if (!m_bInTransaction)
2432 : {
2433 1 : CPLError(CE_Failure, CPLE_AppDefined, "No transaction in progress");
2434 1 : return OGRERR_FAILURE;
2435 : }
2436 :
2437 7 : for (auto &poLayer : m_apoLayers)
2438 3 : poLayer->CommitEmulatedTransaction();
2439 :
2440 4 : VSIRmdirRecursive(m_osTransactionBackupDirname.c_str());
2441 :
2442 4 : m_bInTransaction = false;
2443 4 : m_bSystemTablesBackedup = false;
2444 4 : m_oSetLayersCreatedInTransaction.clear();
2445 4 : m_oSetLayersDeletedInTransaction.clear();
2446 :
2447 4 : return OGRERR_NONE;
2448 : }
2449 :
2450 : /************************************************************************/
2451 : /* RollbackTransaction() */
2452 : /************************************************************************/
2453 :
2454 11 : OGRErr OGROpenFileGDBDataSource::RollbackTransaction()
2455 : {
2456 11 : if (!m_bInTransaction)
2457 : {
2458 1 : CPLError(CE_Failure, CPLE_AppDefined, "No transaction in progress");
2459 1 : return OGRERR_FAILURE;
2460 : }
2461 :
2462 10 : OGRErr eErr = OGRERR_NONE;
2463 :
2464 : // Restore system tables
2465 : {
2466 10 : char **papszFiles = VSIReadDir(m_osTransactionBackupDirname.c_str());
2467 10 : if (papszFiles == nullptr)
2468 : {
2469 2 : CPLError(CE_Failure, CPLE_AppDefined,
2470 : "Backup directory %s no longer found! Original database "
2471 : "cannot be restored",
2472 : m_osTransactionBackupDirname.c_str());
2473 2 : return OGRERR_FAILURE;
2474 : }
2475 78 : for (char **papszIter = papszFiles;
2476 78 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2477 : {
2478 140 : const std::string osBasename = CPLGetBasenameSafe(*papszIter);
2479 70 : if (osBasename.size() == strlen("a00000001") &&
2480 56 : osBasename.compare(0, 8, "a0000000") == 0 &&
2481 126 : osBasename[8] >= '1' && osBasename[8] <= '8')
2482 : {
2483 : const std::string osDestFilename = CPLFormFilenameSafe(
2484 88 : m_osDirName.c_str(), *papszIter, nullptr);
2485 : const std::string osSourceFilename = CPLFormFilenameSafe(
2486 88 : m_osTransactionBackupDirname.c_str(), *papszIter, nullptr);
2487 44 : if (CPLCopyFile(osDestFilename.c_str(),
2488 44 : osSourceFilename.c_str()) != 0)
2489 : {
2490 0 : eErr = OGRERR_FAILURE;
2491 : }
2492 : }
2493 : }
2494 8 : CSLDestroy(papszFiles);
2495 : }
2496 :
2497 : // Restore layers in their original state
2498 13 : for (auto &poLayer : m_apoLayers)
2499 5 : poLayer->RollbackEmulatedTransaction();
2500 9 : for (auto &poLayer : m_oSetLayersDeletedInTransaction)
2501 1 : poLayer->RollbackEmulatedTransaction();
2502 :
2503 : // Remove layers created during transaction
2504 9 : for (auto poLayer : m_oSetLayersCreatedInTransaction)
2505 : {
2506 : const std::string osThisBasename =
2507 2 : CPLGetBasenameSafe(poLayer->GetFilename().c_str());
2508 1 : poLayer->Close();
2509 :
2510 1 : char **papszFiles = VSIReadDir(m_osDirName.c_str());
2511 23 : for (char **papszIter = papszFiles;
2512 23 : papszIter != nullptr && *papszIter != nullptr; ++papszIter)
2513 : {
2514 44 : const std::string osBasename = CPLGetBasenameSafe(*papszIter);
2515 22 : if (osBasename == osThisBasename)
2516 : {
2517 : const std::string osDestFilename = CPLFormFilenameSafe(
2518 6 : m_osDirName.c_str(), *papszIter, nullptr);
2519 3 : VSIUnlink(osDestFilename.c_str());
2520 : }
2521 : }
2522 1 : CSLDestroy(papszFiles);
2523 : }
2524 :
2525 8 : if (eErr == OGRERR_NONE)
2526 : {
2527 8 : if (VSIRmdirRecursive(m_osTransactionBackupDirname.c_str()) != 0)
2528 : {
2529 0 : CPLError(
2530 : CE_Warning, CPLE_AppDefined,
2531 : "Backup directory %s could not be destroyed. But original "
2532 : "dataset "
2533 : "should have been properly restored. You will need to manually "
2534 : "remove the backup directory.",
2535 : m_osTransactionBackupDirname.c_str());
2536 : }
2537 : }
2538 : else
2539 : {
2540 0 : CPLError(CE_Failure, CPLE_AppDefined,
2541 : "Backup directory %s could not be properly restored onto "
2542 : "live database. Corruption is likely!",
2543 : m_osTransactionBackupDirname.c_str());
2544 : }
2545 :
2546 8 : m_bInTransaction = false;
2547 8 : m_bSystemTablesBackedup = false;
2548 8 : m_oSetLayersCreatedInTransaction.clear();
2549 8 : m_oSetLayersDeletedInTransaction.clear();
2550 :
2551 8 : return eErr;
2552 : }
|