Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: Implements FileGDB OGR layer.
5 : * Author: Ragi Yaser Burhum, ragi@burhum.com
6 : * Paul Ramsey, pramsey at cleverelephant.ca
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2010, Ragi Yaser Burhum
10 : * Copyright (c) 2011, Paul Ramsey <pramsey at cleverelephant.ca>
11 : * Copyright (c) 2011-2014, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * SPDX-License-Identifier: MIT
14 : ****************************************************************************/
15 :
16 : #include <cassert>
17 : #include <cmath>
18 :
19 : #include "ogr_fgdb.h"
20 : #include "ogrpgeogeometry.h"
21 : #include "cpl_conv.h"
22 : #include "cpl_string.h"
23 : #include "FGdbUtils.h"
24 : #include "cpl_minixml.h" // the only way right now to extract schema information
25 : #include "filegdb_gdbtoogrfieldtype.h"
26 : #include "filegdb_fielddomain.h"
27 : #include "filegdb_coordprec_read.h"
28 : #include "filegdb_coordprec_write.h"
29 :
30 : // See https://github.com/Esri/file-geodatabase-api/issues/46
31 : // On certain FileGDB datasets with binary fields, iterating over a result set
32 : // where the binary field is requested crashes in EnumRows::Next() at the
33 : // second iteration.
34 : // The workaround consists in iterating only over OBJECTID in the main loop,
35 : // and requesting each feature in a separate request.
36 : #define WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
37 :
38 : using std::string;
39 : using std::wstring;
40 :
41 : /************************************************************************/
42 : /* FGdbBaseLayer() */
43 : /************************************************************************/
44 410 : FGdbBaseLayer::FGdbBaseLayer()
45 : : m_pFeatureDefn(nullptr), m_pSRS(nullptr), m_pEnumRows(nullptr),
46 410 : m_suppressColumnMappingError(false), m_forceMulti(false)
47 : {
48 410 : }
49 :
50 : /************************************************************************/
51 : /* ~FGdbBaseLayer() */
52 : /************************************************************************/
53 410 : FGdbBaseLayer::~FGdbBaseLayer()
54 : {
55 410 : if (m_pFeatureDefn)
56 : {
57 410 : m_pFeatureDefn->Release();
58 410 : m_pFeatureDefn = nullptr;
59 : }
60 :
61 410 : FGdbBaseLayer::CloseGDBObjects();
62 :
63 410 : if (m_pSRS)
64 : {
65 354 : m_pSRS->Release();
66 354 : m_pSRS = nullptr;
67 : }
68 410 : }
69 :
70 : /************************************************************************/
71 : /* CloseGDBObjects() */
72 : /************************************************************************/
73 :
74 1225 : void FGdbBaseLayer::CloseGDBObjects()
75 : {
76 1225 : if (m_pEnumRows)
77 : {
78 410 : delete m_pEnumRows;
79 410 : m_pEnumRows = nullptr;
80 : }
81 1225 : }
82 :
83 : /************************************************************************/
84 : /* GetNextFeature() */
85 : /************************************************************************/
86 :
87 272 : OGRFeature *FGdbBaseLayer::GetNextFeature()
88 : {
89 : while (true) // want to skip errors
90 : {
91 272 : if (m_pEnumRows == nullptr)
92 272 : return nullptr;
93 :
94 : long hr;
95 :
96 272 : Row row;
97 :
98 272 : if (FAILED(hr = m_pEnumRows->Next(row)))
99 : {
100 1 : GDBErr(hr, "Failed fetching features");
101 1 : return nullptr;
102 : }
103 :
104 271 : if (hr != S_OK)
105 : {
106 : // It's OK, we are done fetching - failure is caught by FAILED macro
107 29 : return nullptr;
108 : }
109 :
110 242 : OGRFeature *pOGRFeature = nullptr;
111 :
112 242 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature) || !pOGRFeature)
113 : {
114 0 : int32 oid = -1;
115 0 : CPL_IGNORE_RET_VAL(row.GetOID(oid));
116 :
117 0 : GDBErr(hr,
118 : CPLSPrintf("Failed translating FGDB row [%d] to OGR Feature",
119 : oid));
120 :
121 : // return NULL;
122 0 : continue; // skip feature
123 : }
124 :
125 288 : if ((m_poFilterGeom == nullptr ||
126 46 : FilterGeometry(pOGRFeature->GetGeometryRef())))
127 : {
128 242 : return pOGRFeature;
129 : }
130 0 : delete pOGRFeature;
131 0 : }
132 : }
133 :
134 : /************************************************************************/
135 : /* FGdbLayer() */
136 : /************************************************************************/
137 408 : FGdbLayer::FGdbLayer()
138 : : m_pDS(nullptr), m_pTable(nullptr), m_wstrSubfields(L"*"),
139 408 : m_bFilterDirty(true), m_bLaunderReservedKeywords(true)
140 : {
141 408 : m_bBulkLoadAllowed = -1; /* uninitialized */
142 408 : m_bBulkLoadInProgress = FALSE;
143 408 : m_pEnumRows = new EnumRows;
144 :
145 : #ifdef EXTENT_WORKAROUND
146 408 : m_bLayerEnvelopeValid = false;
147 408 : m_bLayerJustCreated = false;
148 : #endif
149 408 : m_papszOptions = nullptr;
150 408 : m_bCreateMultipatch = FALSE;
151 408 : m_nResyncThreshold =
152 408 : atoi(CPLGetConfigOption("FGDB_RESYNC_THRESHOLD", "1000000"));
153 408 : m_bSymlinkFlag = FALSE;
154 408 : }
155 :
156 : /************************************************************************/
157 : /* ~FGdbLayer() */
158 : /************************************************************************/
159 :
160 816 : FGdbLayer::~FGdbLayer()
161 : {
162 408 : FGdbLayer::CloseGDBObjects();
163 :
164 991 : for (size_t i = 0; i < m_apoByteArrays.size(); i++)
165 583 : delete m_apoByteArrays[i];
166 408 : m_apoByteArrays.resize(0);
167 :
168 408 : CSLDestroy(m_papszOptions);
169 408 : m_papszOptions = nullptr;
170 816 : }
171 :
172 : /************************************************************************/
173 : /* CloseGDBObjects() */
174 : /************************************************************************/
175 :
176 815 : void FGdbLayer::CloseGDBObjects()
177 : {
178 815 : EndBulkLoad();
179 :
180 : #ifdef EXTENT_WORKAROUND
181 815 : WorkAroundExtentProblem();
182 : #endif
183 :
184 815 : if (m_pTable)
185 : {
186 408 : delete m_pTable;
187 408 : m_pTable = nullptr;
188 : }
189 :
190 815 : FGdbBaseLayer::CloseGDBObjects();
191 815 : }
192 :
193 : /************************************************************************/
194 : /* EditIndexesForFIDHack() */
195 : /************************************************************************/
196 :
197 1 : int FGdbLayer::EditIndexesForFIDHack(const char *pszRadixTablename)
198 : {
199 : // Fix FIDs in .gdbtablx, .spx and .atx's
200 :
201 : const CPLString osGDBTablX =
202 2 : CPLResetExtensionSafe(pszRadixTablename, "gdbtablx");
203 : const CPLString osNewGDBTablX =
204 2 : CPLResetExtensionSafe(pszRadixTablename, "gdbtablx.new");
205 :
206 1 : if (!EditGDBTablX(osGDBTablX, osNewGDBTablX))
207 : {
208 0 : CPLError(CE_Failure, CPLE_AppDefined, "Error occurred when editing %s",
209 : osNewGDBTablX.c_str());
210 0 : VSIUnlink(osNewGDBTablX);
211 0 : return FALSE;
212 : }
213 :
214 2 : CPLString osDirectory(CPLGetPathSafe(pszRadixTablename));
215 1 : char **papszFiles = VSIReadDir(osDirectory);
216 2 : const CPLString osBasename(CPLGetBasenameSafe(pszRadixTablename));
217 1 : int bRet = TRUE;
218 50 : for (char **papszIter = papszFiles; papszIter && *papszIter; papszIter++)
219 : {
220 55 : if (strncmp(*papszIter, osBasename.c_str(), osBasename.size()) == 0 &&
221 55 : (EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "atx") ||
222 55 : EQUAL(CPLGetExtensionSafe(*papszIter).c_str(), "spx")))
223 : {
224 : const CPLString osIndex(
225 2 : CPLFormFilenameSafe(osDirectory, *papszIter, nullptr));
226 1 : if (!EditATXOrSPX(osIndex))
227 : {
228 0 : CPLError(CE_Failure, CPLE_AppDefined,
229 : "Error occurred when editing %s", osIndex.c_str());
230 0 : bRet = FALSE;
231 : }
232 : }
233 : }
234 1 : CSLDestroy(papszFiles);
235 :
236 1 : CPLString osGDBTablXTmp(CPLSPrintf("%s.tmp", osGDBTablX.c_str()));
237 2 : int bRet2 = (VSIRename(osGDBTablX, osGDBTablXTmp) == 0 &&
238 1 : VSIRename(osNewGDBTablX, osGDBTablX) == 0);
239 1 : VSIUnlink(osGDBTablXTmp);
240 1 : if (!bRet2)
241 : {
242 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename %s to %s",
243 : osNewGDBTablX.c_str(), osGDBTablX.c_str());
244 0 : bRet = FALSE;
245 : }
246 :
247 1 : return bRet;
248 : }
249 :
250 : /************************************************************************/
251 : /* EditATXOrSPX() */
252 : /************************************************************************/
253 :
254 : /* See https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec */
255 1 : int FGdbLayer::EditATXOrSPX(const CPLString &osIndex)
256 : {
257 1 : VSILFILE *fp = VSIFOpenL(osIndex, "rb+");
258 1 : if (fp == nullptr)
259 : {
260 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osIndex.c_str());
261 0 : return FALSE;
262 : }
263 1 : VSIFSeekL(fp, 0, SEEK_END);
264 1 : vsi_l_offset nPos = VSIFTellL(fp);
265 1 : int bRet = FALSE;
266 1 : int bInvalidateIndex = FALSE;
267 1 : if (nPos > 22)
268 : {
269 1 : VSIFSeekL(fp, nPos - 22, SEEK_SET);
270 : GByte nSizeIndexedValue;
271 2 : if (VSIFReadL(&nSizeIndexedValue, 1, 1, fp) == 1 &&
272 1 : nSizeIndexedValue > 0)
273 : {
274 : GByte abyIndexedValue[255];
275 1 : VSIFSeekL(fp, nPos - 22 + 6, SEEK_SET);
276 : int nDepth;
277 1 : if (VSIFReadL(&nDepth, 1, 4, fp) == 4)
278 : {
279 1 : CPL_LSBPTR32(&nDepth);
280 :
281 1 : int bIndexedValueIsValid = FALSE;
282 1 : int nFirstIndexAtThisValue = -1;
283 1 : std::vector<int> anPagesAtThisValue;
284 1 : int bSortThisValue = FALSE;
285 1 : int nLastPageVisited = 0;
286 1 : bRet = EditATXOrSPX(fp, 1, nLastPageVisited, nDepth,
287 : nSizeIndexedValue, abyIndexedValue,
288 : bIndexedValueIsValid,
289 : nFirstIndexAtThisValue, anPagesAtThisValue,
290 : bSortThisValue, bInvalidateIndex);
291 : }
292 : }
293 : }
294 1 : VSIFCloseL(fp);
295 1 : if (bInvalidateIndex)
296 : {
297 : // CPLDebug("FGDB", "Invalidate %s", osIndex.c_str());
298 0 : CPLError(CE_Warning, CPLE_AppDefined, "Invalidate %s", osIndex.c_str());
299 0 : VSIUnlink(osIndex);
300 : }
301 1 : return bRet;
302 : }
303 :
304 12 : static int FGdbLayerSortATX(const void *_pa, const void *_pb)
305 : {
306 12 : int a = CPL_LSBWORD32(*(int *)_pa);
307 12 : int b = CPL_LSBWORD32(*(int *)_pb);
308 12 : if (a < b)
309 12 : return -1;
310 0 : else if (a > b)
311 0 : return 1;
312 0 : CPLAssert(false);
313 : return 0;
314 : }
315 :
316 1 : int FGdbLayer::EditATXOrSPX(VSILFILE *fp, int nThisPage, int &nLastPageVisited,
317 : int nDepth, int nSizeIndexedValue,
318 : GByte *pabyLastIndexedValue,
319 : int &bIndexedValueIsValid,
320 : int &nFirstIndexAtThisValue,
321 : std::vector<int> &anPagesAtThisValue,
322 : int &bSortThisValue, int &bInvalidateIndex)
323 : {
324 : GByte abyBuffer[4096];
325 :
326 1 : VSIFSeekL(fp, (nThisPage - 1) * 4096, SEEK_SET);
327 :
328 1 : if (nDepth == 1)
329 : {
330 1 : if (nThisPage == nLastPageVisited)
331 0 : return TRUE;
332 :
333 : /* This page directly references features */
334 1 : int bRewritePage = FALSE;
335 1 : if (VSIFReadL(abyBuffer, 1, 4096, fp) != 4096)
336 0 : return FALSE;
337 : int nNextPageID;
338 1 : memcpy(&nNextPageID, abyBuffer, 4);
339 : int nFeatures;
340 1 : memcpy(&nFeatures, abyBuffer + 4, 4);
341 1 : CPL_LSBPTR32(&nFeatures);
342 :
343 : // if( nLastPageVisited == 0 )
344 : // printf("nFeatures = %d\n", nFeatures);
345 :
346 1 : const int nMaxPerPages = (4096 - 12) / (4 + nSizeIndexedValue);
347 1 : const int nOffsetFirstValInPage = 12 + nMaxPerPages * 4;
348 1 : if (nFeatures > nMaxPerPages)
349 0 : return FALSE;
350 23 : for (int i = 0; i < nFeatures; i++)
351 : {
352 43 : int bNewVal = (!bIndexedValueIsValid ||
353 21 : memcmp(pabyLastIndexedValue,
354 21 : abyBuffer + nOffsetFirstValInPage +
355 21 : i * nSizeIndexedValue,
356 : nSizeIndexedValue) != 0);
357 :
358 : int nFID;
359 22 : memcpy(&nFID, abyBuffer + 12 + 4 * i, 4);
360 22 : CPL_LSBPTR32(&nFID);
361 22 : int nOGRFID = m_oMapFGDBFIDToOGRFID[nFID];
362 22 : if (nOGRFID)
363 : {
364 1 : nFID = nOGRFID;
365 1 : CPL_LSBPTR32(&nOGRFID);
366 1 : memcpy(abyBuffer + 12 + 4 * i, &nOGRFID, 4);
367 1 : bRewritePage = TRUE;
368 :
369 1 : if (bIndexedValueIsValid && i == nFeatures - 1 &&
370 0 : nNextPageID == 0)
371 0 : bSortThisValue = TRUE;
372 : }
373 :
374 : // We must make sure that features with same indexed values are
375 : // sorted by increasing FID, even when that spans over several
376 : // pages
377 22 : if (bSortThisValue &&
378 0 : (bNewVal || (i == nFeatures - 1 && nNextPageID == 0)))
379 : {
380 1 : if (anPagesAtThisValue[0] == nThisPage)
381 : {
382 1 : CPLAssert(anPagesAtThisValue.size() == 1);
383 1 : int nFeaturesToSortThisPage = i - nFirstIndexAtThisValue;
384 1 : if (!bNewVal && i == nFeatures - 1 && nNextPageID == 0)
385 0 : nFeaturesToSortThisPage++;
386 1 : CPLAssert(nFeaturesToSortThisPage > 0);
387 :
388 1 : bRewritePage = TRUE;
389 1 : qsort(abyBuffer + 12 + 4 * nFirstIndexAtThisValue,
390 : nFeaturesToSortThisPage, 4, FGdbLayerSortATX);
391 : }
392 : else
393 : {
394 0 : std::vector<int> anValues;
395 0 : int nFeaturesToSort = 0;
396 0 : anValues.resize(anPagesAtThisValue.size() * nMaxPerPages);
397 :
398 0 : int nFeaturesToSortLastPage = i;
399 0 : if (!bNewVal && i == nFeatures - 1 && nNextPageID == 0)
400 0 : nFeaturesToSortLastPage++;
401 :
402 0 : for (size_t j = 0; j < anPagesAtThisValue.size(); j++)
403 : {
404 : int nFeaturesPrevPage;
405 0 : VSIFSeekL(fp, (anPagesAtThisValue[j] - 1) * 4096 + 4,
406 : SEEK_SET);
407 0 : VSIFReadL(&nFeaturesPrevPage, 1, 4, fp);
408 0 : CPL_LSBPTR32(&nFeaturesPrevPage);
409 0 : if (j == 0)
410 : {
411 0 : VSIFSeekL(fp,
412 0 : (anPagesAtThisValue[j] - 1) * 4096 + 12 +
413 0 : 4 * nFirstIndexAtThisValue,
414 : SEEK_SET);
415 0 : VSIFReadL(
416 0 : &anValues[nFeaturesToSort], 4,
417 0 : nFeaturesPrevPage - nFirstIndexAtThisValue, fp);
418 0 : nFeaturesToSort +=
419 0 : nFeaturesPrevPage - nFirstIndexAtThisValue;
420 : }
421 0 : else if (j == anPagesAtThisValue.size() - 1 &&
422 0 : anPagesAtThisValue[j] == nThisPage)
423 : {
424 0 : bRewritePage = TRUE;
425 0 : memcpy(&anValues[nFeaturesToSort], abyBuffer + 12,
426 0 : nFeaturesToSortLastPage * 4);
427 0 : nFeaturesToSort += nFeaturesToSortLastPage;
428 : }
429 : else
430 : {
431 0 : VSIFSeekL(fp,
432 0 : (anPagesAtThisValue[j] - 1) * 4096 + 12,
433 : SEEK_SET);
434 0 : VSIFReadL(&anValues[nFeaturesToSort], 4,
435 : nFeaturesPrevPage, fp);
436 0 : nFeaturesToSort += nFeaturesPrevPage;
437 : }
438 : }
439 :
440 0 : qsort(&anValues[0], nFeaturesToSort, 4, FGdbLayerSortATX);
441 :
442 0 : nFeaturesToSort = 0;
443 0 : for (size_t j = 0; j < anPagesAtThisValue.size(); j++)
444 : {
445 : int nFeaturesPrevPage;
446 0 : VSIFSeekL(fp, (anPagesAtThisValue[j] - 1) * 4096 + 4,
447 : SEEK_SET);
448 0 : VSIFReadL(&nFeaturesPrevPage, 1, 4, fp);
449 0 : CPL_LSBPTR32(&nFeaturesPrevPage);
450 0 : if (j == 0)
451 : {
452 0 : VSIFSeekL(fp,
453 0 : (anPagesAtThisValue[j] - 1) * 4096 + 12 +
454 0 : 4 * nFirstIndexAtThisValue,
455 : SEEK_SET);
456 0 : VSIFWriteL(
457 0 : &anValues[nFeaturesToSort], 4,
458 0 : nFeaturesPrevPage - nFirstIndexAtThisValue, fp);
459 0 : nFeaturesToSort +=
460 0 : nFeaturesPrevPage - nFirstIndexAtThisValue;
461 : }
462 0 : else if (j == anPagesAtThisValue.size() - 1 &&
463 0 : anPagesAtThisValue[j] == nThisPage)
464 : {
465 0 : memcpy(abyBuffer + 12, &anValues[nFeaturesToSort],
466 0 : nFeaturesToSortLastPage * 4);
467 0 : nFeaturesToSort += nFeaturesToSortLastPage;
468 : }
469 : else
470 : {
471 0 : VSIFSeekL(fp,
472 0 : (anPagesAtThisValue[j] - 1) * 4096 + 12,
473 : SEEK_SET);
474 0 : VSIFWriteL(&anValues[nFeaturesToSort], 4,
475 : nFeaturesPrevPage, fp);
476 0 : nFeaturesToSort += nFeaturesPrevPage;
477 : }
478 : }
479 : }
480 : }
481 :
482 22 : if (bNewVal)
483 : {
484 6 : nFirstIndexAtThisValue = i;
485 6 : anPagesAtThisValue.clear();
486 6 : anPagesAtThisValue.push_back(nThisPage);
487 :
488 6 : memcpy(pabyLastIndexedValue,
489 6 : abyBuffer + nOffsetFirstValInPage +
490 6 : i * nSizeIndexedValue,
491 : nSizeIndexedValue);
492 6 : bSortThisValue = FALSE;
493 : }
494 16 : else if (i == 0)
495 : {
496 0 : if (anPagesAtThisValue.size() > 100000)
497 : {
498 0 : bInvalidateIndex = TRUE;
499 0 : return FALSE;
500 : }
501 : else
502 : {
503 0 : anPagesAtThisValue.push_back(nThisPage);
504 : }
505 : }
506 :
507 22 : if (nOGRFID)
508 1 : bSortThisValue = TRUE;
509 :
510 22 : bIndexedValueIsValid = TRUE;
511 : }
512 :
513 1 : if (bRewritePage)
514 : {
515 1 : VSIFSeekL(fp, (nThisPage - 1) * 4096, SEEK_SET);
516 1 : if (VSIFWriteL(abyBuffer, 1, 4096, fp) != 4096)
517 0 : return FALSE;
518 : }
519 :
520 1 : nLastPageVisited = nThisPage;
521 :
522 1 : return TRUE;
523 : }
524 : else
525 : {
526 : /* This page references other pages */
527 0 : if (VSIFReadL(abyBuffer, 1, 4096, fp) != 4096)
528 0 : return FALSE;
529 : int nSubPages;
530 0 : memcpy(&nSubPages, abyBuffer + 4, 4);
531 0 : CPL_LSBPTR32(&nSubPages);
532 0 : nSubPages++;
533 0 : if (nSubPages > (4096 - 8) / 4)
534 0 : return FALSE;
535 0 : for (int i = 0; i < nSubPages; i++)
536 : {
537 : int nSubPageID;
538 0 : memcpy(&nSubPageID, abyBuffer + 8 + 4 * i, 4);
539 0 : CPL_LSBPTR32(&nSubPageID);
540 0 : if (nSubPageID < 1)
541 0 : return FALSE;
542 0 : if (!EditATXOrSPX(fp, nSubPageID, nLastPageVisited, nDepth - 1,
543 : nSizeIndexedValue, pabyLastIndexedValue,
544 : bIndexedValueIsValid, nFirstIndexAtThisValue,
545 : anPagesAtThisValue, bSortThisValue,
546 : bInvalidateIndex))
547 : {
548 0 : return FALSE;
549 : }
550 : }
551 :
552 0 : return TRUE;
553 : }
554 : }
555 :
556 : /************************************************************************/
557 : /* GetInt32() */
558 : /************************************************************************/
559 :
560 5 : static GInt32 GetInt32(const GByte *pBaseAddr, int iOffset)
561 : {
562 : GInt32 nVal;
563 5 : memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
564 5 : CPL_LSBPTR32(&nVal);
565 5 : return nVal;
566 : }
567 :
568 : /************************************************************************/
569 : /* UpdateNextOGRFIDAndFGDBFID() */
570 : /************************************************************************/
571 :
572 10 : static CPL_INLINE void UpdateNextOGRFIDAndFGDBFID(
573 : int i, const std::map<int, int> &oMapOGRFIDToFGDBFID,
574 : std::map<int, int>::iterator &oIterO2F, int &nNextOGRFID,
575 : const std::map<int, int> &oMapFGDBFIDToOGRFID,
576 : std::map<int, int>::iterator &oIterF2O, int &nNextFGDBFID)
577 : {
578 10 : while (nNextOGRFID > 0 && i > nNextOGRFID)
579 : {
580 0 : ++oIterO2F;
581 0 : if (oIterO2F == oMapOGRFIDToFGDBFID.end())
582 0 : nNextOGRFID = -1;
583 : else
584 0 : nNextOGRFID = oIterO2F->first;
585 : }
586 :
587 10 : while (nNextFGDBFID > 0 && i > nNextFGDBFID)
588 : {
589 0 : ++oIterF2O;
590 0 : if (oIterF2O == oMapFGDBFIDToOGRFID.end())
591 0 : nNextFGDBFID = -1;
592 : else
593 0 : nNextFGDBFID = oIterF2O->first;
594 : }
595 10 : }
596 :
597 : /************************************************************************/
598 : /* EditGDBTablX() */
599 : /************************************************************************/
600 :
601 : #define TEST_BIT(ar, bit) (ar[(bit) / 8] & (1 << ((bit) % 8)))
602 : #define SET_BIT(ar, bit) ar[(bit) / 8] |= (1 << ((bit) % 8))
603 : #define BIT_ARRAY_SIZE_IN_BYTES(bitsize) (((bitsize) + 7) / 8)
604 :
605 : /* See https://github.com/rouault/dump_gdbtable/wiki/FGDB-Spec */
606 1 : int FGdbLayer::EditGDBTablX(const CPLString &osGDBTablX,
607 : const CPLString &osNewGDBTablX)
608 : {
609 1 : VSILFILE *fp = VSIFOpenL(osGDBTablX, "rb");
610 1 : if (fp == nullptr)
611 : {
612 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s", osGDBTablX.c_str());
613 0 : return FALSE;
614 : }
615 1 : VSILFILE *fpNew = VSIFOpenL(osNewGDBTablX, "wb");
616 1 : if (fpNew == nullptr)
617 : {
618 0 : CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
619 : osNewGDBTablX.c_str());
620 0 : VSIFCloseL(fp);
621 0 : return FALSE;
622 : }
623 : GByte abyBuffer[16];
624 1 : VSIFReadL(abyBuffer, 1, 16, fp);
625 1 : int n1024Blocks = GetInt32(abyBuffer, 1);
626 1 : int nInMaxFID = GetInt32(abyBuffer, 2);
627 : #ifdef DEBUG
628 1 : const int nInMaxFIDOri = nInMaxFID;
629 : #endif
630 1 : int nRecordSize = GetInt32(abyBuffer, 3);
631 1 : CPLAssert(nRecordSize >= 4 && nRecordSize <= 6);
632 :
633 1 : std::map<int, int>::iterator oIterO2F = m_oMapOGRFIDToFGDBFID.begin();
634 1 : int nMaxOGRFID = 0;
635 2 : for (; oIterO2F != m_oMapOGRFIDToFGDBFID.end(); ++oIterO2F)
636 1 : nMaxOGRFID = oIterO2F->first;
637 : // printf("nInMaxFID = %d\n", nInMaxFID);
638 : // printf("nMaxOGRFID = %d\n", nMaxOGRFID);
639 1 : int nOutMaxFID = MAX(nInMaxFID, nMaxOGRFID);
640 :
641 : // Optimization: If the feature ids at the end of the file all map to a OGR
642 : // fid then they don't need to be included in the final file
643 2 : for (int i = nInMaxFID; i > nMaxOGRFID; i--)
644 : {
645 1 : if (m_oMapFGDBFIDToOGRFID.find(i) != m_oMapFGDBFIDToOGRFID.end())
646 : {
647 1 : nOutMaxFID--;
648 1 : nInMaxFID--;
649 : }
650 : else
651 0 : break;
652 : }
653 :
654 : // printf("nInMaxFID = %d\n", nInMaxFID);
655 : // printf("nOutMaxFID = %d\n", nOutMaxFID);
656 :
657 1 : int n1024BlocksOut = (int)(((GIntBig)nOutMaxFID + 1023) / 1024);
658 : int nTmp;
659 :
660 1 : nTmp = CPL_LSBWORD32(n1024BlocksOut);
661 1 : memcpy(abyBuffer + 4, &nTmp, 4);
662 :
663 1 : nTmp = CPL_LSBWORD32(nOutMaxFID);
664 1 : memcpy(abyBuffer + 8, &nTmp, 4);
665 1 : VSIFWriteL(abyBuffer, 1, 16, fpNew);
666 :
667 1 : VSIFSeekL(fp, 1024 * n1024Blocks * nRecordSize, SEEK_CUR);
668 1 : VSIFReadL(abyBuffer, 1, 16, fp);
669 1 : int nBitmapInt32Words = GetInt32(abyBuffer, 0);
670 1 : int n1024BlocksTotal = GetInt32(abyBuffer, 1);
671 1 : CPLAssert(n1024BlocksTotal == (int)(((GIntBig)nInMaxFIDOri + 1023) / 1024));
672 1 : GByte *pabyBlockMap = nullptr;
673 1 : if (nBitmapInt32Words != 0)
674 : {
675 0 : int nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(n1024BlocksTotal);
676 0 : pabyBlockMap = (GByte *)CPLMalloc(nSizeInBytes);
677 0 : VSIFReadL(pabyBlockMap, nSizeInBytes, 1, fp);
678 : }
679 1 : int nSizeInBytesOut = BIT_ARRAY_SIZE_IN_BYTES(n1024BlocksOut);
680 : /* Round to the next multiple of 128 bytes (32 int4 words) */
681 1 : nSizeInBytesOut = ((nSizeInBytesOut + 127) / 128) * 128;
682 1 : GByte *pabyBlockMapOut = (GByte *)VSI_CALLOC_VERBOSE(1, nSizeInBytesOut);
683 1 : GByte *pabyPage = (GByte *)VSI_MALLOC_VERBOSE(1024 * nRecordSize);
684 1 : if (pabyBlockMapOut == nullptr || pabyPage == nullptr)
685 : {
686 0 : VSIFree(pabyBlockMapOut);
687 0 : VSIFree(pabyPage);
688 0 : VSIFCloseL(fp);
689 0 : return FALSE;
690 : }
691 : GByte abyEmptyOffset[6];
692 1 : memset(abyEmptyOffset, 0, 6);
693 1 : int nNonEmptyPages = 0;
694 1 : int nOffsetInPage = 0, nLastWrittenOffset = 0;
695 : int bDisableSparsePages =
696 1 : CPLTestBool(CPLGetConfigOption("FILEGDB_DISABLE_SPARSE_PAGES", "NO"));
697 :
698 1 : oIterO2F = m_oMapOGRFIDToFGDBFID.begin();
699 1 : int nNextOGRFID = oIterO2F->first;
700 1 : std::map<int, int>::iterator oIterF2O = m_oMapFGDBFIDToOGRFID.begin();
701 1 : int nNextFGDBFID = oIterF2O->first;
702 :
703 1 : int nCountBlocksBeforeIBlockIdx = 0;
704 1 : int nCountBlocksBeforeIBlockValue = 0;
705 :
706 1 : int bRet = TRUE;
707 1 : int i = 0;
708 11 : for (unsigned iUnsigned = 1; iUnsigned <= static_cast<unsigned>(nOutMaxFID);
709 10 : iUnsigned = static_cast<unsigned>(i) + 1, nOffsetInPage += nRecordSize)
710 : {
711 10 : i = static_cast<int>(iUnsigned);
712 10 : if (nOffsetInPage == 1024 * nRecordSize)
713 : {
714 0 : if (nLastWrittenOffset > 0 || bDisableSparsePages)
715 : {
716 0 : SET_BIT(pabyBlockMapOut, (i - 2) / 1024);
717 0 : nNonEmptyPages++;
718 0 : if (nLastWrittenOffset < nOffsetInPage)
719 0 : memset(pabyPage + nLastWrittenOffset, 0,
720 0 : nOffsetInPage - nLastWrittenOffset);
721 0 : if (VSIFWriteL(pabyPage, 1024 * nRecordSize, 1, fpNew) != 1)
722 : {
723 0 : bRet = FALSE;
724 0 : goto end;
725 : }
726 : }
727 0 : nOffsetInPage = 0;
728 0 : nLastWrittenOffset = 0;
729 :
730 : // A few optimizations :
731 0 : if (!bDisableSparsePages && i > nInMaxFID && nNextOGRFID > 0 &&
732 0 : i < nNextOGRFID - 1024)
733 : {
734 : // If we created a OGR FID far away from the latest FGDB FID
735 : // then skip to it
736 0 : i = ((nNextOGRFID - 1) / 1024) * 1024 + 1;
737 : }
738 : // coverity[negative_shift]
739 0 : else if (!bDisableSparsePages && pabyBlockMap != nullptr &&
740 0 : i <= nInMaxFID &&
741 0 : TEST_BIT(pabyBlockMap, (i - 1) / 1024) == 0)
742 : {
743 : // Skip empty pages
744 0 : UpdateNextOGRFIDAndFGDBFID(i, m_oMapOGRFIDToFGDBFID, oIterO2F,
745 0 : nNextOGRFID, m_oMapFGDBFIDToOGRFID,
746 : oIterF2O, nNextFGDBFID);
747 0 : if ((nNextOGRFID < 0 || i < nNextOGRFID - 1024) &&
748 0 : (nNextFGDBFID < 0 || i < nNextFGDBFID - 1024))
749 : {
750 0 : if (i > INT_MAX - 1024)
751 0 : break;
752 0 : i += 1023;
753 0 : nOffsetInPage += 1023 * nRecordSize;
754 0 : continue;
755 : }
756 : }
757 : }
758 :
759 10 : UpdateNextOGRFIDAndFGDBFID(i, m_oMapOGRFIDToFGDBFID, oIterO2F,
760 10 : nNextOGRFID, m_oMapFGDBFIDToOGRFID, oIterF2O,
761 : nNextFGDBFID);
762 :
763 : int nSrcFID;
764 10 : if (i == nNextOGRFID)
765 : {
766 : // This FID matches a user defined OGR FID, then find the
767 : // corresponding FGDB record
768 1 : nSrcFID = oIterO2F->second;
769 : // printf("(1) i = %d, nSrcFID = %d\n", i, nSrcFID);
770 : }
771 9 : else if (i == nNextFGDBFID || i > nInMaxFID)
772 : {
773 : // This record is a temporary one (will be moved to a user-define
774 : // FID) or we are out of the validity zone of input records
775 : // printf("(2) i = %d, nNextFGDBFID = %d, nInMaxFID = %d\n", i,
776 : // nNextFGDBFID, nInMaxFID);
777 0 : continue;
778 : }
779 : else
780 : {
781 : // Regular record, not overloaded by user defined FID
782 9 : nSrcFID = i;
783 : // printf("(3) i = %d, nSrcFID = %d\n", i, nSrcFID);
784 : }
785 :
786 10 : if (pabyBlockMap != nullptr)
787 : {
788 0 : int iBlock = (nSrcFID - 1) / 1024;
789 :
790 : // Check if the block is not empty
791 : // coverity[negative_shift]
792 0 : if (TEST_BIT(pabyBlockMap, iBlock))
793 : {
794 : int nCountBlocksBefore;
795 0 : if (iBlock >= nCountBlocksBeforeIBlockIdx)
796 : {
797 0 : nCountBlocksBefore = nCountBlocksBeforeIBlockValue;
798 0 : for (int j = nCountBlocksBeforeIBlockIdx; j < iBlock; j++)
799 : {
800 : // coverity[negative_shift]
801 0 : nCountBlocksBefore += TEST_BIT(pabyBlockMap, j) != 0;
802 : }
803 : }
804 : else
805 : {
806 0 : nCountBlocksBefore = 0;
807 0 : for (int j = 0; j < iBlock; j++)
808 : {
809 : // coverity[negative_shift]
810 0 : nCountBlocksBefore += TEST_BIT(pabyBlockMap, j) != 0;
811 : }
812 : }
813 0 : nCountBlocksBeforeIBlockIdx = iBlock;
814 0 : nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
815 0 : int iCorrectedRow =
816 0 : nCountBlocksBefore * 1024 + ((nSrcFID - 1) % 1024);
817 0 : VSIFSeekL(fp, 16 + nRecordSize * iCorrectedRow, SEEK_SET);
818 0 : VSIFReadL(abyBuffer, 1, nRecordSize, fp);
819 0 : if (memcmp(abyBuffer, abyEmptyOffset, nRecordSize) != 0)
820 : {
821 0 : if (nLastWrittenOffset < nOffsetInPage)
822 0 : memset(pabyPage + nLastWrittenOffset, 0,
823 0 : nOffsetInPage - nLastWrittenOffset);
824 0 : memcpy(pabyPage + nOffsetInPage, abyBuffer, nRecordSize);
825 0 : nLastWrittenOffset = nOffsetInPage + nRecordSize;
826 : }
827 : }
828 : }
829 : else
830 : {
831 10 : VSIFSeekL(fp, 16 + nRecordSize * (nSrcFID - 1), SEEK_SET);
832 10 : VSIFReadL(abyBuffer, 1, nRecordSize, fp);
833 10 : if (memcmp(abyBuffer, abyEmptyOffset, nRecordSize) != 0)
834 : {
835 10 : if (nLastWrittenOffset < nOffsetInPage)
836 0 : memset(pabyPage + nLastWrittenOffset, 0,
837 0 : nOffsetInPage - nLastWrittenOffset);
838 10 : memcpy(pabyPage + nOffsetInPage, abyBuffer, nRecordSize);
839 10 : nLastWrittenOffset = nOffsetInPage + nRecordSize;
840 : }
841 : }
842 : }
843 : // printf("nLastWrittenOffset = %d\n", nLastWrittenOffset);
844 1 : if (nLastWrittenOffset > 0 || bDisableSparsePages)
845 : {
846 1 : assert(nOutMaxFID >= 1);
847 1 : SET_BIT(pabyBlockMapOut, (nOutMaxFID - 1) / 1024);
848 1 : nNonEmptyPages++;
849 1 : if (nLastWrittenOffset < 1024 * nRecordSize)
850 1 : memset(pabyPage + nLastWrittenOffset, 0,
851 1 : 1024 * nRecordSize - nLastWrittenOffset);
852 1 : if (VSIFWriteL(pabyPage, 1024 * nRecordSize, 1, fpNew) != 1)
853 : {
854 0 : bRet = FALSE;
855 0 : goto end;
856 : }
857 : }
858 :
859 1 : memset(abyBuffer, 0, 16);
860 :
861 : /* Number of total blocks, including omitted ones */
862 1 : nTmp = CPL_LSBWORD32(n1024BlocksOut);
863 1 : memcpy(abyBuffer + 4, &nTmp, 4);
864 :
865 1 : nTmp = CPL_LSBWORD32(nNonEmptyPages);
866 1 : memcpy(abyBuffer + 8, &nTmp, 4);
867 :
868 1 : if (nNonEmptyPages < n1024BlocksOut)
869 : {
870 : /* Number of int4 words for the bitmap (rounded to the next multiple of
871 : * 32) */
872 0 : nTmp = CPL_LSBWORD32(nSizeInBytesOut / 4);
873 0 : memcpy(abyBuffer + 0, &nTmp, 4);
874 :
875 : /* Number of int4 words in the bitmap where there's at least a non-zero
876 : * bit */
877 : /* Seems to be unused */
878 0 : nTmp = CPL_LSBWORD32(((nOutMaxFID - 1) / 1024 + 31) / 32);
879 0 : memcpy(abyBuffer + 12, &nTmp, 4);
880 : }
881 :
882 1 : if (VSIFWriteL(abyBuffer, 1, 16, fpNew) != 16)
883 : {
884 0 : bRet = FALSE;
885 0 : goto end;
886 : }
887 :
888 1 : if (nNonEmptyPages < n1024BlocksOut)
889 : {
890 0 : VSIFWriteL(pabyBlockMapOut, 1, nSizeInBytesOut, fpNew);
891 :
892 0 : VSIFSeekL(fpNew, 4, SEEK_SET);
893 0 : nTmp = CPL_LSBWORD32(nNonEmptyPages);
894 0 : VSIFWriteL(&nTmp, 1, 4, fpNew);
895 : }
896 :
897 1 : end:
898 1 : CPLFree(pabyBlockMap);
899 1 : CPLFree(pabyBlockMapOut);
900 1 : CPLFree(pabyPage);
901 1 : VSIFCloseL(fpNew);
902 1 : VSIFCloseL(fp);
903 :
904 1 : return bRet;
905 : }
906 :
907 : #ifdef EXTENT_WORKAROUND
908 :
909 : /************************************************************************/
910 : /* UpdateRowWithGeometry() */
911 : /************************************************************************/
912 :
913 0 : bool FGdbLayer::UpdateRowWithGeometry(Row &row, OGRGeometry *poGeom)
914 : {
915 0 : ShapeBuffer shape;
916 : long hr;
917 :
918 : /* Write geometry to a buffer */
919 0 : GByte *pabyShape = nullptr;
920 0 : int nShapeSize = 0;
921 0 : if (OGRWriteToShapeBin(poGeom, &pabyShape, &nShapeSize) != OGRERR_NONE)
922 : {
923 0 : CPLFree(pabyShape);
924 0 : return false;
925 : }
926 :
927 : /* Copy it into a ShapeBuffer */
928 0 : if (nShapeSize > 0)
929 : {
930 0 : shape.Allocate(nShapeSize);
931 0 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
932 0 : shape.inUseLength = nShapeSize;
933 : }
934 :
935 : /* Free the shape buffer */
936 0 : CPLFree(pabyShape);
937 :
938 : /* Write ShapeBuffer into the Row */
939 0 : hr = row.SetGeometry(shape);
940 0 : if (FAILED(hr))
941 : {
942 0 : return false;
943 : }
944 :
945 : /* Update row */
946 0 : hr = m_pTable->Update(row);
947 0 : if (FAILED(hr))
948 : {
949 0 : return false;
950 : }
951 :
952 0 : return true;
953 : }
954 :
955 : /************************************************************************/
956 : /* WorkAroundExtentProblem() */
957 : /* */
958 : /* Work-around problem with FileGDB API 1.1 on Linux 64bit. See #4455 */
959 : /************************************************************************/
960 :
961 815 : void FGdbLayer::WorkAroundExtentProblem()
962 : {
963 815 : if (!m_bLayerJustCreated || !m_bLayerEnvelopeValid)
964 719 : return;
965 96 : m_bLayerJustCreated = FALSE;
966 :
967 96 : OGREnvelope sEnvelope;
968 96 : if (FGdbLayer::GetExtent(&sEnvelope, TRUE) != OGRERR_NONE)
969 0 : return;
970 :
971 : /* The characteristic of the bug is that the reported extent */
972 : /* is the real extent truncated incorrectly to integer values */
973 : /* We work around that by temporary updating one feature with a geometry */
974 : /* whose coordinates are integer values but ceil'ed and floor'ed */
975 : /* such that they include the real layer extent. */
976 96 : if (((double)(int)sEnvelope.MinX == sEnvelope.MinX &&
977 90 : (double)(int)sEnvelope.MinY == sEnvelope.MinY &&
978 90 : (double)(int)sEnvelope.MaxX == sEnvelope.MaxX &&
979 90 : (double)(int)sEnvelope.MaxY == sEnvelope.MaxY) &&
980 90 : (fabs(sEnvelope.MinX - sLayerEnvelope.MinX) > 1e-5 ||
981 90 : fabs(sEnvelope.MinY - sLayerEnvelope.MinY) > 1e-5 ||
982 90 : fabs(sEnvelope.MaxX - sLayerEnvelope.MaxX) > 1e-5 ||
983 90 : fabs(sEnvelope.MaxY - sLayerEnvelope.MaxY) > 1e-5))
984 : {
985 : long hr;
986 0 : Row row;
987 0 : EnumRows enumRows;
988 :
989 0 : if (FAILED(hr = m_pTable->Search(StringToWString("*"),
990 : StringToWString(""), true, enumRows)))
991 0 : return;
992 :
993 0 : if (FAILED(hr = enumRows.Next(row)))
994 0 : return;
995 :
996 0 : if (hr != S_OK)
997 0 : return;
998 :
999 : /* Backup original shape buffer */
1000 0 : ShapeBuffer originalGdbGeometry;
1001 0 : if (FAILED(hr = row.GetGeometry(originalGdbGeometry)))
1002 0 : return;
1003 :
1004 0 : OGRGeometry *pOGRGeo = nullptr;
1005 0 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &originalGdbGeometry,
1006 0 : m_pSRS, &pOGRGeo)) ||
1007 0 : pOGRGeo == nullptr)
1008 : {
1009 0 : delete pOGRGeo;
1010 0 : return;
1011 : }
1012 :
1013 0 : OGRwkbGeometryType eType = wkbFlatten(pOGRGeo->getGeometryType());
1014 :
1015 0 : delete pOGRGeo;
1016 0 : pOGRGeo = nullptr;
1017 :
1018 0 : OGRPoint oP1(floor(sLayerEnvelope.MinX), floor(sLayerEnvelope.MinY));
1019 0 : OGRPoint oP2(ceil(sLayerEnvelope.MaxX), ceil(sLayerEnvelope.MaxY));
1020 :
1021 0 : OGRLinearRing oLR;
1022 0 : oLR.addPoint(&oP1);
1023 0 : oLR.addPoint(&oP2);
1024 0 : oLR.addPoint(&oP1);
1025 :
1026 0 : if (eType == wkbPoint)
1027 : {
1028 0 : UpdateRowWithGeometry(row, &oP1);
1029 0 : UpdateRowWithGeometry(row, &oP2);
1030 : }
1031 0 : else if (eType == wkbLineString)
1032 : {
1033 0 : UpdateRowWithGeometry(row, &oLR);
1034 : }
1035 0 : else if (eType == wkbPolygon)
1036 : {
1037 0 : OGRPolygon oPoly;
1038 0 : oPoly.addRing(&oLR);
1039 :
1040 0 : UpdateRowWithGeometry(row, &oPoly);
1041 : }
1042 0 : else if (eType == wkbMultiPoint)
1043 : {
1044 0 : OGRMultiPoint oColl;
1045 0 : oColl.addGeometry(&oP1);
1046 0 : oColl.addGeometry(&oP2);
1047 :
1048 0 : UpdateRowWithGeometry(row, &oColl);
1049 : }
1050 0 : else if (eType == wkbMultiLineString)
1051 : {
1052 0 : OGRMultiLineString oColl;
1053 0 : oColl.addGeometry(&oLR);
1054 :
1055 0 : UpdateRowWithGeometry(row, &oColl);
1056 : }
1057 0 : else if (eType == wkbMultiPolygon)
1058 : {
1059 0 : OGRMultiPolygon oColl;
1060 0 : OGRPolygon oPoly;
1061 0 : oPoly.addRing(&oLR);
1062 0 : oColl.addGeometry(&oPoly);
1063 :
1064 0 : UpdateRowWithGeometry(row, &oColl);
1065 : }
1066 : else
1067 0 : return;
1068 :
1069 : /* Restore original ShapeBuffer */
1070 0 : hr = row.SetGeometry(originalGdbGeometry);
1071 0 : if (FAILED(hr))
1072 0 : return;
1073 :
1074 : /* Update Row */
1075 0 : hr = m_pTable->Update(row);
1076 0 : if (FAILED(hr))
1077 0 : return;
1078 :
1079 0 : CPLDebug("FGDB",
1080 : "Workaround extent problem with Linux 64bit FGDB SDK 1.1");
1081 : }
1082 : }
1083 : #endif // EXTENT_WORKAROUND
1084 :
1085 : /************************************************************************/
1086 : /* ICreateFeature() */
1087 : /* Create an FGDB Row and populate it from an OGRFeature. */
1088 : /* */
1089 : /************************************************************************/
1090 :
1091 1568 : OGRErr FGdbLayer::ICreateFeature(OGRFeature *poFeature)
1092 : {
1093 3136 : Row fgdb_row;
1094 : fgdbError hr;
1095 :
1096 1568 : if (!m_pDS->GetUpdate() || m_pTable == nullptr)
1097 0 : return OGRERR_FAILURE;
1098 :
1099 1568 : GIntBig nFID = poFeature->GetFID();
1100 1568 : if (nFID < -1 || nFID == 0 || !CPL_INT64_FITS_ON_INT32(nFID))
1101 : {
1102 0 : CPLError(CE_Failure, CPLE_NotSupported,
1103 : "Only 32 bit positive integers FID supported by FileGDB");
1104 0 : return OGRERR_FAILURE;
1105 : }
1106 :
1107 1568 : if (nFID > 0)
1108 : {
1109 1 : if (m_pDS->GetOpenFileGDBDrv() == nullptr)
1110 : {
1111 0 : CPLError(CE_Failure, CPLE_AppDefined,
1112 : "Cannot call CreateFeature() with a set FID when "
1113 : "OpenFileGDB driver not available");
1114 0 : return OGRERR_FAILURE;
1115 : }
1116 :
1117 1 : if (m_pDS->HasSelectLayers())
1118 : {
1119 0 : CPLError(CE_Failure, CPLE_AppDefined,
1120 : "Cannot call CreateFeature() with a set FID when a layer "
1121 : "resulting from ExecuteSQL() is still opened");
1122 0 : return OGRERR_FAILURE;
1123 : }
1124 :
1125 1 : if (m_pDS->GetConnection()->GetRefCount() > 1)
1126 : {
1127 0 : CPLError(CE_Failure, CPLE_AppDefined,
1128 : "Cannot call CreateFeature() with a set FID when a "
1129 : "dataset is opened more than once");
1130 0 : return OGRERR_FAILURE;
1131 : }
1132 :
1133 1 : if (m_oMapOGRFIDToFGDBFID.find((int)poFeature->GetFID()) !=
1134 2 : m_oMapOGRFIDToFGDBFID.end())
1135 : {
1136 0 : CPLError(CE_Failure, CPLE_AppDefined,
1137 : "A feature with same FID already exists");
1138 0 : return OGRERR_FAILURE;
1139 : }
1140 :
1141 1 : if (m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID()) ==
1142 2 : m_oMapFGDBFIDToOGRFID.end())
1143 : {
1144 1 : EnumRows enumRows;
1145 1 : Row row;
1146 1 : if (GetRow(enumRows, row, (int)poFeature->GetFID()) == OGRERR_NONE)
1147 : {
1148 0 : CPLError(CE_Failure, CPLE_AppDefined,
1149 : "A feature with same FID already exists");
1150 0 : return OGRERR_FAILURE;
1151 : }
1152 : }
1153 :
1154 1 : if ((int)m_oMapOGRFIDToFGDBFID.size() == m_nResyncThreshold)
1155 0 : ResyncIDs();
1156 : }
1157 :
1158 1568 : if (m_bSymlinkFlag && !CreateRealCopy())
1159 0 : return OGRERR_FAILURE;
1160 :
1161 1568 : if (m_bBulkLoadAllowed < 0)
1162 1 : m_bBulkLoadAllowed =
1163 1 : CPLTestBool(CPLGetConfigOption("FGDB_BULK_LOAD", "NO"));
1164 :
1165 1568 : if (m_bBulkLoadAllowed && !m_bBulkLoadInProgress)
1166 121 : StartBulkLoad();
1167 :
1168 1568 : hr = m_pTable->CreateRowObject(fgdb_row);
1169 :
1170 : /* Check the status of the Row create */
1171 1568 : if (FAILED(hr))
1172 : {
1173 0 : GDBErr(hr, "Failed at creating Row in CreateFeature.");
1174 0 : return OGRERR_FAILURE;
1175 : }
1176 :
1177 : /* As we have issues with fixed values for dates, or CURRENT_xxxx isn't */
1178 : /* handled anyway, let's fill ourselves all unset fields with their default
1179 : */
1180 1568 : poFeature->FillUnsetWithDefault(FALSE, nullptr);
1181 :
1182 : /* Populate the row with the feature content */
1183 1568 : if (PopulateRowWithFeature(fgdb_row, poFeature) != OGRERR_NONE)
1184 6 : return OGRERR_FAILURE;
1185 :
1186 : /* Cannot write to FID field - it is managed by GDB*/
1187 : // std::wstring wfield_name = StringToWString(m_strOIDFieldName);
1188 : // hr = fgdb_row.SetInteger(wfield_name, poFeature->GetFID());
1189 :
1190 : /* Write the row to the table */
1191 1562 : hr = m_pTable->Insert(fgdb_row);
1192 1562 : if (FAILED(hr))
1193 : {
1194 0 : GDBErr(hr, "Failed at writing Row to Table in CreateFeature.");
1195 0 : return OGRERR_FAILURE;
1196 : }
1197 :
1198 1562 : int32 oid = -1;
1199 1562 : if (!FAILED(hr = fgdb_row.GetOID(oid)))
1200 : {
1201 1562 : if (poFeature->GetFID() < 0)
1202 : {
1203 : // Avoid colliding with a user set FID
1204 1561 : while (m_oMapOGRFIDToFGDBFID.find(oid) !=
1205 3122 : m_oMapOGRFIDToFGDBFID.end())
1206 : {
1207 0 : EndBulkLoad();
1208 :
1209 0 : CPLDebug("FGDB", "Collision with user set FID %d", oid);
1210 0 : if (FAILED(hr = m_pTable->Delete(fgdb_row)))
1211 : {
1212 0 : GDBErr(hr, "Failed deleting row ");
1213 0 : return OGRERR_FAILURE;
1214 : }
1215 0 : hr = m_pTable->Insert(fgdb_row);
1216 0 : if (FAILED(hr))
1217 : {
1218 0 : GDBErr(hr,
1219 : "Failed at writing Row to Table in CreateFeature.");
1220 0 : return OGRERR_FAILURE;
1221 : }
1222 0 : if (FAILED(hr = fgdb_row.GetOID(oid)))
1223 : {
1224 0 : return OGRERR_FAILURE;
1225 : }
1226 : }
1227 1561 : poFeature->SetFID(oid);
1228 : }
1229 1 : else if ((int)poFeature->GetFID() != oid)
1230 : {
1231 1 : m_pDS->GetConnection()->SetFIDHackInProgress(TRUE);
1232 1 : m_oMapOGRFIDToFGDBFID[(int)poFeature->GetFID()] = oid;
1233 1 : m_oMapFGDBFIDToOGRFID[oid] = (int)poFeature->GetFID();
1234 : }
1235 : }
1236 :
1237 : #ifdef EXTENT_WORKAROUND
1238 : /* For WorkAroundExtentProblem() needs */
1239 1562 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1240 1562 : if (m_bLayerJustCreated && poGeom != nullptr && !poGeom->IsEmpty())
1241 : {
1242 1467 : OGREnvelope sFeatureGeomEnvelope;
1243 1467 : poGeom->getEnvelope(&sFeatureGeomEnvelope);
1244 1467 : if (!m_bLayerEnvelopeValid)
1245 : {
1246 96 : sLayerEnvelope = sFeatureGeomEnvelope;
1247 96 : m_bLayerEnvelopeValid = true;
1248 : }
1249 : else
1250 : {
1251 1371 : sLayerEnvelope.Merge(sFeatureGeomEnvelope);
1252 : }
1253 : }
1254 : #endif
1255 :
1256 1562 : return OGRERR_NONE;
1257 : }
1258 :
1259 : /************************************************************************/
1260 : /* PopulateRowWithFeature() */
1261 : /* */
1262 : /************************************************************************/
1263 :
1264 1575 : OGRErr FGdbLayer::PopulateRowWithFeature(Row &fgdb_row, OGRFeature *poFeature)
1265 : {
1266 3150 : ShapeBuffer shape;
1267 : fgdbError hr;
1268 :
1269 1575 : OGRFeatureDefn *poFeatureDefn = m_pFeatureDefn;
1270 1575 : int nFieldCount = poFeatureDefn->GetFieldCount();
1271 :
1272 : /* Copy the OGR visible fields (everything except geometry and FID) */
1273 1575 : int nCountBinaryField = 0;
1274 9396 : for (int i = 0; i < nFieldCount; i++)
1275 : {
1276 7822 : std::string field_name = poFeatureDefn->GetFieldDefn(i)->GetNameRef();
1277 7822 : std::wstring wfield_name = StringToWString(field_name);
1278 7822 : const std::string &strFieldType = m_vOGRFieldToESRIFieldType[i];
1279 :
1280 : /* Set empty fields to NULL */
1281 7822 : if (!poFeature->IsFieldSetAndNotNull(i))
1282 : {
1283 18 : if (strFieldType == "esriFieldTypeGlobalID")
1284 2 : continue;
1285 :
1286 16 : if (FAILED(hr = fgdb_row.SetNull(wfield_name)))
1287 : {
1288 1 : GDBErr(hr, "Failed setting field to NULL.");
1289 1 : return OGRERR_FAILURE;
1290 : }
1291 15 : continue;
1292 : }
1293 :
1294 : /* Set the information using the appropriate FGDB function */
1295 7804 : int nOGRFieldType = poFeatureDefn->GetFieldDefn(i)->GetType();
1296 :
1297 7804 : if (nOGRFieldType == OFTInteger)
1298 : {
1299 3048 : int fldvalue = poFeature->GetFieldAsInteger(i);
1300 3048 : if (strFieldType == "esriFieldTypeInteger")
1301 2025 : hr = fgdb_row.SetInteger(wfield_name, fldvalue);
1302 : else
1303 : {
1304 1023 : if (fldvalue < -32768 || fldvalue > 32767)
1305 : {
1306 0 : CPLErrorOnce(CE_Warning, CPLE_NotSupported,
1307 : "Value %d for field %s does not fit into a "
1308 : "short and will be clamped. "
1309 : "This warning will not be emitted any more",
1310 : fldvalue, field_name.c_str());
1311 0 : if (fldvalue < -32768)
1312 0 : fldvalue = -32768;
1313 : else
1314 0 : fldvalue = 32767;
1315 : }
1316 1023 : hr = fgdb_row.SetShort(wfield_name, (short)fldvalue);
1317 : }
1318 : }
1319 4756 : else if (nOGRFieldType == OFTReal || nOGRFieldType == OFTInteger64)
1320 : {
1321 : /* Doubles (we don't handle FGDB Floats) */
1322 1632 : double fldvalue = poFeature->GetFieldAsDouble(i);
1323 1632 : if (strFieldType == "esriFieldTypeDouble")
1324 609 : hr = fgdb_row.SetDouble(wfield_name, fldvalue);
1325 : else
1326 1632 : hr = fgdb_row.SetFloat(wfield_name, (float)fldvalue);
1327 : }
1328 3124 : else if (nOGRFieldType == OFTString)
1329 : {
1330 : /* Strings we convert to wstring */
1331 3176 : std::string fldvalue = poFeature->GetFieldAsString(i);
1332 1588 : if (strFieldType == "esriFieldTypeString")
1333 : {
1334 564 : std::wstring wfldvalue = StringToWString(fldvalue);
1335 564 : hr = fgdb_row.SetString(wfield_name, wfldvalue);
1336 : }
1337 : // Apparently, esriFieldTypeGlobalID can not be set, but is
1338 : // initialized by the FileGDB SDK itself.
1339 1024 : else if( strFieldType == "esriFieldTypeGUID" /*||
1340 : strFieldType == "esriFieldTypeGlobalID" */ )
1341 : {
1342 1024 : Guid guid;
1343 1024 : std::wstring wfldvalue = StringToWString(fldvalue);
1344 512 : if (FAILED(hr = guid.FromString(wfldvalue)))
1345 : {
1346 0 : CPLError(CE_Failure, CPLE_AppDefined,
1347 : "Cannot parse GUID value %s for field %s.",
1348 : fldvalue.c_str(), field_name.c_str());
1349 : }
1350 : else
1351 : {
1352 512 : hr = fgdb_row.SetGUID(wfield_name, guid);
1353 : }
1354 : }
1355 512 : else if (strFieldType == "esriFieldTypeXML")
1356 : {
1357 511 : hr = fgdb_row.SetXML(wfield_name, fldvalue);
1358 : }
1359 : else
1360 1 : hr = 0;
1361 : }
1362 1536 : else if (nOGRFieldType == OFTDateTime || nOGRFieldType == OFTDate)
1363 : {
1364 : /* Dates we need to coerce a little */
1365 : struct tm val;
1366 514 : poFeature->GetFieldAsDateTime(
1367 : i, &(val.tm_year), &(val.tm_mon), &(val.tm_mday),
1368 : &(val.tm_hour), &(val.tm_min), &(val.tm_sec), nullptr);
1369 514 : val.tm_year -= 1900;
1370 514 : val.tm_mon = val.tm_mon - 1; /* OGR months go 1-12, FGDB go 0-11 */
1371 514 : hr = fgdb_row.SetDate(wfield_name, val);
1372 : }
1373 1022 : else if (nOGRFieldType == OFTBinary)
1374 : {
1375 : /* Binary data */
1376 : int bytesize;
1377 1022 : GByte *bytes = poFeature->GetFieldAsBinary(i, &bytesize);
1378 1022 : if (bytesize)
1379 : {
1380 : /* This is annoying but SetBinary() doesn't keep the binary */
1381 : /* content. The ByteArray object must still be alive at */
1382 : /* the time Insert() is called */
1383 1022 : m_apoByteArrays[nCountBinaryField]->Allocate(bytesize);
1384 1022 : memcpy(m_apoByteArrays[nCountBinaryField]->byteArray, bytes,
1385 : bytesize);
1386 1022 : m_apoByteArrays[nCountBinaryField]->inUseLength = bytesize;
1387 1022 : hr = fgdb_row.SetBinary(wfield_name,
1388 1022 : *(m_apoByteArrays[nCountBinaryField]));
1389 : }
1390 : else
1391 : {
1392 0 : hr = fgdb_row.SetNull(wfield_name);
1393 : }
1394 1022 : nCountBinaryField++;
1395 : }
1396 : else
1397 : {
1398 : /* We can't handle this type */
1399 0 : CPLError(CE_Failure, CPLE_AppDefined,
1400 : "FGDB driver does not support OGR type.");
1401 0 : return OGRERR_FAILURE;
1402 : }
1403 :
1404 7804 : if (FAILED(hr))
1405 : {
1406 0 : CPLError(CE_Warning, CPLE_AppDefined,
1407 : "Cannot set value for field %s", field_name.c_str());
1408 : }
1409 : }
1410 :
1411 1574 : const auto eFlatLayerGeomType = wkbFlatten(m_pFeatureDefn->GetGeomType());
1412 1574 : if (eFlatLayerGeomType != wkbNone)
1413 : {
1414 : /* Done with attribute fields, now do geometry */
1415 1539 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1416 :
1417 1539 : if (poGeom == nullptr || poGeom->IsEmpty())
1418 : {
1419 : /* EMPTY geometries should be treated as NULL, see #4832 */
1420 61 : hr = fgdb_row.SetNull(StringToWString(m_strShapeFieldName));
1421 61 : if (FAILED(hr))
1422 : {
1423 1 : GDBErr(hr, "Failed at writing EMPTY Geometry to Row in "
1424 : "CreateFeature.");
1425 1 : return OGRERR_FAILURE;
1426 : }
1427 : }
1428 : else
1429 : {
1430 : /* Write geometry to a buffer */
1431 1478 : GByte *pabyShape = nullptr;
1432 1478 : int nShapeSize = 0;
1433 : OGRErr err;
1434 :
1435 : const OGRwkbGeometryType eType =
1436 1478 : wkbFlatten(poGeom->getGeometryType());
1437 1478 : if (m_bCreateMultipatch &&
1438 60 : (eType == wkbMultiPolygon || eType == wkbMultiSurface ||
1439 30 : eType == wkbTIN || eType == wkbPolyhedralSurface ||
1440 : eType == wkbGeometryCollection))
1441 : {
1442 60 : err = OGRWriteMultiPatchToShapeBin(poGeom, &pabyShape,
1443 : &nShapeSize);
1444 60 : if (err == OGRERR_UNSUPPORTED_OPERATION)
1445 0 : err = OGRWriteToShapeBin(poGeom, &pabyShape, &nShapeSize);
1446 : }
1447 : else
1448 : {
1449 1418 : if (((eFlatLayerGeomType == wkbLineString ||
1450 122 : eFlatLayerGeomType == wkbMultiLineString) &&
1451 1418 : (eType != wkbLineString && eType != wkbMultiLineString)) ||
1452 1417 : ((eFlatLayerGeomType == wkbPolygon ||
1453 170 : eFlatLayerGeomType == wkbMultiPolygon) &&
1454 1417 : (eType != wkbPolygon && eType != wkbMultiPolygon)) ||
1455 351 : ((eFlatLayerGeomType == wkbPoint ||
1456 1126 : eFlatLayerGeomType == wkbMultiPoint) &&
1457 : eType != eFlatLayerGeomType))
1458 : {
1459 : // Otherwise crash in the SDK...
1460 4 : CPLError(
1461 : CE_Failure, CPLE_NotSupported,
1462 : "Geometry type %s not supported in layer of type %s",
1463 : OGRToOGCGeomType(eType),
1464 : OGRToOGCGeomType(eFlatLayerGeomType));
1465 4 : return OGRERR_FAILURE;
1466 : }
1467 :
1468 1414 : err = OGRWriteToShapeBin(poGeom, &pabyShape, &nShapeSize);
1469 : }
1470 1474 : if (err != OGRERR_NONE)
1471 : {
1472 0 : CPLFree(pabyShape);
1473 0 : return err;
1474 : }
1475 :
1476 : /* Copy it into a ShapeBuffer */
1477 1474 : if (nShapeSize > 0)
1478 : {
1479 1474 : shape.Allocate(nShapeSize);
1480 1474 : memcpy(shape.shapeBuffer, pabyShape, nShapeSize);
1481 1474 : shape.inUseLength = nShapeSize;
1482 : }
1483 :
1484 : /* Free the shape buffer */
1485 1474 : CPLFree(pabyShape);
1486 :
1487 : /* Write ShapeBuffer into the Row */
1488 1474 : hr = fgdb_row.SetGeometry(shape);
1489 1474 : if (FAILED(hr))
1490 : {
1491 0 : GDBErr(hr,
1492 : "Failed at writing Geometry to Row in CreateFeature.");
1493 0 : return OGRERR_FAILURE;
1494 : }
1495 : }
1496 : }
1497 :
1498 1569 : return OGRERR_NONE;
1499 : }
1500 :
1501 : /************************************************************************/
1502 : /* GetRow() */
1503 : /************************************************************************/
1504 :
1505 1820 : OGRErr FGdbLayer::GetRow(EnumRows &enumRows, Row &row, GIntBig nFID)
1506 : {
1507 : long hr;
1508 3640 : CPLString osQuery;
1509 :
1510 : /* Querying a 64bit FID causes a runtime exception in FileGDB... */
1511 1820 : if (!CPL_INT64_FITS_ON_INT32(nFID))
1512 : {
1513 0 : return OGRERR_FAILURE;
1514 : }
1515 :
1516 1820 : osQuery.Printf("%s = " CPL_FRMT_GIB, m_strOIDFieldName.c_str(), nFID);
1517 :
1518 1820 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields,
1519 : StringToWString(osQuery.c_str()), true,
1520 : enumRows)))
1521 : {
1522 0 : GDBErr(hr, "Failed fetching row ");
1523 0 : return OGRERR_FAILURE;
1524 : }
1525 :
1526 1820 : if (FAILED(hr = enumRows.Next(row)))
1527 : {
1528 0 : GDBErr(hr, "Failed fetching row ");
1529 0 : return OGRERR_FAILURE;
1530 : }
1531 :
1532 1820 : if (hr != S_OK)
1533 43 : return OGRERR_NON_EXISTING_FEATURE; // none found - but no failure
1534 :
1535 1777 : return OGRERR_NONE;
1536 : }
1537 :
1538 : /************************************************************************/
1539 : /* DeleteFeature() */
1540 : /************************************************************************/
1541 :
1542 38 : OGRErr FGdbLayer::DeleteFeature(GIntBig nFID)
1543 :
1544 : {
1545 : long hr;
1546 76 : EnumRows enumRows;
1547 76 : Row row;
1548 :
1549 38 : if (!m_pDS->GetUpdate() || m_pTable == nullptr)
1550 34 : return OGRERR_FAILURE;
1551 4 : if (!CPL_INT64_FITS_ON_INT32(nFID))
1552 0 : return OGRERR_NON_EXISTING_FEATURE;
1553 :
1554 4 : if (m_bSymlinkFlag && !CreateRealCopy())
1555 0 : return OGRERR_FAILURE;
1556 :
1557 4 : int nFID32 = (int)nFID;
1558 4 : std::map<int, int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID32);
1559 4 : if (oIter != m_oMapOGRFIDToFGDBFID.end())
1560 : {
1561 0 : nFID32 = oIter->second;
1562 0 : m_oMapFGDBFIDToOGRFID.erase(nFID32);
1563 0 : m_oMapOGRFIDToFGDBFID.erase(oIter);
1564 : }
1565 4 : else if (m_oMapFGDBFIDToOGRFID.find(nFID32) != m_oMapFGDBFIDToOGRFID.end())
1566 0 : return OGRERR_NON_EXISTING_FEATURE;
1567 :
1568 4 : EndBulkLoad();
1569 :
1570 4 : OGRErr eErr = GetRow(enumRows, row, nFID32);
1571 4 : if (eErr != OGRERR_NONE)
1572 3 : return eErr;
1573 :
1574 1 : if (FAILED(hr = m_pTable->Delete(row)))
1575 : {
1576 0 : GDBErr(hr, "Failed deleting row ");
1577 0 : return OGRERR_FAILURE;
1578 : }
1579 :
1580 1 : return OGRERR_NONE;
1581 : }
1582 :
1583 : /************************************************************************/
1584 : /* ISetFeature() */
1585 : /************************************************************************/
1586 :
1587 26 : OGRErr FGdbLayer::ISetFeature(OGRFeature *poFeature)
1588 :
1589 : {
1590 : long hr;
1591 52 : EnumRows enumRows;
1592 52 : Row row;
1593 :
1594 26 : if (!m_pDS->GetUpdate() || m_pTable == nullptr)
1595 17 : return OGRERR_FAILURE;
1596 :
1597 9 : GIntBig nFID64 = poFeature->GetFID();
1598 9 : if (nFID64 == OGRNullFID)
1599 : {
1600 0 : CPLError(CE_Failure, CPLE_AppDefined,
1601 : "SetFeature() with unset FID fails.");
1602 0 : return OGRERR_FAILURE;
1603 : }
1604 9 : if (!CPL_INT64_FITS_ON_INT32(nFID64))
1605 0 : return OGRERR_NON_EXISTING_FEATURE;
1606 :
1607 9 : EndBulkLoad();
1608 :
1609 9 : if (m_bSymlinkFlag && !CreateRealCopy())
1610 0 : return OGRERR_FAILURE;
1611 :
1612 9 : int nFID = (int)nFID64;
1613 9 : std::map<int, int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID);
1614 9 : if (oIter != m_oMapOGRFIDToFGDBFID.end())
1615 0 : nFID = oIter->second;
1616 9 : else if (m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID()) !=
1617 18 : m_oMapFGDBFIDToOGRFID.end())
1618 0 : return OGRERR_NON_EXISTING_FEATURE;
1619 :
1620 9 : OGRErr eErr = GetRow(enumRows, row, nFID);
1621 9 : if (eErr != OGRERR_NONE)
1622 2 : return eErr;
1623 :
1624 : /* Populate the row with the feature content */
1625 7 : if (PopulateRowWithFeature(row, poFeature) != OGRERR_NONE)
1626 0 : return OGRERR_FAILURE;
1627 :
1628 7 : if (FAILED(hr = m_pTable->Update(row)))
1629 : {
1630 0 : GDBErr(hr, "Failed updating row ");
1631 0 : return OGRERR_FAILURE;
1632 : }
1633 :
1634 7 : return OGRERR_NONE;
1635 : }
1636 :
1637 : /************************************************************************/
1638 : /* CreateFieldDefn() */
1639 : /************************************************************************/
1640 :
1641 1375 : char *FGdbLayer::CreateFieldDefn(OGRFieldDefn &oField, int bApproxOK,
1642 : std::string &fieldname_clean,
1643 : std::string &gdbFieldType)
1644 : {
1645 2750 : std::string fieldname = oField.GetNameRef();
1646 : // std::string fidname = std::string(GetFIDColumn());
1647 2750 : std::string nullable = (oField.IsNullable()) ? "true" : "false";
1648 :
1649 : /* Try to map the OGR type to an ESRI type */
1650 1375 : OGRFieldType fldtype = oField.GetType();
1651 1375 : if (!OGRToGDBFieldType(fldtype, oField.GetSubType(), &gdbFieldType))
1652 : {
1653 0 : GDBErr(-1, "Failed converting field type.");
1654 0 : return nullptr;
1655 : }
1656 :
1657 1375 : if (oField.GetType() == OFTInteger64 && !bApproxOK)
1658 : {
1659 0 : CPLError(CE_Failure, CPLE_AppDefined,
1660 : "Integer64 not supported in FileGDB");
1661 0 : return nullptr;
1662 : }
1663 :
1664 : const char *pszColumnTypes =
1665 1375 : CSLFetchNameValue(m_papszOptions, "COLUMN_TYPES");
1666 1375 : if (pszColumnTypes != nullptr)
1667 : {
1668 1327 : char **papszTokens = CSLTokenizeString2(pszColumnTypes, ",", 0);
1669 : const char *pszFieldType =
1670 1327 : CSLFetchNameValue(papszTokens, fieldname.c_str());
1671 1327 : if (pszFieldType != nullptr)
1672 : {
1673 : OGRFieldType fldtypeCheck;
1674 : OGRFieldSubType eSubType;
1675 409 : if (GDBToOGRFieldType(pszFieldType, &fldtypeCheck, &eSubType))
1676 : {
1677 409 : if (fldtypeCheck != fldtype)
1678 : {
1679 0 : CPLError(CE_Warning, CPLE_AppDefined,
1680 : "Ignoring COLUMN_TYPES=%s=%s : %s not consistent "
1681 : "with OGR data type",
1682 : fieldname.c_str(), pszFieldType, pszFieldType);
1683 : }
1684 : else
1685 409 : gdbFieldType = pszFieldType;
1686 : }
1687 : else
1688 0 : CPLError(CE_Warning, CPLE_AppDefined,
1689 : "Ignoring COLUMN_TYPES=%s=%s : %s not recognized",
1690 : fieldname.c_str(), pszFieldType, pszFieldType);
1691 : }
1692 1327 : CSLDestroy(papszTokens);
1693 : }
1694 :
1695 1375 : if (!fieldname_clean.empty())
1696 : {
1697 0 : oField.SetName(fieldname_clean.c_str());
1698 : }
1699 : else
1700 : {
1701 : /* Clean field names */
1702 : std::wstring wfieldname_clean =
1703 1375 : FGDBLaunderName(StringToWString(fieldname));
1704 :
1705 1375 : if (m_bLaunderReservedKeywords)
1706 1375 : wfieldname_clean = FGDBEscapeReservedKeywords(wfieldname_clean);
1707 :
1708 : /* Truncate to 64 characters */
1709 1375 : constexpr size_t FIELD_NAME_MAX_SIZE = 64;
1710 1375 : if (wfieldname_clean.size() > FIELD_NAME_MAX_SIZE)
1711 2 : wfieldname_clean.resize(FIELD_NAME_MAX_SIZE);
1712 :
1713 : /* Ensures uniqueness of field name */
1714 1375 : int numRenames = 1;
1715 1378 : while ((m_pFeatureDefn->GetFieldIndex(
1716 2756 : WStringToString(wfieldname_clean).c_str()) >= 0) &&
1717 : (numRenames < 10))
1718 : {
1719 9 : wfieldname_clean = StringToWString(
1720 : CPLSPrintf("%s_%d",
1721 6 : WStringToString(wfieldname_clean.substr(
1722 : 0, FIELD_NAME_MAX_SIZE - 2))
1723 : .c_str(),
1724 3 : numRenames));
1725 3 : numRenames++;
1726 : }
1727 1375 : while ((m_pFeatureDefn->GetFieldIndex(
1728 2750 : WStringToString(wfieldname_clean).c_str()) >= 0) &&
1729 : (numRenames < 100))
1730 : {
1731 0 : wfieldname_clean = StringToWString(
1732 : CPLSPrintf("%s_%d",
1733 0 : WStringToString(wfieldname_clean.substr(
1734 : 0, FIELD_NAME_MAX_SIZE - 3))
1735 : .c_str(),
1736 0 : numRenames));
1737 0 : numRenames++;
1738 : }
1739 :
1740 1375 : fieldname_clean = WStringToString(wfieldname_clean);
1741 1375 : if (fieldname_clean != fieldname)
1742 : {
1743 10 : if (!bApproxOK ||
1744 5 : (m_pFeatureDefn->GetFieldIndex(fieldname_clean.c_str()) >= 0))
1745 : {
1746 0 : CPLError(CE_Failure, CPLE_NotSupported,
1747 : "Failed to add field named '%s'", fieldname.c_str());
1748 0 : return nullptr;
1749 : }
1750 5 : CPLError(CE_Warning, CPLE_NotSupported,
1751 : "Normalized/laundered field name: '%s' to '%s'",
1752 : fieldname.c_str(), fieldname_clean.c_str());
1753 :
1754 5 : oField.SetName(fieldname_clean.c_str());
1755 : }
1756 : }
1757 :
1758 : /* Then the Field definition */
1759 1375 : CPLXMLNode *defn_xml = CPLCreateXMLNode(nullptr, CXT_Element, "esri:Field");
1760 :
1761 : /* Add the XML attributes to the Field node */
1762 1375 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi",
1763 : "http://www.w3.org/2001/XMLSchema-instance");
1764 1375 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs",
1765 : "http://www.w3.org/2001/XMLSchema");
1766 1375 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri",
1767 : "http://www.esri.com/schemas/ArcGIS/10.1");
1768 1375 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:Field");
1769 :
1770 : /* Basic field information */
1771 1375 : CPLCreateXMLElementAndValue(defn_xml, "Name", fieldname_clean.c_str());
1772 1375 : CPLCreateXMLElementAndValue(defn_xml, "Type", gdbFieldType.c_str());
1773 1375 : CPLCreateXMLElementAndValue(defn_xml, "IsNullable", nullable.c_str());
1774 :
1775 : /* Get the Length */
1776 1375 : int nLength = 0;
1777 1375 : GDBFieldTypeToLengthInBytes(gdbFieldType, nLength);
1778 1375 : if (oField.GetType() == OFTString)
1779 : {
1780 320 : const int nFieldWidth = oField.GetWidth();
1781 320 : if (nFieldWidth > 0)
1782 8 : nLength = nFieldWidth;
1783 : }
1784 :
1785 : /* Write out the Length */
1786 : char buf[100];
1787 1375 : snprintf(buf, 100, "%d", nLength);
1788 1375 : CPLCreateXMLElementAndValue(defn_xml, "Length", buf);
1789 :
1790 : // According to https://resources.arcgis.com/en/help/arcobjects-java/api/arcobjects/com/esri/arcgis/geodatabase/Field.html#getPrecision()
1791 : // always 0
1792 1375 : CPLCreateXMLElementAndValue(defn_xml, "Precision", "0");
1793 :
1794 : // According to https://resources.arcgis.com/en/help/arcobjects-java/api/arcobjects/com/esri/arcgis/geodatabase/Field.html#getScale()
1795 : // always 0
1796 1375 : CPLCreateXMLElementAndValue(defn_xml, "Scale", "0");
1797 :
1798 1375 : const char *pszAlternativeName = oField.GetAlternativeNameRef();
1799 1375 : if (pszAlternativeName != nullptr && pszAlternativeName[0])
1800 : {
1801 2 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszAlternativeName);
1802 : }
1803 1373 : else if (fieldname != fieldname_clean)
1804 : {
1805 : /* Attempt to preserve the original fieldname */
1806 5 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", fieldname.c_str());
1807 : }
1808 :
1809 1375 : if (oField.GetDefault() != nullptr)
1810 : {
1811 12 : const char *pszDefault = oField.GetDefault();
1812 : /*int nYear, nMonth, nDay, nHour, nMinute;
1813 : float fSecond;*/
1814 12 : if (oField.GetType() == OFTString)
1815 : {
1816 2 : CPLString osVal = pszDefault;
1817 1 : if (osVal[0] == '\'' && osVal.back() == '\'')
1818 : {
1819 1 : osVal = osVal.substr(1);
1820 1 : osVal.pop_back();
1821 1 : char *pszTmp = CPLUnescapeString(osVal, nullptr, CPLES_SQL);
1822 1 : osVal = pszTmp;
1823 1 : CPLFree(pszTmp);
1824 : }
1825 : CPLXMLNode *psDefaultValue =
1826 1 : CPLCreateXMLElementAndValue(defn_xml, "DefaultValue", osVal);
1827 1 : FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:string");
1828 : }
1829 11 : else if (oField.GetType() == OFTInteger &&
1830 13 : !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSmallInteger") &&
1831 2 : CPLGetValueType(pszDefault) == CPL_VALUE_INTEGER)
1832 : {
1833 2 : CPLXMLNode *psDefaultValue = CPLCreateXMLElementAndValue(
1834 : defn_xml, "DefaultValue", pszDefault);
1835 2 : FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:int");
1836 : }
1837 9 : else if (oField.GetType() == OFTReal &&
1838 16 : !EQUAL(gdbFieldType.c_str(), "esriFieldTypeSingle") &&
1839 7 : CPLGetValueType(pszDefault) != CPL_VALUE_STRING)
1840 : {
1841 1 : CPLXMLNode *psDefaultValue = CPLCreateXMLElementAndValue(
1842 : defn_xml, "DefaultValue", pszDefault);
1843 1 : FGDB_CPLAddXMLAttribute(psDefaultValue, "xsi:type", "xs:double");
1844 : }
1845 : /*else if( oField.GetType() == OFTDateTime &&
1846 : sscanf(pszDefault, "'%d/%d/%d %d:%d:%f'", &nYear, &nMonth,
1847 : &nDay, &nHour, &nMinute, &fSecond) == 6 )
1848 : {
1849 : CPLXMLNode* psDefaultValue =
1850 : CPLCreateXMLElementAndValue(defn_xml, "DefaultValue",
1851 : CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
1852 : nYear, nMonth, nDay, nHour, nMinute,
1853 : (int)(fSecond + 0.5))); FGDB_CPLAddXMLAttribute(psDefaultValue,
1854 : "xsi:type", "xs:dateTime");
1855 : }*/
1856 : }
1857 : /* <DefaultValue xsi:type="xs:string">afternoon</DefaultValue> */
1858 :
1859 1375 : const auto &osDomainName = oField.GetDomainName();
1860 1375 : if (!osDomainName.empty())
1861 : {
1862 3 : const auto poDomain = m_pDS->GetFieldDomain(osDomainName);
1863 3 : if (poDomain)
1864 : {
1865 6 : std::string failureReason;
1866 : std::string osXML =
1867 6 : BuildXMLFieldDomainDef(poDomain, true, failureReason);
1868 3 : if (!osXML.empty())
1869 : {
1870 3 : auto psDomain = CPLParseXMLString(osXML.c_str());
1871 3 : if (psDomain)
1872 : {
1873 3 : CPLFree(psDomain->pszValue);
1874 3 : psDomain->pszValue = CPLStrdup("Domain");
1875 3 : CPLAddXMLChild(defn_xml, psDomain);
1876 : }
1877 : }
1878 : }
1879 : }
1880 :
1881 : /* Convert our XML tree into a string for FGDB */
1882 1375 : char *defn_str = CPLSerializeXMLTree(defn_xml);
1883 1375 : CPLDebug("FGDB", "CreateField() generated XML for FGDB\n%s", defn_str);
1884 :
1885 : /* Free the XML */
1886 1375 : CPLDestroyXMLNode(defn_xml);
1887 :
1888 1375 : return defn_str;
1889 : }
1890 :
1891 : /************************************************************************/
1892 : /* CreateField() */
1893 : /* Build up an FGDB XML field definition and use it to create a Field */
1894 : /* Update the OGRFeatureDefn to reflect the new field. */
1895 : /* */
1896 : /************************************************************************/
1897 :
1898 1375 : OGRErr FGdbLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK)
1899 : {
1900 2750 : OGRFieldDefn oField(poField);
1901 2750 : std::string fieldname_clean;
1902 2750 : std::string gdbFieldType;
1903 :
1904 1375 : if (!m_pDS->GetUpdate() || m_pTable == nullptr)
1905 0 : return OGRERR_FAILURE;
1906 :
1907 : char *defn_str =
1908 1375 : CreateFieldDefn(oField, bApproxOK, fieldname_clean, gdbFieldType);
1909 1375 : if (defn_str == nullptr)
1910 0 : return OGRERR_FAILURE;
1911 :
1912 : /* Add the FGDB Field to the FGDB Table. */
1913 1375 : fgdbError hr = m_pTable->AddField(defn_str);
1914 :
1915 1375 : CPLFree(defn_str);
1916 :
1917 : /* Check the status of the Field add */
1918 1375 : if (FAILED(hr))
1919 : {
1920 0 : GDBErr(hr, "Failed at creating Field for " +
1921 0 : std::string(oField.GetNameRef()));
1922 0 : return OGRERR_FAILURE;
1923 : }
1924 :
1925 : /* Now add the OGRFieldDefn to the OGRFeatureDefn */
1926 1375 : m_pFeatureDefn->AddFieldDefn(&oField);
1927 :
1928 1375 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldname_clean));
1929 1375 : m_vOGRFieldToESRIFieldType.push_back(gdbFieldType);
1930 :
1931 1375 : if (oField.GetType() == OFTBinary)
1932 204 : m_apoByteArrays.push_back(new ByteArray());
1933 :
1934 : /* All done and happy */
1935 1375 : return OGRERR_NONE;
1936 : }
1937 :
1938 : /************************************************************************/
1939 : /* DeleteField() */
1940 : /************************************************************************/
1941 :
1942 1 : OGRErr FGdbLayer::DeleteField(int iFieldToDelete)
1943 : {
1944 :
1945 1 : if (!m_pDS->GetUpdate() || m_pTable == nullptr)
1946 0 : return OGRERR_FAILURE;
1947 :
1948 1 : if (iFieldToDelete < 0 || iFieldToDelete >= m_pFeatureDefn->GetFieldCount())
1949 : {
1950 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
1951 0 : return OGRERR_FAILURE;
1952 : }
1953 :
1954 1 : ResetReading();
1955 :
1956 : const char *pszFieldName =
1957 1 : m_pFeatureDefn->GetFieldDefn(iFieldToDelete)->GetNameRef();
1958 :
1959 : fgdbError hr;
1960 1 : if (FAILED(hr = m_pTable->DeleteField(StringToWString(pszFieldName))))
1961 : {
1962 0 : GDBErr(hr, "Failed deleting field " + std::string(pszFieldName));
1963 0 : return OGRERR_FAILURE;
1964 : }
1965 :
1966 0 : m_vOGRFieldToESRIField.erase(m_vOGRFieldToESRIField.begin() +
1967 1 : iFieldToDelete);
1968 0 : m_vOGRFieldToESRIFieldType.erase(m_vOGRFieldToESRIFieldType.begin() +
1969 1 : iFieldToDelete);
1970 :
1971 1 : return m_pFeatureDefn->DeleteFieldDefn(iFieldToDelete);
1972 : }
1973 :
1974 : #ifdef AlterFieldDefn_implemented_but_not_working
1975 :
1976 : /************************************************************************/
1977 : /* AlterFieldDefn() */
1978 : /************************************************************************/
1979 :
1980 : OGRErr FGdbLayer::AlterFieldDefn(int iFieldToAlter,
1981 : OGRFieldDefn *poNewFieldDefn, int nFlags)
1982 : {
1983 :
1984 : if (!m_pDS->GetUpdate() || m_pTable == NULL)
1985 : return OGRERR_FAILURE;
1986 :
1987 : if (iFieldToAlter < 0 || iFieldToAlter >= m_pFeatureDefn->GetFieldCount())
1988 : {
1989 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
1990 : return OGRERR_FAILURE;
1991 : }
1992 :
1993 : OGRFieldDefn *poFieldDefn = m_pFeatureDefn->GetFieldDefn(iFieldToAlter);
1994 : OGRFieldDefn oField(poFieldDefn);
1995 :
1996 : if (nFlags & ALTER_TYPE_FLAG)
1997 : {
1998 : oField.SetSubType(OFSTNone);
1999 : oField.SetType(poNewFieldDefn->GetType());
2000 : oField.SetSubType(poNewFieldDefn->GetSubType());
2001 : }
2002 : if (nFlags & ALTER_NAME_FLAG)
2003 : {
2004 : if (strcmp(poNewFieldDefn->GetNameRef(), oField.GetNameRef()) != 0)
2005 : {
2006 : CPLError(CE_Failure, CPLE_NotSupported,
2007 : "Altering field name is not supported");
2008 : return OGRERR_FAILURE;
2009 : }
2010 : oField.SetName(poNewFieldDefn->GetNameRef());
2011 : }
2012 : if (nFlags & ALTER_WIDTH_PRECISION_FLAG)
2013 : {
2014 : if (oField.GetType() == OFTString)
2015 : oField.SetWidth(poNewFieldDefn->GetWidth());
2016 : }
2017 :
2018 : std::string fieldname_clean =
2019 : WStringToString(m_vOGRFieldToESRIField[iFieldToAlter]);
2020 : std::string gdbFieldType;
2021 :
2022 : char *defn_str =
2023 : CreateFieldDefn(oField, TRUE, fieldname_clean, gdbFieldType);
2024 : if (defn_str == NULL)
2025 : return OGRERR_FAILURE;
2026 :
2027 : ResetReading();
2028 :
2029 : /* Add the FGDB Field to the FGDB Table. */
2030 : fgdbError hr = m_pTable->AlterField(defn_str);
2031 :
2032 : CPLFree(defn_str);
2033 :
2034 : /* Check the status of the AlterField */
2035 : if (FAILED(hr))
2036 : {
2037 : GDBErr(hr,
2038 : "Failed at altering field " + std::string(oField.GetNameRef()));
2039 : return OGRERR_FAILURE;
2040 : }
2041 :
2042 : m_vOGRFieldToESRIFieldType[iFieldToAlter] = gdbFieldType;
2043 :
2044 : poFieldDefn->SetSubType(OFSTNone);
2045 : poFieldDefn->SetType(oField.GetType());
2046 : poFieldDefn->SetType(oField.GetSubType());
2047 : poFieldDefn->SetWidth(oField.GetWidth());
2048 :
2049 : return OGRERR_NONE;
2050 : }
2051 : #endif // AlterFieldDefn_implemented_but_not_working
2052 :
2053 : /************************************************************************/
2054 : /* XMLSpatialReference() */
2055 : /* Build up an XML representation of an OGRSpatialReference. */
2056 : /* Used in layer creation. */
2057 : /* Fill oCoordPrec. */
2058 : /************************************************************************/
2059 :
2060 : static CPLXMLNode *
2061 139 : XMLSpatialReference(const OGRGeomFieldDefn *poSrcGeomFieldDefn,
2062 : CSLConstList papszOptions,
2063 : OGRGeomCoordinatePrecision &oCoordPrec)
2064 : {
2065 139 : const auto poSRS = poSrcGeomFieldDefn->GetSpatialRef();
2066 :
2067 : /* We always need a SpatialReference */
2068 : CPLXMLNode *srs_xml =
2069 139 : CPLCreateXMLNode(nullptr, CXT_Element, "SpatialReference");
2070 :
2071 : /* Extract the WKID before morphing */
2072 139 : int nSRID = 0;
2073 139 : if (poSRS && poSRS->GetAuthorityCode(nullptr))
2074 : {
2075 127 : nSRID = atoi(poSRS->GetAuthorityCode(nullptr));
2076 : }
2077 :
2078 : /* NULL poSRS => UnknownCoordinateSystem */
2079 139 : if (!poSRS)
2080 : {
2081 5 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type",
2082 : "esri:UnknownCoordinateSystem");
2083 : }
2084 : else
2085 : {
2086 : /* Set the SpatialReference type attribute correctly for GEOGCS/PROJCS
2087 : */
2088 134 : if (poSRS->IsProjected())
2089 7 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type",
2090 : "esri:ProjectedCoordinateSystem");
2091 : else
2092 127 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type",
2093 : "esri:GeographicCoordinateSystem");
2094 :
2095 : /* Add the WKT to the XML */
2096 134 : SpatialReferenceInfo oESRI_SRS;
2097 :
2098 : /* Do we have a known SRID ? If so, directly query the ESRI SRS DB */
2099 261 : if (nSRID &&
2100 127 : SpatialReferences::FindSpatialReferenceBySRID(nSRID, oESRI_SRS))
2101 : {
2102 126 : CPLDebug("FGDB",
2103 : "Layer SRS has a SRID (%d). Using WKT from ESRI SRS "
2104 : "DBFound perfect match. ",
2105 : nSRID);
2106 126 : CPLCreateXMLElementAndValue(
2107 252 : srs_xml, "WKT", WStringToString(oESRI_SRS.srtext).c_str());
2108 : }
2109 : else
2110 : {
2111 : /* Make a clone so we can morph it without morphing the original */
2112 8 : OGRSpatialReference *poSRSClone = poSRS->Clone();
2113 :
2114 : /* Flip the WKT to ESRI form, return UnknownCoordinateSystem if we
2115 : * can't */
2116 8 : if (poSRSClone->morphToESRI() != OGRERR_NONE)
2117 : {
2118 0 : delete poSRSClone;
2119 0 : FGDB_CPLAddXMLAttribute(srs_xml, "xsi:type",
2120 : "esri:UnknownCoordinateSystem");
2121 0 : return srs_xml;
2122 : }
2123 :
2124 8 : char *wkt = nullptr;
2125 8 : poSRSClone->exportToWkt(&wkt);
2126 8 : if (wkt)
2127 : {
2128 16 : std::vector<int> oaiCandidateSRS;
2129 8 : nSRID = 0;
2130 :
2131 : // Ask PROJ which known SRS matches poSRS
2132 8 : int nEntries = 0;
2133 8 : int *panMatchConfidence = nullptr;
2134 : auto pahSRS =
2135 8 : poSRS->FindMatches(nullptr, &nEntries, &panMatchConfidence);
2136 148 : for (int i = 0; i < nEntries; ++i)
2137 : {
2138 143 : if (panMatchConfidence[i] >= 70)
2139 : {
2140 : // Look for candidates in the EPSG/ESRI namespace,
2141 : // and find the correspond ESRI SRS from the code
2142 : const char *pszAuthName =
2143 4 : OSRGetAuthorityName(pahSRS[i], nullptr);
2144 : const char *pszAuthCode =
2145 4 : OSRGetAuthorityCode(pahSRS[i], nullptr);
2146 8 : if (pszAuthName &&
2147 4 : (EQUAL(pszAuthName, "EPSG") ||
2148 4 : EQUAL(pszAuthName, "ESRI")) &&
2149 8 : pszAuthCode &&
2150 4 : SpatialReferences::FindSpatialReferenceBySRID(
2151 : atoi(pszAuthCode), oESRI_SRS))
2152 : {
2153 : const std::string osESRI_WKT =
2154 3 : WStringToString(oESRI_SRS.srtext);
2155 3 : OGRSpatialReference oSRS_FromESRI;
2156 3 : oSRS_FromESRI.SetAxisMappingStrategy(
2157 : OAMS_TRADITIONAL_GIS_ORDER);
2158 3 : if (oSRS_FromESRI.importFromWkt(
2159 6 : osESRI_WKT.c_str()) == OGRERR_NONE &&
2160 3 : poSRSClone->IsSame(&oSRS_FromESRI))
2161 : {
2162 3 : if (panMatchConfidence[i] == 100)
2163 : {
2164 : /* Exact match found (not sure this case
2165 : * happens) */
2166 3 : nSRID = oESRI_SRS.auth_srid;
2167 3 : break;
2168 : }
2169 0 : oaiCandidateSRS.push_back(oESRI_SRS.auth_srid);
2170 : }
2171 : }
2172 : }
2173 : }
2174 8 : OSRFreeSRSArray(pahSRS);
2175 8 : CPLFree(panMatchConfidence);
2176 :
2177 8 : if (nSRID != 0)
2178 : {
2179 3 : CPLDebug("FGDB",
2180 : "Found perfect match in ESRI SRS DB "
2181 : "for layer SRS. SRID is %d",
2182 : nSRID);
2183 : }
2184 5 : else if (oaiCandidateSRS.empty())
2185 : {
2186 5 : CPLDebug(
2187 : "FGDB",
2188 : "Did not found a match in ESRI SRS DB for layer SRS. "
2189 : "Using morphed SRS WKT. Failure is to be expected");
2190 : }
2191 0 : else if (oaiCandidateSRS.size() == 1)
2192 : {
2193 0 : nSRID = oaiCandidateSRS[0];
2194 0 : if (SpatialReferences::FindSpatialReferenceBySRID(
2195 : nSRID, oESRI_SRS))
2196 : {
2197 0 : CPLDebug("FGDB",
2198 : "Found a single match in ESRI SRS DB "
2199 : "for layer SRS. SRID is %d",
2200 : nSRID);
2201 0 : nSRID = oESRI_SRS.auth_srid;
2202 0 : CPLFree(wkt);
2203 0 : wkt = CPLStrdup(
2204 0 : WStringToString(oESRI_SRS.srtext).c_str());
2205 : }
2206 : }
2207 : else
2208 : {
2209 : /* Not sure this case can happen */
2210 :
2211 0 : CPLString osCandidateSRS;
2212 0 : for (int i = 0; i < (int)oaiCandidateSRS.size() && i < 10;
2213 : i++)
2214 : {
2215 0 : if (!osCandidateSRS.empty())
2216 0 : osCandidateSRS += ", ";
2217 0 : osCandidateSRS += CPLSPrintf("%d", oaiCandidateSRS[i]);
2218 : }
2219 0 : if (oaiCandidateSRS.size() > 10)
2220 0 : osCandidateSRS += "...";
2221 :
2222 0 : CPLDebug(
2223 : "FGDB",
2224 : "As several candidates (%s) have been found in "
2225 : "ESRI SRS DB for layer SRS, none has been selected. "
2226 : "Using morphed SRS WKT. Failure is to be expected",
2227 : osCandidateSRS.c_str());
2228 : }
2229 :
2230 8 : CPLCreateXMLElementAndValue(srs_xml, "WKT", wkt);
2231 8 : CPLFree(wkt);
2232 : }
2233 :
2234 : /* Dispose of our close */
2235 8 : delete poSRSClone;
2236 : }
2237 : }
2238 :
2239 : /* Handle Origin/Scale/Tolerance */
2240 :
2241 139 : oCoordPrec = GDBGridSettingsFromOGR(poSrcGeomFieldDefn, papszOptions);
2242 : const auto oIter =
2243 139 : oCoordPrec.oFormatSpecificOptions.find("FileGeodatabase");
2244 : // Note: test is true
2245 139 : if (oIter != oCoordPrec.oFormatSpecificOptions.end())
2246 : {
2247 139 : const auto &oGridsOptions = oIter->second;
2248 1529 : for (int i = 0; i < oGridsOptions.size(); ++i)
2249 : {
2250 1390 : char *pszKey = nullptr;
2251 1390 : const char *pszValue = CPLParseNameValue(oGridsOptions[i], &pszKey);
2252 1390 : if (pszKey && pszValue)
2253 : {
2254 1390 : CPLCreateXMLElementAndValue(srs_xml, pszKey, pszValue);
2255 : }
2256 1390 : CPLFree(pszKey);
2257 : }
2258 : }
2259 :
2260 : /* FGDB is always High Precision */
2261 139 : CPLCreateXMLElementAndValue(srs_xml, "HighPrecision", "true");
2262 :
2263 : /* Add the WKID to the XML */
2264 139 : const char *pszWKID = CSLFetchNameValue(papszOptions, "WKID");
2265 139 : if (pszWKID)
2266 0 : nSRID = atoi(pszWKID);
2267 139 : if (nSRID)
2268 : {
2269 129 : CPLCreateXMLElementAndValue(srs_xml, "WKID", CPLSPrintf("%d", nSRID));
2270 : }
2271 :
2272 139 : return srs_xml;
2273 : }
2274 :
2275 : /************************************************************************/
2276 : /* CreateFeatureDataset() */
2277 : /************************************************************************/
2278 :
2279 2 : bool FGdbLayer::CreateFeatureDataset(FGdbDataSource *pParentDataSource,
2280 : const std::string &feature_dataset_name,
2281 : const OGRGeomFieldDefn *poSrcGeomFieldDefn,
2282 : CSLConstList papszOptions)
2283 : {
2284 : /* XML node */
2285 2 : CPLXMLNode *xml_xml = CPLCreateXMLNode(nullptr, CXT_Element, "?xml");
2286 2 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
2287 2 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
2288 :
2289 : /* First build up a bare-bones feature definition */
2290 : CPLXMLNode *defn_xml =
2291 2 : CPLCreateXMLNode(nullptr, CXT_Element, "esri:DataElement");
2292 2 : CPLAddXMLSibling(xml_xml, defn_xml);
2293 :
2294 : /* Add the attributes to the DataElement */
2295 2 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi",
2296 : "http://www.w3.org/2001/XMLSchema-instance");
2297 2 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs",
2298 : "http://www.w3.org/2001/XMLSchema");
2299 2 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri",
2300 : "http://www.esri.com/schemas/ArcGIS/10.1");
2301 :
2302 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
2303 2 : FGDB_CPLAddXMLAttribute(defn_xml, "xsi:type", "esri:DEFeatureDataset");
2304 :
2305 : /* Add in more children */
2306 4 : std::string catalog_page = "\\" + feature_dataset_name;
2307 2 : CPLCreateXMLElementAndValue(defn_xml, "CatalogPath", catalog_page.c_str());
2308 2 : CPLCreateXMLElementAndValue(defn_xml, "Name", feature_dataset_name.c_str());
2309 2 : CPLCreateXMLElementAndValue(defn_xml, "ChildrenExpanded", "false");
2310 2 : CPLCreateXMLElementAndValue(defn_xml, "DatasetType",
2311 : "esriDTFeatureDataset");
2312 2 : CPLCreateXMLElementAndValue(defn_xml, "Versioned", "false");
2313 2 : CPLCreateXMLElementAndValue(defn_xml, "CanVersion", "false");
2314 :
2315 : /* Add in empty extent */
2316 2 : CPLXMLNode *extent_xml = CPLCreateXMLNode(nullptr, CXT_Element, "Extent");
2317 2 : FGDB_CPLAddXMLAttribute(extent_xml, "xsi:nil", "true");
2318 2 : CPLAddXMLChild(defn_xml, extent_xml);
2319 :
2320 : /* Add the SRS */
2321 2 : if (poSrcGeomFieldDefn)
2322 : {
2323 4 : OGRGeomCoordinatePrecision oCoordPrec;
2324 : CPLXMLNode *srs_xml =
2325 2 : XMLSpatialReference(poSrcGeomFieldDefn, papszOptions, oCoordPrec);
2326 2 : if (srs_xml)
2327 2 : CPLAddXMLChild(defn_xml, srs_xml);
2328 : }
2329 :
2330 : /* Convert our XML tree into a string for FGDB */
2331 2 : char *defn_str = CPLSerializeXMLTree(xml_xml);
2332 2 : CPLDestroyXMLNode(xml_xml);
2333 :
2334 : /* TODO, tie this to debugging levels */
2335 2 : CPLDebug("FGDB", "%s", defn_str);
2336 :
2337 : /* Create the FeatureDataset. */
2338 2 : Geodatabase *gdb = pParentDataSource->GetGDB();
2339 2 : fgdbError hr = gdb->CreateFeatureDataset(defn_str);
2340 :
2341 : /* Free the XML */
2342 2 : CPLFree(defn_str);
2343 :
2344 : /* Check table create status */
2345 2 : if (FAILED(hr))
2346 : {
2347 0 : return GDBErr(hr, "Failed at creating FeatureDataset " +
2348 0 : feature_dataset_name);
2349 : }
2350 :
2351 2 : return true;
2352 : }
2353 :
2354 : /************************************************************************/
2355 : /* Create() */
2356 : /* Build up an FGDB XML layer definition and use it to create a Table */
2357 : /* or Feature Class to work from. */
2358 : /* */
2359 : /* Layer creation options: */
2360 : /* FEATURE_DATASET, nest layer inside a FeatureDataset folder */
2361 : /* GEOMETRY_NAME, user-selected name for the geometry column */
2362 : /* FID/OID_NAME, user-selected name for the FID column */
2363 : /* XORIGIN, YORIGIN, ZORIGIN, origin of the snapping grid */
2364 : /* XYSCALE, ZSCALE, inverse resolution of the snapping grid */
2365 : /* XYTOLERANCE, ZTOLERANCE, snapping tolerance for topology/networks */
2366 : /* */
2367 : /************************************************************************/
2368 :
2369 150 : bool FGdbLayer::Create(FGdbDataSource *pParentDataSource,
2370 : const char *pszLayerNameIn,
2371 : const OGRGeomFieldDefn *poSrcGeomFieldDefn,
2372 : CSLConstList papszOptions)
2373 : {
2374 300 : std::string parent_path = "";
2375 300 : std::wstring wtable_path, wparent_path;
2376 300 : std::string geometry_name = FGDB_GEOMETRY_NAME;
2377 300 : std::string fid_name = FGDB_OID_NAME;
2378 300 : std::string esri_type;
2379 150 : bool has_z = false;
2380 150 : bool has_m = false;
2381 :
2382 : const auto eType =
2383 150 : poSrcGeomFieldDefn ? poSrcGeomFieldDefn->GetType() : wkbNone;
2384 150 : if (eType == wkbNone)
2385 13 : poSrcGeomFieldDefn = nullptr;
2386 :
2387 : #ifdef EXTENT_WORKAROUND
2388 150 : m_bLayerJustCreated = true;
2389 : #endif
2390 :
2391 : /* Launder the Layer name */
2392 300 : std::wstring wlayerName;
2393 :
2394 150 : wlayerName = FGDBLaunderName(StringToWString(pszLayerNameIn));
2395 150 : wlayerName = FGDBEscapeReservedKeywords(wlayerName);
2396 150 : wlayerName = FGDBEscapeUnsupportedPrefixes(wlayerName);
2397 :
2398 : // https://desktop.arcgis.com/en/arcmap/latest/manage-data/administer-file-gdbs/file-geodatabase-size-and-name-limits.htm
2399 : // document 160 character limit but
2400 : // https://desktop.arcgis.com/en/arcmap/latest/manage-data/tables/fundamentals-of-adding-and-deleting-fields.htm#GUID-8E190093-8F8F-4132-AF4F-B0C9220F76B3
2401 : // mentions 64. let be optimistic and aim for 160
2402 150 : constexpr size_t TABLE_NAME_MAX_SIZE = 160;
2403 150 : if (wlayerName.size() > TABLE_NAME_MAX_SIZE)
2404 2 : wlayerName.resize(TABLE_NAME_MAX_SIZE);
2405 :
2406 : /* Ensures uniqueness of layer name */
2407 150 : int numRenames = 1;
2408 153 : while ((pParentDataSource->GetLayerByName(
2409 306 : WStringToString(wlayerName).c_str()) != nullptr) &&
2410 : (numRenames < 10))
2411 : {
2412 9 : wlayerName = StringToWString(CPLSPrintf(
2413 : "%s_%d",
2414 6 : WStringToString(wlayerName.substr(0, TABLE_NAME_MAX_SIZE - 2))
2415 : .c_str(),
2416 3 : numRenames));
2417 3 : numRenames++;
2418 : }
2419 150 : while ((pParentDataSource->GetLayerByName(
2420 300 : WStringToString(wlayerName).c_str()) != nullptr) &&
2421 : (numRenames < 100))
2422 : {
2423 0 : wlayerName = StringToWString(CPLSPrintf(
2424 : "%s_%d",
2425 0 : WStringToString(wlayerName.substr(0, TABLE_NAME_MAX_SIZE - 3))
2426 : .c_str(),
2427 0 : numRenames));
2428 0 : numRenames++;
2429 : }
2430 :
2431 300 : const std::string layerName = WStringToString(wlayerName);
2432 150 : if (layerName != pszLayerNameIn)
2433 : {
2434 6 : CPLError(CE_Warning, CPLE_NotSupported,
2435 : "Normalized/laundered layer name: '%s' to '%s'",
2436 : pszLayerNameIn, layerName.c_str());
2437 : }
2438 :
2439 300 : std::string table_path = "\\" + std::string(layerName);
2440 :
2441 : /* Handle the FEATURE_DATASET case */
2442 150 : if (CSLFetchNameValue(papszOptions, "FEATURE_DATASET") != nullptr)
2443 : {
2444 : std::string feature_dataset =
2445 3 : CSLFetchNameValue(papszOptions, "FEATURE_DATASET");
2446 :
2447 : /* Check if FEATURE_DATASET exists. Otherwise create it */
2448 3 : std::vector<wstring> featuredatasets;
2449 3 : Geodatabase *gdb = pParentDataSource->GetGDB();
2450 3 : int bFeatureDataSetExists = FALSE;
2451 : fgdbError hr;
2452 3 : if (!FAILED(hr = gdb->GetChildDatasets(L"\\", L"Feature Dataset",
2453 : featuredatasets)))
2454 : {
2455 : std::wstring feature_dataset_with_slash =
2456 6 : L"\\" + StringToWString(feature_dataset);
2457 4 : for (unsigned int i = 0; i < featuredatasets.size(); i++)
2458 : {
2459 1 : if (featuredatasets[i] == feature_dataset_with_slash)
2460 1 : bFeatureDataSetExists = TRUE;
2461 : }
2462 : }
2463 :
2464 3 : if (!bFeatureDataSetExists)
2465 : {
2466 2 : bool rv = CreateFeatureDataset(pParentDataSource, feature_dataset,
2467 : poSrcGeomFieldDefn, papszOptions);
2468 2 : if (!rv)
2469 0 : return rv;
2470 : }
2471 :
2472 3 : table_path = "\\" + feature_dataset + table_path;
2473 3 : parent_path = "\\" + feature_dataset;
2474 : }
2475 :
2476 : /* Convert table_path into wstring */
2477 150 : wtable_path = StringToWString(table_path);
2478 150 : wparent_path = StringToWString(parent_path);
2479 :
2480 : /* Over-ride the geometry name if necessary */
2481 150 : if (CSLFetchNameValue(papszOptions, "GEOMETRY_NAME") != nullptr)
2482 2 : geometry_name = CSLFetchNameValue(papszOptions, "GEOMETRY_NAME");
2483 :
2484 : /* Over-ride the OID name if necessary */
2485 150 : if (CSLFetchNameValue(papszOptions, "FID") != nullptr)
2486 2 : fid_name = CSLFetchNameValue(papszOptions, "FID");
2487 148 : else if (CSLFetchNameValue(papszOptions, "OID_NAME") != nullptr)
2488 0 : fid_name = CSLFetchNameValue(papszOptions, "OID_NAME");
2489 :
2490 150 : m_bCreateMultipatch = CPLTestBool(
2491 : CSLFetchNameValueDef(papszOptions, "CREATE_MULTIPATCH", "NO"));
2492 :
2493 : /* Figure out our geometry type */
2494 150 : if (eType != wkbNone)
2495 : {
2496 137 : if (wkbFlatten(eType) == wkbUnknown)
2497 : {
2498 0 : return GDBErr(-1, "FGDB layers cannot be created with a wkbUnknown "
2499 0 : "layer geometry type.");
2500 : }
2501 137 : if (!OGRGeometryToGDB(eType, &esri_type, &has_z, &has_m))
2502 0 : return GDBErr(-1, "Unable to map OGR type to ESRI type");
2503 :
2504 137 : if (wkbFlatten(eType) == wkbMultiPolygon && m_bCreateMultipatch)
2505 : {
2506 6 : esri_type = "esriGeometryMultiPatch";
2507 6 : has_z = true;
2508 : }
2509 : // For TIN and PolyhedralSurface, default to create a multipatch,
2510 : // unless the user explicitly disabled it
2511 131 : else if ((wkbFlatten(eType) == wkbTIN ||
2512 137 : wkbFlatten(eType) == wkbPolyhedralSurface) &&
2513 6 : CPLTestBool(CSLFetchNameValueDef(papszOptions,
2514 : "CREATE_MULTIPATCH", "YES")))
2515 : {
2516 6 : m_bCreateMultipatch = true;
2517 6 : esri_type = "esriGeometryMultiPatch";
2518 6 : has_z = true;
2519 : }
2520 : }
2521 :
2522 150 : const auto eFlattenType = wkbFlatten(eType);
2523 150 : const bool bIsLine =
2524 150 : eFlattenType == wkbLineString || eFlattenType == wkbMultiLineString;
2525 150 : const bool bIsPolygon =
2526 150 : eFlattenType == wkbPolygon || eFlattenType == wkbMultiPolygon;
2527 :
2528 : const bool bCreateShapeLength =
2529 220 : (bIsLine || bIsPolygon) && !m_bCreateMultipatch &&
2530 70 : CPLTestBool(CSLFetchNameValueDef(
2531 150 : papszOptions, "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS", "NO"));
2532 : // Setting a non-default value doesn't work
2533 : const char *pszLengthFieldName =
2534 150 : CSLFetchNameValueDef(papszOptions, "LENGTH_FIELD_NAME", "Shape_Length");
2535 :
2536 : const bool bCreateShapeArea =
2537 193 : bIsPolygon && !m_bCreateMultipatch &&
2538 43 : CPLTestBool(CSLFetchNameValueDef(
2539 150 : papszOptions, "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS", "NO"));
2540 : // Setting a non-default value doesn't work
2541 : const char *pszAreaFieldName =
2542 150 : CSLFetchNameValueDef(papszOptions, "AREA_FIELD_NAME", "Shape_Area");
2543 :
2544 150 : m_bLaunderReservedKeywords =
2545 150 : CPLFetchBool(papszOptions, "LAUNDER_RESERVED_KEYWORDS", true);
2546 :
2547 : /* XML node */
2548 150 : CPLXMLNode *xml_xml = CPLCreateXMLNode(nullptr, CXT_Element, "?xml");
2549 150 : FGDB_CPLAddXMLAttribute(xml_xml, "version", "1.0");
2550 150 : FGDB_CPLAddXMLAttribute(xml_xml, "encoding", "UTF-8");
2551 :
2552 : /* First build up a bare-bones feature definition */
2553 : CPLXMLNode *defn_xml =
2554 150 : CPLCreateXMLNode(nullptr, CXT_Element, "esri:DataElement");
2555 150 : CPLAddXMLSibling(xml_xml, defn_xml);
2556 :
2557 : /* Add the attributes to the DataElement */
2558 150 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xsi",
2559 : "http://www.w3.org/2001/XMLSchema-instance");
2560 150 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:xs",
2561 : "http://www.w3.org/2001/XMLSchema");
2562 150 : FGDB_CPLAddXMLAttribute(defn_xml, "xmlns:esri",
2563 : "http://www.esri.com/schemas/ArcGIS/10.1");
2564 :
2565 : /* Need to set this to esri:DEFeatureDataset or esri:DETable */
2566 150 : FGDB_CPLAddXMLAttribute(
2567 : defn_xml, "xsi:type",
2568 : (eType == wkbNone ? "esri:DETable" : "esri:DEFeatureClass"));
2569 :
2570 : /* Add in more children */
2571 150 : CPLCreateXMLElementAndValue(defn_xml, "CatalogPath", table_path.c_str());
2572 150 : CPLCreateXMLElementAndValue(defn_xml, "Name", layerName.c_str());
2573 150 : CPLCreateXMLElementAndValue(defn_xml, "ChildrenExpanded", "false");
2574 :
2575 : /* WKB type of none implies this is a 'Table' otherwise it is a 'Feature
2576 : * Class' */
2577 : std::string datasettype =
2578 300 : (eType == wkbNone ? "esriDTTable" : "esriDTFeatureClass");
2579 150 : CPLCreateXMLElementAndValue(defn_xml, "DatasetType", datasettype.c_str());
2580 150 : CPLCreateXMLElementAndValue(defn_xml, "Versioned", "false");
2581 150 : CPLCreateXMLElementAndValue(defn_xml, "CanVersion", "false");
2582 :
2583 150 : if (CSLFetchNameValue(papszOptions, "CONFIGURATION_KEYWORD") != nullptr)
2584 0 : CPLCreateXMLElementAndValue(
2585 : defn_xml, "ConfigurationKeyword",
2586 : CSLFetchNameValue(papszOptions, "CONFIGURATION_KEYWORD"));
2587 :
2588 : /* We might need to make OID optional later, but OGR likes to have a FID */
2589 150 : CPLCreateXMLElementAndValue(defn_xml, "HasOID", "true");
2590 150 : CPLCreateXMLElementAndValue(defn_xml, "OIDFieldName", fid_name.c_str());
2591 :
2592 : /* Add in empty Fields */
2593 150 : CPLXMLNode *fields_xml = CPLCreateXMLNode(defn_xml, CXT_Element, "Fields");
2594 150 : FGDB_CPLAddXMLAttribute(fields_xml, "xsi:type", "esri:Fields");
2595 : CPLXMLNode *fieldarray_xml =
2596 150 : CPLCreateXMLNode(fields_xml, CXT_Element, "FieldArray");
2597 150 : FGDB_CPLAddXMLAttribute(fieldarray_xml, "xsi:type", "esri:ArrayOfField");
2598 :
2599 : /* Feature Classes have an implicit geometry column, so we'll add it at
2600 : * creation time */
2601 150 : CPLXMLNode *srs_xml = nullptr;
2602 300 : OGRGeomCoordinatePrecision oCoordPrec;
2603 150 : if (poSrcGeomFieldDefn)
2604 : {
2605 : CPLXMLNode *shape_xml =
2606 137 : CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
2607 137 : FGDB_CPLAddXMLAttribute(shape_xml, "xsi:type", "esri:Field");
2608 137 : CPLCreateXMLElementAndValue(shape_xml, "Name", geometry_name.c_str());
2609 137 : CPLCreateXMLElementAndValue(shape_xml, "Type", "esriFieldTypeGeometry");
2610 137 : if (CPLFetchBool(papszOptions, "GEOMETRY_NULLABLE", true))
2611 136 : CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "true");
2612 : else
2613 1 : CPLCreateXMLElementAndValue(shape_xml, "IsNullable", "false");
2614 137 : CPLCreateXMLElementAndValue(shape_xml, "Length", "0");
2615 137 : CPLCreateXMLElementAndValue(shape_xml, "Precision", "0");
2616 137 : CPLCreateXMLElementAndValue(shape_xml, "Scale", "0");
2617 137 : CPLCreateXMLElementAndValue(shape_xml, "Required", "true");
2618 : CPLXMLNode *geom_xml =
2619 137 : CPLCreateXMLNode(shape_xml, CXT_Element, "GeometryDef");
2620 137 : FGDB_CPLAddXMLAttribute(geom_xml, "xsi:type", "esri:GeometryDef");
2621 137 : CPLCreateXMLElementAndValue(geom_xml, "AvgNumPoints", "0");
2622 137 : CPLCreateXMLElementAndValue(geom_xml, "GeometryType",
2623 : esri_type.c_str());
2624 137 : CPLCreateXMLElementAndValue(geom_xml, "HasM",
2625 : (has_m ? "true" : "false"));
2626 137 : CPLCreateXMLElementAndValue(geom_xml, "HasZ",
2627 : (has_z ? "true" : "false"));
2628 :
2629 : /* Add the SRS if we have one */
2630 : srs_xml =
2631 137 : XMLSpatialReference(poSrcGeomFieldDefn, papszOptions, oCoordPrec);
2632 137 : if (srs_xml)
2633 137 : CPLAddXMLChild(geom_xml, srs_xml);
2634 : }
2635 :
2636 : /* All (?) Tables and Feature Classes will have an ObjectID */
2637 : CPLXMLNode *oid_xml =
2638 150 : CPLCreateXMLNode(fieldarray_xml, CXT_Element, "Field");
2639 150 : FGDB_CPLAddXMLAttribute(oid_xml, "xsi:type", "esri:Field");
2640 150 : CPLCreateXMLElementAndValue(oid_xml, "Name", fid_name.c_str());
2641 150 : CPLCreateXMLElementAndValue(oid_xml, "Type", "esriFieldTypeOID");
2642 150 : CPLCreateXMLElementAndValue(oid_xml, "IsNullable", "false");
2643 150 : CPLCreateXMLElementAndValue(oid_xml, "Length", "4");
2644 150 : CPLCreateXMLElementAndValue(oid_xml, "Precision", "0");
2645 150 : CPLCreateXMLElementAndValue(oid_xml, "Scale", "0");
2646 150 : CPLCreateXMLElementAndValue(oid_xml, "Required", "true");
2647 :
2648 : /* Add in empty Indexes */
2649 : CPLXMLNode *indexes_xml =
2650 150 : CPLCreateXMLNode(defn_xml, CXT_Element, "Indexes");
2651 150 : FGDB_CPLAddXMLAttribute(indexes_xml, "xsi:type", "esri:Indexes");
2652 : CPLXMLNode *indexarray_xml =
2653 150 : CPLCreateXMLNode(indexes_xml, CXT_Element, "IndexArray");
2654 150 : FGDB_CPLAddXMLAttribute(indexarray_xml, "xsi:type", "esri:ArrayOfIndex");
2655 :
2656 : /* CLSID http://forums.arcgis.com/threads/34536?p=118484#post118484 */
2657 150 : if (eType == wkbNone)
2658 : {
2659 13 : CPLCreateXMLElementAndValue(defn_xml, "CLSID",
2660 : "{7A566981-C114-11D2-8A28-006097AFF44E}");
2661 13 : CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
2662 : }
2663 : else
2664 : {
2665 137 : CPLCreateXMLElementAndValue(defn_xml, "CLSID",
2666 : "{52353152-891A-11D0-BEC6-00805F7C4268}");
2667 137 : CPLCreateXMLElementAndValue(defn_xml, "EXTCLSID", "");
2668 : }
2669 :
2670 : /* Set the alias for the Feature Class, check if we received an */
2671 : /* explicit one in the options vector. */
2672 150 : const char *pszLayerAlias = CSLFetchNameValue(papszOptions, "LAYER_ALIAS");
2673 150 : if (pszLayerAlias != nullptr)
2674 : {
2675 0 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerAlias);
2676 : }
2677 150 : else if (pszLayerNameIn != layerName)
2678 : {
2679 6 : CPLCreateXMLElementAndValue(defn_xml, "AliasName", pszLayerNameIn);
2680 : }
2681 :
2682 : /* Map from OGR WKB type to ESRI type */
2683 150 : if (eType != wkbNone)
2684 : {
2685 : /* Declare our feature type */
2686 137 : CPLCreateXMLElementAndValue(defn_xml, "FeatureType", "esriFTSimple");
2687 137 : CPLCreateXMLElementAndValue(defn_xml, "ShapeType", esri_type.c_str());
2688 137 : CPLCreateXMLElementAndValue(defn_xml, "ShapeFieldName",
2689 : geometry_name.c_str());
2690 :
2691 : /* Dimensionality */
2692 137 : CPLCreateXMLElementAndValue(defn_xml, "HasM",
2693 : (has_m ? "true" : "false"));
2694 137 : CPLCreateXMLElementAndValue(defn_xml, "HasZ",
2695 : (has_z ? "true" : "false"));
2696 :
2697 137 : CPLCreateXMLElementAndValue(defn_xml, "HasSpatialIndex", "true");
2698 :
2699 : /* These field are required for Arcmap to display aliases correctly */
2700 137 : if (bCreateShapeArea)
2701 2 : CPLCreateXMLElementAndValue(defn_xml, "AreaFieldName",
2702 : pszAreaFieldName);
2703 : else
2704 135 : CPLCreateXMLNode(defn_xml, CXT_Element, "AreaFieldName");
2705 :
2706 137 : if (bCreateShapeLength)
2707 4 : CPLCreateXMLElementAndValue(defn_xml, "LengthFieldName",
2708 : pszLengthFieldName);
2709 : else
2710 133 : CPLCreateXMLNode(defn_xml, CXT_Element, "LengthFieldName");
2711 :
2712 : /* We can't know the extent at this point <Extent xsi:nil='true'/> */
2713 : CPLXMLNode *extn_xml =
2714 137 : CPLCreateXMLNode(defn_xml, CXT_Element, "Extent");
2715 137 : FGDB_CPLAddXMLAttribute(extn_xml, "xsi:nil", "true");
2716 : }
2717 :
2718 : /* Feature Class with known SRS gets an SRS entry */
2719 150 : if (eType != wkbNone && srs_xml != nullptr)
2720 : {
2721 137 : CPLAddXMLChild(defn_xml, CPLCloneXMLTree(srs_xml));
2722 : }
2723 :
2724 : /* Convert our XML tree into a string for FGDB */
2725 : char *defn_str;
2726 :
2727 150 : if (CSLFetchNameValue(papszOptions, "XML_DEFINITION") != nullptr)
2728 1 : defn_str = CPLStrdup(CSLFetchNameValue(papszOptions, "XML_DEFINITION"));
2729 : else
2730 149 : defn_str = CPLSerializeXMLTree(xml_xml);
2731 150 : CPLDestroyXMLNode(xml_xml);
2732 :
2733 : /* TODO, tie this to debugging levels */
2734 150 : CPLDebug("FGDB", "%s", defn_str);
2735 : // std::cout << defn_str << std::endl;
2736 :
2737 : /* Create the table. */
2738 150 : Table *table = new Table;
2739 150 : Geodatabase *gdb = pParentDataSource->GetGDB();
2740 150 : fgdbError hr = gdb->CreateTable(defn_str, wparent_path, *table);
2741 :
2742 : /* Free the XML */
2743 150 : CPLFree(defn_str);
2744 :
2745 : /* Check table create status */
2746 150 : if (FAILED(hr))
2747 : {
2748 0 : delete table;
2749 0 : return GDBErr(hr, "Failed at creating table for " + table_path);
2750 : }
2751 :
2752 150 : m_papszOptions = CSLDuplicate(papszOptions);
2753 :
2754 : // Default to YES here assuming ogr2ogr scenario
2755 150 : m_bBulkLoadAllowed =
2756 150 : CPLTestBool(CPLGetConfigOption("FGDB_BULK_LOAD", "YES"));
2757 :
2758 : /* Store the new FGDB Table pointer and set up the OGRFeatureDefn */
2759 : bool bRet =
2760 150 : FGdbLayer::Initialize(pParentDataSource, table, wtable_path, L"Table");
2761 150 : if (bRet)
2762 : {
2763 150 : if (m_pFeatureDefn->GetGeomFieldCount() != 0)
2764 137 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetCoordinatePrecision(
2765 : oCoordPrec);
2766 :
2767 150 : if (bCreateShapeArea)
2768 : {
2769 2 : OGRFieldDefn oField(pszAreaFieldName, OFTReal);
2770 2 : oField.SetDefault("FILEGEODATABASE_SHAPE_AREA");
2771 2 : bRet &= CreateField(&oField, false) == OGRERR_NONE;
2772 : }
2773 150 : if (bCreateShapeLength)
2774 : {
2775 4 : OGRFieldDefn oField(pszLengthFieldName, OFTReal);
2776 4 : oField.SetDefault("FILEGEODATABASE_SHAPE_LENGTH");
2777 4 : bRet &= CreateField(&oField, false) == OGRERR_NONE;
2778 : }
2779 : }
2780 150 : return bRet;
2781 : }
2782 :
2783 : /*************************************************************************/
2784 : /* Initialize() */
2785 : /* Has ownership of the table as soon as it is called. */
2786 : /************************************************************************/
2787 :
2788 408 : bool FGdbLayer::Initialize(FGdbDataSource *pParentDataSource, Table *pTable,
2789 : const std::wstring &wstrTablePath,
2790 : const std::wstring &wstrType)
2791 : {
2792 : long hr;
2793 :
2794 408 : m_pDS = pParentDataSource; // we never assume ownership of the parent - so
2795 : // our destructor should not delete
2796 :
2797 408 : m_pTable = pTable;
2798 :
2799 408 : m_wstrTablePath = wstrTablePath;
2800 408 : m_wstrType = wstrType;
2801 :
2802 816 : wstring wstrQueryName;
2803 408 : if (FAILED(hr = pParentDataSource->GetGDB()->GetQueryName(wstrTablePath,
2804 : wstrQueryName)))
2805 0 : return GDBErr(hr, "Failed at getting underlying table name for " +
2806 0 : WStringToString(wstrTablePath));
2807 :
2808 408 : m_strName = WStringToString(wstrQueryName);
2809 :
2810 408 : m_pFeatureDefn = new OGRFeatureDefn(
2811 408 : m_strName.c_str()); // TODO: Should I "new" an OGR smart pointer -
2812 : // sample says so, but it doesn't seem right
2813 408 : SetDescription(m_pFeatureDefn->GetName());
2814 : // as long as we use the same compiler & settings in both the ogr build and
2815 : // this driver, we should be OK
2816 408 : m_pFeatureDefn->Reference();
2817 :
2818 816 : string tableDef;
2819 408 : if (FAILED(hr = m_pTable->GetDefinition(tableDef)))
2820 0 : return GDBErr(hr, "Failed at getting table definition for " +
2821 0 : WStringToString(wstrTablePath));
2822 :
2823 : // CPLDebug("FGDB", "tableDef = %s", tableDef.c_str());
2824 :
2825 408 : bool abort = false;
2826 :
2827 : // extract schema information from table
2828 408 : CPLXMLNode *psRoot = CPLParseXMLString(tableDef.c_str());
2829 :
2830 408 : if (psRoot == nullptr)
2831 : {
2832 0 : CPLError(
2833 : CE_Failure, CPLE_AppDefined, "%s",
2834 0 : ("Failed parsing GDB Table Schema XML for " + m_strName).c_str());
2835 0 : return false;
2836 : }
2837 :
2838 408 : CPLXMLNode *pDataElementNode =
2839 : psRoot->psNext; // Move to next field which should be DataElement
2840 :
2841 408 : if (pDataElementNode != nullptr && pDataElementNode->psChild != nullptr &&
2842 408 : pDataElementNode->eType == CXT_Element &&
2843 408 : EQUAL(pDataElementNode->pszValue, "esri:DataElement"))
2844 : {
2845 : CPLXMLNode *psNode;
2846 :
2847 408 : m_bTimeInUTC = CPLTestBool(
2848 : CPLGetXMLValue(pDataElementNode, "IsTimeInUTC", "false"));
2849 :
2850 816 : std::string osAreaFieldName;
2851 816 : std::string osLengthFieldName;
2852 11586 : for (psNode = pDataElementNode->psChild; psNode != nullptr;
2853 11178 : psNode = psNode->psNext)
2854 : {
2855 11178 : if (psNode->eType == CXT_Element && psNode->psChild != nullptr)
2856 : {
2857 8187 : if (EQUAL(psNode->pszValue, "OIDFieldName"))
2858 : {
2859 408 : m_strOIDFieldName = CPLGetXMLValue(psNode, nullptr, "");
2860 : }
2861 7779 : else if (EQUAL(psNode->pszValue, "ShapeFieldName"))
2862 : {
2863 370 : m_strShapeFieldName = CPLGetXMLValue(psNode, nullptr, "");
2864 : }
2865 7409 : else if (EQUAL(psNode->pszValue, "AreaFieldName"))
2866 : {
2867 14 : osAreaFieldName = CPLGetXMLValue(psNode, nullptr, "");
2868 : }
2869 7395 : else if (EQUAL(psNode->pszValue, "LengthFieldName"))
2870 : {
2871 24 : osLengthFieldName = CPLGetXMLValue(psNode, nullptr, "");
2872 : }
2873 7371 : else if (EQUAL(psNode->pszValue, "Fields"))
2874 : {
2875 408 : if (!GDBToOGRFields(psNode))
2876 : {
2877 0 : abort = true;
2878 0 : break;
2879 : }
2880 : }
2881 : }
2882 : }
2883 :
2884 408 : if (!osAreaFieldName.empty())
2885 : {
2886 : const int nIdx =
2887 14 : m_pFeatureDefn->GetFieldIndex(osAreaFieldName.c_str());
2888 14 : if (nIdx >= 0)
2889 : {
2890 12 : m_pFeatureDefn->GetFieldDefn(nIdx)->SetDefault(
2891 : "FILEGEODATABASE_SHAPE_AREA");
2892 : }
2893 : }
2894 :
2895 408 : if (!osLengthFieldName.empty())
2896 : {
2897 : const int nIdx =
2898 24 : m_pFeatureDefn->GetFieldIndex(osLengthFieldName.c_str());
2899 24 : if (nIdx >= 0)
2900 : {
2901 20 : m_pFeatureDefn->GetFieldDefn(nIdx)->SetDefault(
2902 : "FILEGEODATABASE_SHAPE_LENGTH");
2903 : }
2904 : }
2905 :
2906 408 : if (m_strShapeFieldName.empty())
2907 446 : m_pFeatureDefn->SetGeomType(wkbNone);
2908 : }
2909 : else
2910 : {
2911 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s",
2912 0 : ("Failed parsing GDB Table Schema XML (DataElement) for " +
2913 0 : m_strName)
2914 : .c_str());
2915 0 : return false;
2916 : }
2917 408 : CPLDestroyXMLNode(psRoot);
2918 :
2919 408 : if (m_pFeatureDefn->GetGeomFieldCount() != 0)
2920 : {
2921 370 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetName(
2922 : m_strShapeFieldName.c_str());
2923 370 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_pSRS);
2924 : }
2925 :
2926 408 : if (abort)
2927 0 : return false;
2928 :
2929 408 : return true; // AOToOGRFields(ipFields, m_pFeatureDefn,
2930 : // m_vOGRFieldToESRIField);
2931 : }
2932 :
2933 : /************************************************************************/
2934 : /* ParseGeometryDef() */
2935 : /************************************************************************/
2936 :
2937 370 : bool FGdbLayer::ParseGeometryDef(const CPLXMLNode *psRoot)
2938 : {
2939 740 : string geometryType;
2940 370 : bool hasZ = false, hasM = false;
2941 740 : string wkt, wkid, latestwkid;
2942 :
2943 740 : OGRGeomCoordinatePrecision oCoordPrec;
2944 370 : for (const CPLXMLNode *psGeometryDefItem = psRoot->psChild;
2945 2960 : psGeometryDefItem; psGeometryDefItem = psGeometryDefItem->psNext)
2946 : {
2947 : // loop through all "GeometryDef" elements
2948 : //
2949 :
2950 2590 : if (psGeometryDefItem->eType == CXT_Element &&
2951 2220 : psGeometryDefItem->psChild != nullptr)
2952 : {
2953 2220 : if (EQUAL(psGeometryDefItem->pszValue, "GeometryType"))
2954 : {
2955 370 : geometryType = CPLGetXMLValue(psGeometryDefItem, nullptr, "");
2956 : }
2957 1850 : else if (EQUAL(psGeometryDefItem->pszValue, "SpatialReference"))
2958 : {
2959 370 : ParseSpatialReference(
2960 : psGeometryDefItem, &wkt, &wkid,
2961 : &latestwkid); // we don't check for success because it
2962 : // may not be there
2963 370 : oCoordPrec = GDBGridSettingsToOGR(psGeometryDefItem);
2964 : }
2965 1480 : else if (EQUAL(psGeometryDefItem->pszValue, "HasM"))
2966 : {
2967 370 : if (!strcmp(CPLGetXMLValue(psGeometryDefItem, nullptr, ""),
2968 : "true"))
2969 7 : hasM = true;
2970 : }
2971 1110 : else if (EQUAL(psGeometryDefItem->pszValue, "HasZ"))
2972 : {
2973 370 : if (!strcmp(CPLGetXMLValue(psGeometryDefItem, nullptr, ""),
2974 : "true"))
2975 145 : hasZ = true;
2976 : }
2977 : }
2978 : }
2979 :
2980 : OGRwkbGeometryType ogrGeoType;
2981 370 : if (!GDBToOGRGeometry(geometryType, hasZ, hasM, &ogrGeoType))
2982 0 : return false;
2983 :
2984 370 : m_pFeatureDefn->SetGeomType(ogrGeoType);
2985 :
2986 370 : if (m_pFeatureDefn->GetGeomFieldCount() != 0)
2987 370 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetCoordinatePrecision(oCoordPrec);
2988 :
2989 660 : if (wkbFlatten(ogrGeoType) == wkbMultiLineString ||
2990 290 : wkbFlatten(ogrGeoType) == wkbMultiPoint)
2991 115 : m_forceMulti = true;
2992 :
2993 370 : if (latestwkid.length() > 0 || wkid.length() > 0)
2994 : {
2995 347 : int bSuccess = FALSE;
2996 347 : m_pSRS = new OGRSpatialReference();
2997 347 : m_pSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2998 347 : CPLPushErrorHandler(CPLQuietErrorHandler);
2999 347 : if (latestwkid.length() > 0)
3000 : {
3001 0 : if (m_pSRS->importFromEPSG(atoi(latestwkid.c_str())) == OGRERR_NONE)
3002 : {
3003 0 : bSuccess = TRUE;
3004 : }
3005 : else
3006 : {
3007 0 : CPLDebug("FGDB", "Cannot import SRID %s", latestwkid.c_str());
3008 : }
3009 : }
3010 347 : if (!bSuccess && wkid.length() > 0)
3011 : {
3012 347 : if (m_pSRS->importFromEPSG(atoi(wkid.c_str())) == OGRERR_NONE)
3013 : {
3014 347 : bSuccess = TRUE;
3015 : }
3016 : else
3017 : {
3018 0 : CPLDebug("FGDB", "Cannot import SRID %s", wkid.c_str());
3019 : }
3020 : }
3021 347 : CPLPopErrorHandler();
3022 347 : CPLErrorReset();
3023 347 : if (!bSuccess)
3024 : {
3025 0 : delete m_pSRS;
3026 0 : m_pSRS = nullptr;
3027 : }
3028 : else
3029 347 : return true;
3030 : }
3031 :
3032 23 : if (wkt.length() > 0)
3033 : {
3034 7 : if (!GDBToOGRSpatialReference(wkt, &m_pSRS))
3035 : {
3036 : // report error, but be passive about it
3037 0 : CPLError(CE_Warning, CPLE_AppDefined,
3038 : "Failed Mapping ESRI Spatial Reference");
3039 : }
3040 : }
3041 :
3042 23 : return true;
3043 : }
3044 :
3045 : /************************************************************************/
3046 : /* ParseSpatialReference() */
3047 : /************************************************************************/
3048 :
3049 370 : bool FGdbLayer::ParseSpatialReference(const CPLXMLNode *psSpatialRefNode,
3050 : string *pOutWkt, string *pOutWKID,
3051 : string *pOutLatestWKID)
3052 : {
3053 370 : *pOutWkt = "";
3054 370 : *pOutWKID = "";
3055 370 : *pOutLatestWKID = "";
3056 :
3057 : /* Loop through all the SRS elements we want to store */
3058 370 : for (const CPLXMLNode *psSRItemNode = psSpatialRefNode->psChild;
3059 5534 : psSRItemNode; psSRItemNode = psSRItemNode->psNext)
3060 : {
3061 : /* The WKID maps (mostly) to an EPSG code */
3062 5164 : if (psSRItemNode->eType == CXT_Element &&
3063 4794 : psSRItemNode->psChild != nullptr &&
3064 4794 : EQUAL(psSRItemNode->pszValue, "WKID"))
3065 : {
3066 370 : *pOutWKID = CPLGetXMLValue(psSRItemNode, nullptr, "");
3067 :
3068 : // Needed with FileGDB v1.4 with layers with empty SRS
3069 370 : if (*pOutWKID == "0")
3070 23 : *pOutWKID = "";
3071 : }
3072 : /* The concept of LatestWKID is explained in
3073 : * http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r3000000n1000000
3074 : */
3075 4794 : else if (psSRItemNode->eType == CXT_Element &&
3076 4424 : psSRItemNode->psChild != nullptr &&
3077 4424 : EQUAL(psSRItemNode->pszValue, "LatestWKID"))
3078 : {
3079 0 : *pOutLatestWKID = CPLGetXMLValue(psSRItemNode, nullptr, "");
3080 : }
3081 : /* The WKT well-known text can be converted by OGR */
3082 4794 : else if (psSRItemNode->eType == CXT_Element &&
3083 4424 : psSRItemNode->psChild != nullptr &&
3084 4424 : EQUAL(psSRItemNode->pszValue, "WKT"))
3085 : {
3086 354 : *pOutWkt = CPLGetXMLValue(psSRItemNode, nullptr, "");
3087 : }
3088 : }
3089 370 : return *pOutWkt != "" || *pOutWKID != "";
3090 : }
3091 :
3092 : /************************************************************************/
3093 : /* GDBToOGRFields() */
3094 : /************************************************************************/
3095 :
3096 408 : bool FGdbLayer::GDBToOGRFields(CPLXMLNode *psRoot)
3097 : {
3098 408 : m_vOGRFieldToESRIField.clear();
3099 :
3100 408 : if (psRoot->psChild == nullptr || psRoot->psChild->psNext == nullptr)
3101 : {
3102 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unrecognized GDB XML Schema");
3103 :
3104 0 : return false;
3105 : }
3106 :
3107 408 : psRoot = psRoot->psChild->psNext; // change root to "FieldArray"
3108 :
3109 : // CPLAssert(ogrToESRIFieldMapping.size() ==
3110 : // pOGRFeatureDef->GetFieldCount());
3111 :
3112 : CPLXMLNode *psFieldNode;
3113 :
3114 4229 : for (psFieldNode = psRoot->psChild; psFieldNode != nullptr;
3115 3821 : psFieldNode = psFieldNode->psNext)
3116 : {
3117 : // loop through all "Field" elements
3118 : //
3119 :
3120 3821 : if (psFieldNode->eType == CXT_Element &&
3121 3413 : psFieldNode->psChild != nullptr &&
3122 3413 : EQUAL(psFieldNode->pszValue, "Field"))
3123 : {
3124 :
3125 : CPLXMLNode *psFieldItemNode;
3126 3413 : std::string fieldName;
3127 3413 : std::string fieldAlias;
3128 3413 : std::string fieldType;
3129 3413 : int nLength = 0;
3130 3413 : int bNullable = TRUE;
3131 3413 : std::string osDefault;
3132 3413 : std::string osDomainName;
3133 :
3134 : // loop through all items in Field element
3135 : //
3136 :
3137 3413 : for (psFieldItemNode = psFieldNode->psChild;
3138 35771 : psFieldItemNode != nullptr;
3139 32358 : psFieldItemNode = psFieldItemNode->psNext)
3140 : {
3141 32358 : if (psFieldItemNode->eType == CXT_Element)
3142 : {
3143 : const char *pszValue =
3144 28945 : CPLGetXMLValue(psFieldItemNode, nullptr, "");
3145 28945 : if (EQUAL(psFieldItemNode->pszValue, "Name"))
3146 : {
3147 3413 : fieldName = pszValue;
3148 : }
3149 25532 : else if (EQUAL(psFieldItemNode->pszValue, "AliasName"))
3150 : {
3151 3413 : fieldAlias = pszValue;
3152 : }
3153 22119 : else if (EQUAL(psFieldItemNode->pszValue, "Type"))
3154 : {
3155 3413 : fieldType = pszValue;
3156 : }
3157 18706 : else if (EQUAL(psFieldItemNode->pszValue, "GeometryDef"))
3158 : {
3159 370 : if (!ParseGeometryDef(psFieldItemNode))
3160 0 : return false; // if we failed parsing the
3161 : // GeometryDef, we are done!
3162 : }
3163 18336 : else if (EQUAL(psFieldItemNode->pszValue, "Length"))
3164 : {
3165 3413 : nLength = atoi(pszValue);
3166 : }
3167 14923 : else if (EQUAL(psFieldItemNode->pszValue, "IsNullable"))
3168 : {
3169 3413 : bNullable = EQUAL(pszValue, "true");
3170 : }
3171 11510 : else if (EQUAL(psFieldItemNode->pszValue, "DefaultValue"))
3172 : {
3173 7 : osDefault = pszValue;
3174 : }
3175 : // NOTE: when using the GetDefinition() API, the domain name
3176 : // is set in <Domain><DomainName>, whereas the raw XML is
3177 : // just <DomainName>
3178 11503 : else if (EQUAL(psFieldItemNode->pszValue, "Domain"))
3179 : {
3180 : osDomainName =
3181 24 : CPLGetXMLValue(psFieldItemNode, "DomainName", "");
3182 : }
3183 : }
3184 : }
3185 :
3186 : ///////////////////////////////////////////////////////////////////
3187 : // At this point we have parsed everything about the current field
3188 :
3189 3413 : if (fieldType == "esriFieldTypeGeometry")
3190 : {
3191 370 : m_strShapeFieldName = fieldName;
3192 370 : m_pFeatureDefn->GetGeomFieldDefn(0)->SetNullable(bNullable);
3193 :
3194 370 : continue; // finish here for special field - don't add as OGR
3195 : // fielddef
3196 : }
3197 3043 : else if (fieldType == "esriFieldTypeOID")
3198 : {
3199 : // m_strOIDFieldName = fieldName; // already set by this point
3200 :
3201 408 : continue; // finish here for special field - don't add as OGR
3202 : // fielddef
3203 : }
3204 :
3205 : OGRFieldType ogrType;
3206 : OGRFieldSubType eSubType;
3207 : // CPLDebug("FGDB", "name = %s, type = %s", fieldName.c_str(),
3208 : // fieldType.c_str() );
3209 2635 : if (!GDBToOGRFieldType(fieldType, &ogrType, &eSubType))
3210 : {
3211 : // field cannot be mapped, skipping further processing
3212 0 : CPLError(CE_Warning, CPLE_AppDefined,
3213 : "Skipping field: [%s] type: [%s] ", fieldName.c_str(),
3214 : fieldType.c_str());
3215 0 : continue;
3216 : }
3217 :
3218 : // TODO: Optimization - modify m_wstrSubFields so it only fetches
3219 : // fields that are mapped
3220 :
3221 5270 : OGRFieldDefn fieldTemplate(fieldName.c_str(), ogrType);
3222 2635 : if (fieldAlias != fieldName)
3223 : {
3224 : // The SDK generates an alias even with it is not explicitly
3225 : // written
3226 4 : fieldTemplate.SetAlternativeName(fieldAlias.c_str());
3227 : }
3228 2635 : fieldTemplate.SetSubType(eSubType);
3229 : /* On creation (GDBFieldTypeToLengthInBytes) if string width is 0,
3230 : * we pick up */
3231 : /* 65536 by default to mean unlimited string length, but we don't
3232 : * want */
3233 : /* to advertise such a big number */
3234 2635 : if (ogrType == OFTString && nLength < 65536)
3235 426 : fieldTemplate.SetWidth(nLength);
3236 2635 : fieldTemplate.SetNullable(bNullable);
3237 2635 : if (!osDefault.empty())
3238 : {
3239 7 : if (ogrType == OFTString)
3240 : {
3241 : char *pszTmp =
3242 1 : CPLEscapeString(osDefault.c_str(), -1, CPLES_SQL);
3243 1 : osDefault = "'";
3244 1 : osDefault += pszTmp;
3245 1 : CPLFree(pszTmp);
3246 1 : osDefault += "'";
3247 1 : fieldTemplate.SetDefault(osDefault.c_str());
3248 : }
3249 6 : else if (ogrType == OFTInteger || ogrType == OFTReal)
3250 : {
3251 : #ifdef unreliable
3252 : /* Disabling this as GDBs and the FileGDB SDK aren't
3253 : * reliable for numeric values */
3254 : /* It often occurs that the XML definition in
3255 : * a00000004.gdbtable doesn't */
3256 : /* match the default values (in binary) found in the field
3257 : * definition */
3258 : /* section of the .gdbtable of the layers themselves */
3259 : /* The Table::GetDefinition() API of FileGDB doesn't seem to
3260 : * use the */
3261 : /* XML definition, but rather the values found in the field
3262 : * definition */
3263 : /* section of the .gdbtable of the layers themselves */
3264 : /* It seems that the XML definition in a00000004.gdbtable is
3265 : * authoritative */
3266 : /* in ArcGIS, so we're screwed... */
3267 :
3268 : fieldTemplate.SetDefault(osDefault.c_str());
3269 : #endif
3270 : }
3271 0 : else if (ogrType == OFTDateTime)
3272 : {
3273 : int nYear, nMonth, nDay, nHour, nMinute;
3274 : float fSecond;
3275 0 : if (sscanf(osDefault.c_str(), "%d-%d-%dT%d:%d:%fZ", &nYear,
3276 : &nMonth, &nDay, &nHour, &nMinute,
3277 0 : &fSecond) == 6 ||
3278 0 : sscanf(osDefault.c_str(), "'%d-%d-%d %d:%d:%fZ'",
3279 : &nYear, &nMonth, &nDay, &nHour, &nMinute,
3280 : &fSecond) == 6)
3281 : {
3282 0 : fieldTemplate.SetDefault(CPLSPrintf(
3283 : "'%04d/%02d/%02d %02d:%02d:%02d'", nYear, nMonth,
3284 0 : nDay, nHour, nMinute, (int)(fSecond + 0.5)));
3285 : }
3286 : }
3287 : }
3288 2635 : if (!osDomainName.empty())
3289 : {
3290 24 : fieldTemplate.SetDomainName(osDomainName);
3291 : }
3292 :
3293 2635 : m_pFeatureDefn->AddFieldDefn(&fieldTemplate);
3294 :
3295 2635 : m_vOGRFieldToESRIField.push_back(StringToWString(fieldName));
3296 2635 : m_vOGRFieldToESRIFieldType.push_back(fieldType);
3297 2635 : if (ogrType == OFTBinary)
3298 379 : m_apoByteArrays.push_back(new ByteArray());
3299 : }
3300 : }
3301 :
3302 : /* Using OpenFileGDB to get reliable default values for integer/real fields
3303 : */
3304 : /* and alias */
3305 408 : if (m_pDS->UseOpenFileGDB())
3306 : {
3307 406 : const char *const apszDrivers[] = {"OpenFileGDB", nullptr};
3308 406 : GDALDataset *poDS = GDALDataset::Open(
3309 406 : m_pDS->GetFSName(), GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr);
3310 406 : if (poDS != nullptr)
3311 : {
3312 37 : OGRLayer *poLyr = poDS->GetLayerByName(GetName());
3313 37 : if (poLyr)
3314 : {
3315 37 : const auto poOFGBLayerDefn = poLyr->GetLayerDefn();
3316 37 : const int nOFGDBFieldCount = poOFGBLayerDefn->GetFieldCount();
3317 99 : for (int i = 0; i < nOFGDBFieldCount; i++)
3318 : {
3319 : const OGRFieldDefn *poSrcDefn =
3320 62 : poOFGBLayerDefn->GetFieldDefn(i);
3321 104 : if ((poSrcDefn->GetType() == OFTInteger ||
3322 124 : poSrcDefn->GetType() == OFTReal) &&
3323 37 : poSrcDefn->GetDefault() != nullptr)
3324 : {
3325 17 : int nIdxDst = m_pFeatureDefn->GetFieldIndex(
3326 17 : poSrcDefn->GetNameRef());
3327 17 : if (nIdxDst >= 0)
3328 17 : m_pFeatureDefn->GetFieldDefn(nIdxDst)->SetDefault(
3329 : poSrcDefn->GetDefault());
3330 : }
3331 :
3332 : // XML parsing by the SDK fails when there are special
3333 : // characters, like &, so fallback to using OpenFileGDB.
3334 : const char *pszAlternativeName =
3335 62 : poSrcDefn->GetAlternativeNameRef();
3336 124 : if (pszAlternativeName != nullptr &&
3337 64 : pszAlternativeName[0] != '\0' &&
3338 2 : strcmp(pszAlternativeName, poSrcDefn->GetNameRef()) !=
3339 : 0)
3340 : {
3341 2 : int nIdxDst = m_pFeatureDefn->GetFieldIndex(
3342 2 : poSrcDefn->GetNameRef());
3343 2 : if (nIdxDst >= 0)
3344 2 : m_pFeatureDefn->GetFieldDefn(nIdxDst)
3345 2 : ->SetAlternativeName(pszAlternativeName);
3346 : }
3347 : }
3348 : }
3349 37 : GDALClose(poDS);
3350 : }
3351 : }
3352 :
3353 408 : return true;
3354 : }
3355 :
3356 : /************************************************************************/
3357 : /* ResetReading() */
3358 : /************************************************************************/
3359 :
3360 1305 : void FGdbLayer::ResetReading()
3361 : {
3362 : long hr;
3363 :
3364 1305 : if (m_pTable == nullptr)
3365 0 : return;
3366 :
3367 1305 : EndBulkLoad();
3368 :
3369 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
3370 1305 : const std::wstring wstrSubFieldBackup(m_wstrSubfields);
3371 1305 : if (!m_apoByteArrays.empty())
3372 : {
3373 1208 : m_bWorkaroundCrashOnCDFWithBinaryField = CPLTestBool(CPLGetConfigOption(
3374 : "OGR_FGDB_WORKAROUND_CRASH_ON_BINARY_FIELD", "YES"));
3375 1208 : if (m_bWorkaroundCrashOnCDFWithBinaryField)
3376 : {
3377 1208 : m_wstrSubfields = StringToWString(m_strOIDFieldName);
3378 1554 : if (!m_strShapeFieldName.empty() && m_poFilterGeom &&
3379 346 : !m_poFilterGeom->IsEmpty())
3380 : {
3381 346 : m_wstrSubfields += StringToWString(", " + m_strShapeFieldName);
3382 : }
3383 : }
3384 : }
3385 : #endif
3386 :
3387 1305 : if (m_poFilterGeom && !m_poFilterGeom->IsEmpty())
3388 : {
3389 : // Search spatial
3390 : // As of beta1, FileGDB only supports bbox searched, if we have GEOS
3391 : // installed, we can do the rest ourselves.
3392 :
3393 370 : OGREnvelope ogrEnv;
3394 :
3395 370 : m_poFilterGeom->getEnvelope(&ogrEnv);
3396 :
3397 : // spatial query
3398 : FileGDBAPI::Envelope env(ogrEnv.MinX, ogrEnv.MaxX, ogrEnv.MinY,
3399 740 : ogrEnv.MaxY);
3400 :
3401 370 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause,
3402 : env, true, *m_pEnumRows)))
3403 0 : GDBErr(hr, "Failed Searching");
3404 : }
3405 : else
3406 : {
3407 : // Search non-spatial
3408 935 : if (FAILED(hr = m_pTable->Search(m_wstrSubfields, m_wstrWhereClause,
3409 : true, *m_pEnumRows)))
3410 0 : GDBErr(hr, "Failed Searching");
3411 : }
3412 :
3413 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
3414 1305 : if (!m_apoByteArrays.empty() && m_bWorkaroundCrashOnCDFWithBinaryField)
3415 1208 : m_wstrSubfields = wstrSubFieldBackup;
3416 : #endif
3417 :
3418 1305 : m_bFilterDirty = false;
3419 : }
3420 :
3421 : /************************************************************************/
3422 : /* SetSpatialFilter() */
3423 : /************************************************************************/
3424 :
3425 405 : void FGdbLayer::SetSpatialFilter(OGRGeometry *pOGRGeom)
3426 : {
3427 405 : m_bFilterDirty = true;
3428 405 : OGRLayer::SetSpatialFilter(pOGRGeom);
3429 405 : }
3430 :
3431 : /************************************************************************/
3432 : /* ResyncIDs() */
3433 : /************************************************************************/
3434 :
3435 1 : void FGdbLayer::ResyncIDs()
3436 : {
3437 1 : if (m_oMapOGRFIDToFGDBFID.empty())
3438 1 : return;
3439 0 : if (m_pDS->CloseInternal())
3440 0 : m_pDS->ReOpen();
3441 : }
3442 :
3443 : /************************************************************************/
3444 : /* SetAttributeFilter() */
3445 : /************************************************************************/
3446 :
3447 545 : OGRErr FGdbLayer::SetAttributeFilter(const char *pszQuery)
3448 : {
3449 827 : if (pszQuery != nullptr &&
3450 827 : CPLString(pszQuery).ifind(GetFIDColumn()) != std::string::npos)
3451 1 : ResyncIDs();
3452 :
3453 545 : m_wstrWhereClause = StringToWString((pszQuery != nullptr) ? pszQuery : "");
3454 :
3455 545 : m_bFilterDirty = true;
3456 :
3457 545 : return OGRERR_NONE;
3458 : }
3459 :
3460 : /************************************************************************/
3461 : /* OGRFeatureFromGdbRow() */
3462 : /************************************************************************/
3463 :
3464 2011 : bool FGdbBaseLayer::OGRFeatureFromGdbRow(Row *pRow, OGRFeature **ppFeature)
3465 : {
3466 : long hr;
3467 :
3468 2011 : OGRFeature *pOutFeature = new OGRFeature(m_pFeatureDefn);
3469 :
3470 : /////////////////////////////////////////////////////////
3471 : // Translate OID
3472 : //
3473 :
3474 2011 : int32 oid = -1;
3475 2011 : if (FAILED(hr = pRow->GetOID(oid)))
3476 : {
3477 : // this should never happen unless not selecting the OBJECTID
3478 : }
3479 : else
3480 : {
3481 2011 : pOutFeature->SetFID(oid);
3482 : }
3483 :
3484 : /////////////////////////////////////////////////////////
3485 : // Translate Geometry
3486 : //
3487 :
3488 4022 : ShapeBuffer gdbGeometry;
3489 : // Row::GetGeometry() will fail with -2147467259 for NULL geometries
3490 : // Row::GetGeometry() will fail with -2147219885 for tables without a
3491 : // geometry field
3492 3942 : if (!m_pFeatureDefn->IsGeometryIgnored() &&
3493 1931 : !FAILED(hr = pRow->GetGeometry(gdbGeometry)))
3494 : {
3495 1678 : OGRGeometry *pOGRGeo = nullptr;
3496 :
3497 1678 : if ((!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry, m_pSRS,
3498 : &pOGRGeo)))
3499 : {
3500 0 : delete pOutFeature;
3501 0 : return GDBErr(hr, "Failed to translate FileGDB Geometry to OGR "
3502 0 : "Geometry for row " +
3503 0 : string(CPLSPrintf("%d", (int)oid)));
3504 : }
3505 :
3506 1678 : pOutFeature->SetGeometryDirectly(pOGRGeo);
3507 : }
3508 :
3509 : //////////////////////////////////////////////////////////
3510 : // Map fields
3511 : //
3512 :
3513 2011 : int mappedFieldCount = static_cast<int>(m_vOGRFieldToESRIField.size());
3514 :
3515 2011 : bool foundBadColumn = false;
3516 :
3517 25517 : for (int i = 0; i < mappedFieldCount; ++i)
3518 : {
3519 23506 : OGRFieldDefn *poFieldDefn = m_pFeatureDefn->GetFieldDefn(i);
3520 : // The IsNull() and GetXXX() API are very slow when there are a
3521 : // big number of fields, for example with Tiger database.
3522 23506 : if (poFieldDefn->IsIgnored())
3523 107 : continue;
3524 :
3525 23411 : const wstring &wstrFieldName = m_vOGRFieldToESRIField[i];
3526 23411 : const std::string &strFieldType = m_vOGRFieldToESRIFieldType[i];
3527 :
3528 23411 : bool isNull = false;
3529 :
3530 23411 : if (FAILED(hr = pRow->IsNull(wstrFieldName, isNull)))
3531 : {
3532 0 : GDBErr(hr, "Failed to determine NULL status from column " +
3533 0 : WStringToString(wstrFieldName));
3534 0 : foundBadColumn = true;
3535 0 : continue;
3536 : }
3537 :
3538 23411 : if (isNull)
3539 : {
3540 12 : pOutFeature->SetFieldNull(i);
3541 12 : continue;
3542 : }
3543 :
3544 : //
3545 : // NOTE: This switch statement needs to be kept in sync with
3546 : // GDBToOGRFieldType utility function
3547 : // since we are only checking for types we mapped in that utility
3548 : // function
3549 :
3550 23399 : switch (poFieldDefn->GetType())
3551 : {
3552 :
3553 6927 : case OFTInteger:
3554 : {
3555 : int32 val;
3556 :
3557 6927 : if (FAILED(hr = pRow->GetInteger(wstrFieldName, val)))
3558 : {
3559 : int16 shortval;
3560 3501 : if (FAILED(hr = pRow->GetShort(wstrFieldName, shortval)))
3561 : {
3562 0 : GDBErr(hr,
3563 0 : "Failed to determine integer value for column " +
3564 0 : WStringToString(wstrFieldName));
3565 0 : foundBadColumn = true;
3566 0 : continue;
3567 : }
3568 3501 : val = shortval;
3569 : }
3570 :
3571 6927 : pOutFeature->SetField(i, (int)val);
3572 : }
3573 6927 : break;
3574 :
3575 5718 : case OFTReal:
3576 : {
3577 5718 : if (strFieldType == "esriFieldTypeSingle")
3578 : {
3579 : float val;
3580 :
3581 3501 : if (FAILED(hr = pRow->GetFloat(wstrFieldName, val)))
3582 : {
3583 0 : GDBErr(hr,
3584 0 : "Failed to determine float value for column " +
3585 0 : WStringToString(wstrFieldName));
3586 0 : foundBadColumn = true;
3587 0 : continue;
3588 : }
3589 :
3590 3501 : pOutFeature->SetField(i, val);
3591 : }
3592 : else
3593 : {
3594 : double val;
3595 :
3596 2217 : if (FAILED(hr = pRow->GetDouble(wstrFieldName, val)))
3597 : {
3598 0 : GDBErr(hr,
3599 0 : "Failed to determine real value for column " +
3600 0 : WStringToString(wstrFieldName));
3601 0 : foundBadColumn = true;
3602 0 : continue;
3603 : }
3604 :
3605 2217 : pOutFeature->SetField(i, val);
3606 : }
3607 : }
3608 5718 : break;
3609 5499 : case OFTString:
3610 : {
3611 5499 : wstring val;
3612 5499 : std::string strValue;
3613 :
3614 5499 : if (strFieldType == "esriFieldTypeGlobalID")
3615 : {
3616 4 : Guid guid;
3617 8 : if (FAILED(hr = pRow->GetGlobalID(guid)) ||
3618 4 : FAILED(hr = guid.ToString(val)))
3619 : {
3620 0 : GDBErr(hr,
3621 0 : "Failed to determine string value for column " +
3622 0 : WStringToString(wstrFieldName));
3623 0 : foundBadColumn = true;
3624 0 : continue;
3625 : }
3626 4 : strValue = WStringToString(val);
3627 : }
3628 5495 : else if (strFieldType == "esriFieldTypeGUID")
3629 : {
3630 1751 : Guid guid;
3631 3502 : if (FAILED(hr = pRow->GetGUID(wstrFieldName, guid)) ||
3632 1751 : FAILED(hr = guid.ToString(val)))
3633 : {
3634 0 : GDBErr(hr,
3635 0 : "Failed to determine string value for column " +
3636 0 : WStringToString(wstrFieldName));
3637 0 : foundBadColumn = true;
3638 0 : continue;
3639 : }
3640 1751 : strValue = WStringToString(val);
3641 : }
3642 3744 : else if (strFieldType == "esriFieldTypeXML")
3643 : {
3644 1750 : if (FAILED(hr = pRow->GetXML(wstrFieldName, strValue)))
3645 : {
3646 0 : GDBErr(hr, "Failed to determine XML value for column " +
3647 0 : WStringToString(wstrFieldName));
3648 0 : foundBadColumn = true;
3649 0 : continue;
3650 : }
3651 : }
3652 : else
3653 : {
3654 1994 : if (FAILED(hr = pRow->GetString(wstrFieldName, val)))
3655 : {
3656 0 : GDBErr(hr,
3657 0 : "Failed to determine string value for column " +
3658 0 : WStringToString(wstrFieldName));
3659 0 : foundBadColumn = true;
3660 0 : continue;
3661 : }
3662 1994 : strValue = WStringToString(val);
3663 : }
3664 :
3665 5499 : pOutFeature->SetField(i, strValue.c_str());
3666 : }
3667 5499 : break;
3668 :
3669 3500 : case OFTBinary:
3670 : {
3671 3500 : ByteArray binaryBuf;
3672 :
3673 3500 : if (FAILED(hr = pRow->GetBinary(wstrFieldName, binaryBuf)))
3674 : {
3675 0 : GDBErr(hr, "Failed to determine binary value for column " +
3676 0 : WStringToString(wstrFieldName));
3677 0 : foundBadColumn = true;
3678 0 : continue;
3679 : }
3680 :
3681 3500 : pOutFeature->SetField(i, (int)binaryBuf.inUseLength,
3682 3500 : (GByte *)binaryBuf.byteArray);
3683 : }
3684 3500 : break;
3685 :
3686 1755 : case OFTDateTime:
3687 : {
3688 : struct tm val;
3689 :
3690 1755 : if (FAILED(hr = pRow->GetDate(wstrFieldName, val)))
3691 : {
3692 0 : GDBErr(hr, "Failed to determine date value for column " +
3693 0 : WStringToString(wstrFieldName));
3694 0 : foundBadColumn = true;
3695 0 : continue;
3696 : }
3697 :
3698 1755 : pOutFeature->SetField(i, val.tm_year + 1900, val.tm_mon + 1,
3699 : val.tm_mday, val.tm_hour, val.tm_min,
3700 1755 : (float)val.tm_sec,
3701 1755 : m_bTimeInUTC ? 100 : 0);
3702 : // Examine test data to figure out how to extract that
3703 : }
3704 1755 : break;
3705 :
3706 0 : default:
3707 : {
3708 0 : if (!m_suppressColumnMappingError)
3709 : {
3710 0 : foundBadColumn = true;
3711 0 : CPLError(CE_Warning, CPLE_AppDefined,
3712 : "Row id: %d col:%d has unhandled col type (%d). "
3713 : "Setting to NULL.",
3714 : (int)oid, (int)i,
3715 0 : m_pFeatureDefn->GetFieldDefn(i)->GetType());
3716 : }
3717 : }
3718 : }
3719 : }
3720 :
3721 2011 : if (foundBadColumn)
3722 0 : m_suppressColumnMappingError = true;
3723 :
3724 2011 : *ppFeature = pOutFeature;
3725 :
3726 2011 : return true;
3727 : }
3728 :
3729 : /************************************************************************/
3730 : /* GetNextFeature() */
3731 : /************************************************************************/
3732 :
3733 2290 : OGRFeature *FGdbLayer::GetNextFeature()
3734 : {
3735 2290 : if (m_bFilterDirty)
3736 104 : ResetReading();
3737 :
3738 2290 : EndBulkLoad();
3739 :
3740 : #ifdef WORKAROUND_CRASH_ON_CDF_WITH_BINARY_FIELD
3741 2290 : if (!m_apoByteArrays.empty() && m_bWorkaroundCrashOnCDFWithBinaryField)
3742 : {
3743 : while (true)
3744 : {
3745 2022 : if (m_pEnumRows == nullptr)
3746 2022 : return nullptr;
3747 :
3748 : long hr;
3749 :
3750 2022 : Row rowOnlyOid;
3751 :
3752 2022 : if (FAILED(hr = m_pEnumRows->Next(rowOnlyOid)))
3753 : {
3754 17 : GDBErr(hr, "Failed fetching features");
3755 17 : return nullptr;
3756 : }
3757 :
3758 2005 : if (hr != S_OK)
3759 : {
3760 : // It's OK, we are done fetching - failure is caught by FAILED
3761 : // macro
3762 335 : return nullptr;
3763 : }
3764 :
3765 1670 : int32 oid = -1;
3766 1670 : if (FAILED(hr = rowOnlyOid.GetOID(oid)))
3767 : {
3768 0 : GDBErr(hr, "Failed to get oid");
3769 0 : continue;
3770 : }
3771 :
3772 1670 : EnumRows enumRows;
3773 1670 : OGRFeature *pOGRFeature = nullptr;
3774 1670 : Row rowFull;
3775 1670 : if (GetRow(enumRows, rowFull, oid) != OGRERR_NONE ||
3776 1670 : !OGRFeatureFromGdbRow(&rowFull, &pOGRFeature) || !pOGRFeature)
3777 : {
3778 0 : GDBErr(hr,
3779 : CPLSPrintf(
3780 : "Failed translating FGDB row [%d] to OGR Feature",
3781 : oid));
3782 :
3783 : // return NULL;
3784 0 : continue; // skip feature
3785 : }
3786 :
3787 1950 : if ((m_poFilterGeom == nullptr ||
3788 280 : FilterGeometry(pOGRFeature->GetGeometryRef())))
3789 : {
3790 1670 : return pOGRFeature;
3791 : }
3792 0 : delete pOGRFeature;
3793 0 : }
3794 : }
3795 : #endif
3796 :
3797 268 : OGRFeature *poFeature = FGdbBaseLayer::GetNextFeature();
3798 268 : if (poFeature)
3799 : {
3800 : std::map<int, int>::iterator oIter =
3801 240 : m_oMapFGDBFIDToOGRFID.find((int)poFeature->GetFID());
3802 240 : if (oIter != m_oMapFGDBFIDToOGRFID.end())
3803 : {
3804 4 : poFeature->SetFID(oIter->second);
3805 : }
3806 : }
3807 268 : return poFeature;
3808 : }
3809 :
3810 : /************************************************************************/
3811 : /* GetFeature() */
3812 : /************************************************************************/
3813 :
3814 154 : OGRFeature *FGdbLayer::GetFeature(GIntBig oid)
3815 : {
3816 : // do query to fetch individual row
3817 308 : EnumRows enumRows;
3818 308 : Row row;
3819 154 : if (!CPL_INT64_FITS_ON_INT32(oid) || m_pTable == nullptr)
3820 18 : return nullptr;
3821 :
3822 136 : EndBulkLoad();
3823 :
3824 136 : int nFID32 = (int)oid;
3825 136 : std::map<int, int>::iterator oIter = m_oMapOGRFIDToFGDBFID.find(nFID32);
3826 136 : if (oIter != m_oMapOGRFIDToFGDBFID.end())
3827 1 : nFID32 = oIter->second;
3828 135 : else if (m_oMapFGDBFIDToOGRFID.find(nFID32) != m_oMapFGDBFIDToOGRFID.end())
3829 0 : return nullptr;
3830 :
3831 136 : if (GetRow(enumRows, row, nFID32) != OGRERR_NONE)
3832 37 : return nullptr;
3833 :
3834 99 : OGRFeature *pOGRFeature = nullptr;
3835 :
3836 99 : if (!OGRFeatureFromGdbRow(&row, &pOGRFeature))
3837 : {
3838 0 : return nullptr;
3839 : }
3840 99 : if (pOGRFeature)
3841 : {
3842 99 : pOGRFeature->SetFID(oid);
3843 : }
3844 :
3845 99 : return pOGRFeature;
3846 : }
3847 :
3848 : /************************************************************************/
3849 : /* GetFeatureCount() */
3850 : /************************************************************************/
3851 :
3852 230 : GIntBig FGdbLayer::GetFeatureCount(CPL_UNUSED int bForce)
3853 : {
3854 230 : int32 rowCount = 0;
3855 :
3856 230 : if (m_pTable == nullptr)
3857 0 : return 0;
3858 :
3859 230 : EndBulkLoad();
3860 :
3861 230 : if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty())
3862 : {
3863 96 : ResetReading();
3864 96 : if (m_pEnumRows == nullptr)
3865 0 : return 0;
3866 :
3867 96 : int nFeatures = 0;
3868 : while (true)
3869 : {
3870 : long hr;
3871 :
3872 354 : Row row;
3873 :
3874 354 : if (FAILED(hr = m_pEnumRows->Next(row)))
3875 : {
3876 0 : GDBErr(hr, "Failed fetching features");
3877 0 : return 0;
3878 : }
3879 :
3880 354 : if (hr != S_OK)
3881 : {
3882 96 : break;
3883 : }
3884 :
3885 258 : if (m_poFilterGeom == nullptr)
3886 : {
3887 95 : nFeatures++;
3888 : }
3889 : else
3890 : {
3891 163 : ShapeBuffer gdbGeometry;
3892 163 : if (FAILED(hr = row.GetGeometry(gdbGeometry)))
3893 : {
3894 0 : continue;
3895 : }
3896 :
3897 163 : OGRGeometry *pOGRGeo = nullptr;
3898 163 : if (!GDBGeometryToOGRGeometry(m_forceMulti, &gdbGeometry,
3899 326 : m_pSRS, &pOGRGeo) ||
3900 163 : pOGRGeo == nullptr)
3901 : {
3902 0 : delete pOGRGeo;
3903 0 : continue;
3904 : }
3905 :
3906 163 : if (FilterGeometry(pOGRGeo))
3907 : {
3908 163 : nFeatures++;
3909 : }
3910 :
3911 163 : delete pOGRGeo;
3912 : }
3913 258 : }
3914 96 : ResetReading();
3915 96 : return nFeatures;
3916 : }
3917 :
3918 : long hr;
3919 134 : if (FAILED(hr = m_pTable->GetRowCount(rowCount)))
3920 : {
3921 0 : GDBErr(hr, "Failed counting rows");
3922 0 : return 0;
3923 : }
3924 :
3925 134 : return static_cast<int>(rowCount);
3926 : }
3927 :
3928 : /************************************************************************/
3929 : /* GetMetadataItem() */
3930 : /************************************************************************/
3931 :
3932 202 : const char *FGdbLayer::GetMetadataItem(const char *pszName,
3933 : const char *pszDomain)
3934 : {
3935 202 : if (pszDomain != nullptr && EQUAL(pszDomain, "MAP_OGR_FID_TO_FGDB_FID"))
3936 : {
3937 0 : if (m_oMapOGRFIDToFGDBFID.find(atoi(pszName)) !=
3938 0 : m_oMapOGRFIDToFGDBFID.end())
3939 0 : return CPLSPrintf("%d", m_oMapOGRFIDToFGDBFID[atoi(pszName)]);
3940 : }
3941 202 : else if (pszDomain != nullptr &&
3942 184 : EQUAL(pszDomain, "MAP_FGDB_FID_TO_OGR_FID"))
3943 : {
3944 0 : if (m_oMapFGDBFIDToOGRFID.find(atoi(pszName)) !=
3945 0 : m_oMapFGDBFIDToOGRFID.end())
3946 0 : return CPLSPrintf("%d", m_oMapFGDBFIDToOGRFID[atoi(pszName)]);
3947 : }
3948 202 : return OGRLayer::GetMetadataItem(pszName, pszDomain);
3949 : }
3950 :
3951 : /************************************************************************/
3952 : /* GetExtent() */
3953 : /************************************************************************/
3954 :
3955 161 : OGRErr FGdbLayer::GetExtent(OGREnvelope *psExtent, int bForce)
3956 : {
3957 161 : if (m_pTable == nullptr)
3958 0 : return OGRERR_FAILURE;
3959 :
3960 322 : if (m_poFilterGeom != nullptr || !m_wstrWhereClause.empty() ||
3961 161 : m_strShapeFieldName.empty())
3962 : {
3963 1 : const int nFieldCount = m_pFeatureDefn->GetFieldCount();
3964 1 : int *pabSaveFieldIgnored = new int[nFieldCount];
3965 14 : for (int i = 0; i < nFieldCount; i++)
3966 : {
3967 : // cppcheck-suppress uninitdata
3968 26 : pabSaveFieldIgnored[i] =
3969 13 : m_pFeatureDefn->GetFieldDefn(i)->IsIgnored();
3970 13 : m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(TRUE);
3971 : }
3972 1 : OGRErr eErr = OGRLayer::GetExtent(psExtent, bForce);
3973 14 : for (int i = 0; i < nFieldCount; i++)
3974 : {
3975 13 : m_pFeatureDefn->GetFieldDefn(i)->SetIgnored(pabSaveFieldIgnored[i]);
3976 : }
3977 1 : delete[] pabSaveFieldIgnored;
3978 1 : return eErr;
3979 : }
3980 :
3981 : long hr;
3982 320 : Envelope envelope;
3983 160 : if (FAILED(hr = m_pTable->GetExtent(envelope)))
3984 : {
3985 0 : GDBErr(hr, "Failed fetching extent");
3986 0 : return OGRERR_FAILURE;
3987 : }
3988 :
3989 160 : psExtent->MinX = envelope.xMin;
3990 160 : psExtent->MinY = envelope.yMin;
3991 160 : psExtent->MaxX = envelope.xMax;
3992 160 : psExtent->MaxY = envelope.yMax;
3993 :
3994 360 : if (std::isnan(psExtent->MinX) || std::isnan(psExtent->MinY) ||
3995 360 : std::isnan(psExtent->MaxX) || std::isnan(psExtent->MaxY))
3996 60 : return OGRERR_FAILURE;
3997 :
3998 100 : return OGRERR_NONE;
3999 : }
4000 :
4001 : /************************************************************************/
4002 : /* StartBulkLoad() */
4003 : /************************************************************************/
4004 :
4005 121 : void FGdbLayer::StartBulkLoad()
4006 : {
4007 121 : if (!m_pTable)
4008 0 : return;
4009 :
4010 121 : if (m_bBulkLoadInProgress)
4011 0 : return;
4012 :
4013 121 : m_bBulkLoadInProgress = TRUE;
4014 121 : m_pTable->LoadOnlyMode(true);
4015 121 : m_pTable->SetWriteLock();
4016 : }
4017 :
4018 : /************************************************************************/
4019 : /* EndBulkLoad() */
4020 : /************************************************************************/
4021 :
4022 6890 : void FGdbLayer::EndBulkLoad()
4023 : {
4024 6890 : if (!m_pTable)
4025 407 : return;
4026 :
4027 6483 : if (!m_bBulkLoadInProgress)
4028 6362 : return;
4029 :
4030 121 : m_bBulkLoadInProgress = FALSE;
4031 121 : m_bBulkLoadAllowed = -1; /* so that the configuration option is read the
4032 : first time we CreateFeature() again */
4033 121 : m_pTable->LoadOnlyMode(false);
4034 121 : m_pTable->FreeWriteLock();
4035 : }
4036 :
4037 : /* OGRErr FGdbLayer::StartTransaction ()
4038 : {
4039 : if ( ! m_pTable )
4040 : return OGRERR_FAILURE;
4041 :
4042 : m_pTable->LoadOnlyMode(true);
4043 : m_pTable->SetWriteLock();
4044 : return OGRERR_NONE;
4045 : } */
4046 :
4047 : /* OGRErr FGdbLayer::CommitTransaction ()
4048 : {
4049 : if ( ! m_pTable )
4050 : return OGRERR_FAILURE;
4051 :
4052 : m_pTable->LoadOnlyMode(false);
4053 : m_pTable->FreeWriteLock();
4054 : return OGRERR_NONE;
4055 : } */
4056 :
4057 : /* OGRErr FGdbLayer::RollbackTransaction ()
4058 : {
4059 : if ( ! m_pTable )
4060 : return OGRERR_FAILURE;
4061 :
4062 : m_pTable->LoadOnlyMode(false);
4063 : m_pTable->FreeWriteLock();
4064 : return OGRERR_NONE;
4065 : } */
4066 :
4067 : /************************************************************************/
4068 : /* GetLayerXML() */
4069 : /* Return XML definition of the Layer as provided by FGDB. Caller must */
4070 : /* free result. */
4071 : /* Not currently used by the driver, but can be used by external code */
4072 : /* for specific purposes. */
4073 : /************************************************************************/
4074 :
4075 17 : OGRErr FGdbLayer::GetLayerXML(char **ppXml)
4076 : {
4077 : long hr;
4078 34 : std::string xml;
4079 :
4080 17 : if (m_pTable == nullptr)
4081 0 : return OGRERR_FAILURE;
4082 :
4083 17 : if (FAILED(hr = m_pTable->GetDefinition(xml)))
4084 : {
4085 0 : GDBErr(hr, "Failed fetching XML table definition");
4086 0 : return OGRERR_FAILURE;
4087 : }
4088 :
4089 17 : *ppXml = CPLStrdup(xml.c_str());
4090 17 : return OGRERR_NONE;
4091 : }
4092 :
4093 : /************************************************************************/
4094 : /* GetLayerMetadataXML() */
4095 : /* Return XML metadata for the Layer as provided by FGDB. Caller must */
4096 : /* free result. */
4097 : /* Not currently used by the driver, but can be used by external code */
4098 : /* for specific purposes. */
4099 : /************************************************************************/
4100 :
4101 17 : OGRErr FGdbLayer::GetLayerMetadataXML(char **ppXml)
4102 : {
4103 : long hr;
4104 34 : std::string xml;
4105 :
4106 17 : if (m_pTable == nullptr)
4107 0 : return OGRERR_FAILURE;
4108 :
4109 17 : if (FAILED(hr = m_pTable->GetDocumentation(xml)))
4110 : {
4111 0 : GDBErr(hr, "Failed fetching XML table metadata");
4112 0 : return OGRERR_FAILURE;
4113 : }
4114 :
4115 17 : *ppXml = CPLStrdup(xml.c_str());
4116 17 : return OGRERR_NONE;
4117 : }
4118 :
4119 : /************************************************************************/
4120 : /* Rename() */
4121 : /************************************************************************/
4122 :
4123 8 : OGRErr FGdbLayer::Rename(const char *pszDstTableName)
4124 : {
4125 8 : if (!TestCapability(OLCRename))
4126 0 : return OGRERR_FAILURE;
4127 :
4128 8 : if (m_pTable == nullptr)
4129 0 : return OGRERR_FAILURE;
4130 :
4131 8 : if (m_pDS->GetLayerByName(pszDstTableName) != nullptr)
4132 : {
4133 4 : CPLError(CE_Failure, CPLE_AppDefined, "Layer %s already exists",
4134 : pszDstTableName);
4135 4 : return OGRERR_FAILURE;
4136 : }
4137 :
4138 8 : long hr = m_pDS->GetGDB()->Rename(m_wstrTablePath, m_wstrType,
4139 8 : StringToWString(pszDstTableName));
4140 :
4141 4 : if (FAILED(hr))
4142 : {
4143 0 : GDBErr(hr, "Failed renaming layer");
4144 0 : return OGRERR_FAILURE;
4145 : }
4146 :
4147 4 : m_strName = pszDstTableName;
4148 4 : auto strTablePath = WStringToString(m_wstrTablePath);
4149 : m_wstrTablePath =
4150 16 : StringToWString(strTablePath.substr(0, strTablePath.rfind('\\')) +
4151 12 : "\\" + pszDstTableName);
4152 4 : SetDescription(pszDstTableName);
4153 4 : m_pFeatureDefn->SetName(pszDstTableName);
4154 :
4155 4 : return OGRERR_NONE;
4156 : }
4157 :
4158 : /************************************************************************/
4159 : /* TestCapability() */
4160 : /************************************************************************/
4161 :
4162 1239 : int FGdbLayer::TestCapability(const char *pszCap)
4163 : {
4164 :
4165 1239 : if (EQUAL(pszCap, OLCRandomRead))
4166 2 : return TRUE;
4167 :
4168 1237 : else if (EQUAL(pszCap, OLCFastFeatureCount))
4169 0 : return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
4170 :
4171 1237 : else if (EQUAL(pszCap, OLCFastSpatialFilter))
4172 0 : return TRUE;
4173 :
4174 1237 : else if (EQUAL(pszCap, OLCFastGetExtent))
4175 30 : return m_poFilterGeom == nullptr && m_wstrWhereClause.empty();
4176 :
4177 1207 : else if (EQUAL(pszCap, OLCCreateField)) /* CreateField() */
4178 36 : return m_pDS->GetUpdate();
4179 :
4180 1171 : else if (EQUAL(pszCap, OLCSequentialWrite)) /* ICreateFeature() */
4181 18 : return m_pDS->GetUpdate();
4182 :
4183 1153 : else if (EQUAL(pszCap,
4184 : OLCStringsAsUTF8)) /* Native UTF16, converted to UTF8 */
4185 18 : return TRUE;
4186 :
4187 1135 : else if (EQUAL(pszCap, OLCDeleteFeature)) /* DeleteFeature() */
4188 18 : return m_pDS->GetUpdate();
4189 :
4190 1117 : else if (EQUAL(pszCap, OLCRandomWrite)) /* ISetFeature() */
4191 20 : return m_pDS->GetUpdate();
4192 :
4193 1097 : else if (EQUAL(pszCap, OLCDeleteField)) /* DeleteField() */
4194 19 : return m_pDS->GetUpdate();
4195 :
4196 : #ifdef AlterFieldDefn_implemented_but_not_working
4197 : else if (EQUAL(pszCap, OLCAlterFieldDefn)) /* AlterFieldDefn() */
4198 : return m_pDS->GetUpdate();
4199 : #endif
4200 :
4201 1078 : else if (EQUAL(pszCap, OLCRename)) /* Rename() */
4202 10 : return m_pDS->GetUpdate();
4203 :
4204 1068 : else if (EQUAL(pszCap,
4205 : OLCFastSetNextByIndex)) /* TBD FastSetNextByIndex() */
4206 0 : return FALSE;
4207 :
4208 1068 : else if (EQUAL(pszCap, OLCTransactions)) /* TBD Start/End Transactions() */
4209 2 : return FALSE;
4210 :
4211 1066 : else if (EQUAL(pszCap, OLCIgnoreFields))
4212 18 : return TRUE;
4213 :
4214 1048 : else if (EQUAL(pszCap, OLCMeasuredGeometries))
4215 457 : return TRUE;
4216 :
4217 591 : else if (EQUAL(pszCap, OLCZGeometries))
4218 54 : return TRUE;
4219 :
4220 : else
4221 537 : return FALSE;
4222 : }
4223 :
4224 : /************************************************************************/
4225 : /* CreateRealCopy() */
4226 : /************************************************************************/
4227 :
4228 0 : int FGdbLayer::CreateRealCopy()
4229 : {
4230 0 : CPLAssert(m_bSymlinkFlag);
4231 :
4232 : // Find the FID of the layer in the system catalog
4233 0 : char *apszDrivers[2] = {nullptr};
4234 0 : apszDrivers[0] = (char *)"OpenFileGDB";
4235 0 : apszDrivers[1] = nullptr;
4236 : const std::string osSystemCatalog =
4237 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), "a00000001.gdbtable", nullptr);
4238 0 : GDALDataset *poOpenFileGDBDS = GDALDataset::Open(
4239 : osSystemCatalog.c_str(), GDAL_OF_VECTOR, apszDrivers, nullptr, nullptr);
4240 0 : if (poOpenFileGDBDS == nullptr || poOpenFileGDBDS->GetLayer(0) == nullptr)
4241 : {
4242 0 : CPLError(CE_Failure, CPLE_AppDefined,
4243 : "Cannot open %s with OpenFileGDB driver. Should not happen.",
4244 : osSystemCatalog.c_str());
4245 0 : GDALClose(poOpenFileGDBDS);
4246 0 : return FALSE;
4247 : }
4248 :
4249 0 : OGRLayer *poLayer = poOpenFileGDBDS->GetLayer(0);
4250 0 : CPLString osFilter = "name = '";
4251 0 : osFilter += GetName();
4252 0 : osFilter += "'";
4253 0 : poLayer->SetAttributeFilter(osFilter);
4254 0 : poLayer->ResetReading();
4255 0 : OGRFeature *poF = poLayer->GetNextFeature();
4256 0 : if (poF == nullptr)
4257 : {
4258 0 : CPLError(CE_Failure, CPLE_AppDefined,
4259 0 : "Cannot find filename for layer %s", GetName());
4260 0 : GDALClose(poOpenFileGDBDS);
4261 0 : return FALSE;
4262 : }
4263 0 : int nLayerFID = (int)poF->GetFID();
4264 0 : delete poF;
4265 0 : GDALClose(poOpenFileGDBDS);
4266 :
4267 0 : if (!m_pDS->CloseInternal(TRUE))
4268 0 : return FALSE;
4269 :
4270 : // Create real copies (in .tmp files now) instead of symlinks
4271 0 : char **papszFiles = VSIReadDir(m_pDS->GetFSName());
4272 0 : CPLString osBasename(CPLSPrintf("a%08x", nLayerFID));
4273 0 : int bError = FALSE;
4274 0 : std::vector<CPLString> aoFiles;
4275 0 : for (char **papszIter = papszFiles; !bError && papszIter && *papszIter;
4276 : papszIter++)
4277 : {
4278 0 : if (strncmp(*papszIter, osBasename.c_str(), osBasename.size()) == 0)
4279 : {
4280 0 : if (CPLCopyFile(
4281 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), *papszIter, "tmp")
4282 : .c_str(),
4283 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), *papszIter, nullptr)
4284 0 : .c_str()) != 0)
4285 : {
4286 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot copy %s",
4287 : *papszIter);
4288 0 : bError = TRUE;
4289 : }
4290 : else
4291 0 : aoFiles.push_back(*papszIter);
4292 : }
4293 : }
4294 0 : CSLDestroy(papszFiles);
4295 :
4296 : // Rename the .tmp into normal filenames
4297 0 : for (size_t i = 0; !bError && i < aoFiles.size(); i++)
4298 : {
4299 0 : if (VSIUnlink(
4300 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), aoFiles[i], nullptr)
4301 0 : .c_str()) != 0 ||
4302 0 : VSIRename(
4303 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), aoFiles[i], "tmp")
4304 : .c_str(),
4305 0 : CPLFormFilenameSafe(m_pDS->GetFSName(), aoFiles[i], nullptr)
4306 : .c_str()) != 0)
4307 : {
4308 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot rename %s.tmp",
4309 0 : aoFiles[i].c_str());
4310 0 : bError = TRUE;
4311 : }
4312 : }
4313 :
4314 0 : int bRet = !bError && m_pDS->ReOpen();
4315 0 : if (bRet)
4316 0 : m_bSymlinkFlag = FALSE;
4317 0 : return bRet;
4318 : }
4319 :
4320 : /************************************************************************/
4321 : /* GetDataset() */
4322 : /************************************************************************/
4323 :
4324 38 : GDALDataset *FGdbLayer::GetDataset()
4325 : {
4326 38 : return m_pDS;
4327 : }
|