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