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