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

Generated by: LCOV version 1.14