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

Generated by: LCOV version 1.14