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