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