LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/filegdb - FGdbLayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1378 1958 70.4 %
Date: 2025-01-18 12:42:00 Functions: 48 51 94.1 %

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

Generated by: LCOV version 1.14