LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - ogropenfilegdblayer_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1439 1659 86.7 %
Date: 2024-11-21 22:18:42 Functions: 36 37 97.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements Open FileGDB OGR driver.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "ogr_openfilegdb.h"
      15             : #include "filegdb_gdbtoogrfieldtype.h"
      16             : 
      17             : #include <cinttypes>
      18             : #include <cmath>
      19             : #include <cstddef>
      20             : #include <cstdio>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <cwchar>
      24             : #include <algorithm>
      25             : #include <limits>
      26             : #include <string>
      27             : 
      28             : #include "cpl_conv.h"
      29             : #include "cpl_error.h"
      30             : #include "cpl_minixml.h"
      31             : #include "cpl_string.h"
      32             : #include "gdal_priv_templates.hpp"
      33             : #include "ogr_api.h"
      34             : #include "ogr_core.h"
      35             : #include "ogr_feature.h"
      36             : #include "ogr_geometry.h"
      37             : #include "ogr_spatialref.h"
      38             : #include "ogr_srs_api.h"
      39             : #include "ogrsf_frmts.h"
      40             : #include "filegdbtable.h"
      41             : #include "filegdbtable_priv.h"
      42             : #include "filegdb_coordprec_write.h"
      43             : #include "filegdb_reserved_keywords.h"
      44             : 
      45             : /*************************************************************************/
      46             : /*                            StringToWString()                          */
      47             : /*************************************************************************/
      48             : 
      49        2130 : static std::wstring StringToWString(const std::string &utf8string)
      50             : {
      51             :     wchar_t *pszUTF16 =
      52        2130 :         CPLRecodeToWChar(utf8string.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
      53        2130 :     std::wstring utf16string = pszUTF16;
      54        2130 :     CPLFree(pszUTF16);
      55        2130 :     return utf16string;
      56             : }
      57             : 
      58             : /*************************************************************************/
      59             : /*                            WStringToString()                          */
      60             : /*************************************************************************/
      61             : 
      62        2533 : static std::string WStringToString(const std::wstring &utf16string)
      63             : {
      64             :     char *pszUTF8 =
      65        2533 :         CPLRecodeFromWChar(utf16string.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
      66        2533 :     std::string utf8string = pszUTF8;
      67        2533 :     CPLFree(pszUTF8);
      68        2533 :     return utf8string;
      69             : }
      70             : 
      71             : /*************************************************************************/
      72             : /*                              LaunderName()                            */
      73             : /*************************************************************************/
      74             : 
      75         623 : static std::wstring LaunderName(const std::wstring &name)
      76             : {
      77         623 :     std::wstring newName = name;
      78             : 
      79             :     // https://support.esri.com/en/technical-article/000005588
      80             : 
      81             :     // "Do not start field or table names with an underscore or a number."
      82             :     // But we can see in the wild table names starting with underscore...
      83             :     // (cf https://github.com/OSGeo/gdal/issues/4112)
      84         623 :     if (!newName.empty() && newName[0] >= '0' && newName[0] <= '9')
      85             :     {
      86           2 :         newName = StringToWString("_") + newName;
      87             :     }
      88             : 
      89             :     // "Essentially, eliminate anything that is not alphanumeric or an
      90             :     // underscore." Note: alphanumeric unicode is supported
      91        5678 :     for (size_t i = 0; i < newName.size(); i++)
      92             :     {
      93        9049 :         if (!(newName[i] == '_' || (newName[i] >= '0' && newName[i] <= '9') ||
      94        3994 :               (newName[i] >= 'a' && newName[i] <= 'z') ||
      95         493 :               (newName[i] >= 'A' && newName[i] <= 'Z') || newName[i] >= 128))
      96             :         {
      97          61 :             newName[i] = '_';
      98             :         }
      99             :     }
     100             : 
     101         623 :     return newName;
     102             : }
     103             : 
     104             : /*************************************************************************/
     105             : /*                      EscapeUnsupportedPrefixes()                      */
     106             : /*************************************************************************/
     107             : 
     108         278 : static std::wstring EscapeUnsupportedPrefixes(const std::wstring &className)
     109             : {
     110         278 :     std::wstring newName = className;
     111             :     // From ESRI docs
     112             :     // Feature classes starting with these strings are unsupported.
     113             :     static const char *const UNSUPPORTED_PREFIXES[] = {"sde_", "gdb_", "delta_",
     114             :                                                        nullptr};
     115             : 
     116        1109 :     for (int i = 0; UNSUPPORTED_PREFIXES[i] != nullptr; i++)
     117             :     {
     118             :         // cppcheck-suppress stlIfStrFind
     119         832 :         if (newName.find(StringToWString(UNSUPPORTED_PREFIXES[i])) == 0)
     120             :         {
     121             :             // Normally table names shouldn't start with underscore, but
     122             :             // there are such in the wild (cf
     123             :             // https://github.com/OSGeo/gdal/issues/4112)
     124           1 :             newName = StringToWString("_") + newName;
     125           1 :             break;
     126             :         }
     127             :     }
     128             : 
     129         278 :     return newName;
     130             : }
     131             : 
     132             : /*************************************************************************/
     133             : /*                         EscapeReservedKeywords()                      */
     134             : /*************************************************************************/
     135             : 
     136         642 : static std::wstring EscapeReservedKeywords(const std::wstring &name)
     137             : {
     138        1284 :     std::string newName = WStringToString(name);
     139        1284 :     std::string upperName = CPLString(newName).toupper();
     140             : 
     141             :     // Append an underscore to any FGDB reserved words used as field names
     142             :     // This is the same behavior ArcCatalog follows.
     143       18578 :     for (const char *pszKeyword : apszRESERVED_WORDS)
     144             :     {
     145       17939 :         if (upperName == pszKeyword)
     146             :         {
     147           3 :             newName += '_';
     148           3 :             break;
     149             :         }
     150             :     }
     151             : 
     152        1284 :     return StringToWString(newName);
     153             : }
     154             : 
     155             : /***********************************************************************/
     156             : /*                     XMLSerializeGeomFieldBase()                     */
     157             : /***********************************************************************/
     158             : 
     159         440 : static void XMLSerializeGeomFieldBase(CPLXMLNode *psRoot,
     160             :                                       const FileGDBGeomField *poGeomFieldDefn,
     161             :                                       const OGRSpatialReference *poSRS)
     162             : {
     163         440 :     auto psExtent = CPLCreateXMLElementAndValue(psRoot, "Extent", "");
     164         440 :     CPLAddXMLAttributeAndValue(psExtent, "xsi:nil", "true");
     165             : 
     166             :     auto psSpatialReference =
     167         440 :         CPLCreateXMLNode(psRoot, CXT_Element, "SpatialReference");
     168             : 
     169         440 :     if (poSRS == nullptr)
     170             :     {
     171         400 :         CPLAddXMLAttributeAndValue(psSpatialReference, "xsi:type",
     172             :                                    "typens:UnknownCoordinateSystem");
     173             :     }
     174             :     else
     175             :     {
     176          40 :         if (poSRS->IsGeographic())
     177          27 :             CPLAddXMLAttributeAndValue(psSpatialReference, "xsi:type",
     178             :                                        "typens:GeographicCoordinateSystem");
     179             :         else
     180          13 :             CPLAddXMLAttributeAndValue(psSpatialReference, "xsi:type",
     181             :                                        "typens:ProjectedCoordinateSystem");
     182          40 :         CPLCreateXMLElementAndValue(psSpatialReference, "WKT",
     183          40 :                                     poGeomFieldDefn->GetWKT().c_str());
     184             :     }
     185         440 :     CPLCreateXMLElementAndValue(
     186             :         psSpatialReference, "XOrigin",
     187             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetXOrigin()));
     188         440 :     CPLCreateXMLElementAndValue(
     189             :         psSpatialReference, "YOrigin",
     190             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetYOrigin()));
     191         440 :     CPLCreateXMLElementAndValue(
     192             :         psSpatialReference, "XYScale",
     193             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetXYScale()));
     194         440 :     CPLCreateXMLElementAndValue(
     195             :         psSpatialReference, "ZOrigin",
     196             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetZOrigin()));
     197         440 :     CPLCreateXMLElementAndValue(
     198             :         psSpatialReference, "ZScale",
     199             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetZScale()));
     200         440 :     CPLCreateXMLElementAndValue(
     201             :         psSpatialReference, "MOrigin",
     202             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetMOrigin()));
     203         440 :     CPLCreateXMLElementAndValue(
     204             :         psSpatialReference, "MScale",
     205             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetMScale()));
     206         440 :     CPLCreateXMLElementAndValue(
     207             :         psSpatialReference, "XYTolerance",
     208             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetXYTolerance()));
     209         440 :     CPLCreateXMLElementAndValue(
     210             :         psSpatialReference, "ZTolerance",
     211             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetZTolerance()));
     212         440 :     CPLCreateXMLElementAndValue(
     213             :         psSpatialReference, "MTolerance",
     214             :         CPLSPrintf("%.17g", poGeomFieldDefn->GetMTolerance()));
     215         440 :     CPLCreateXMLElementAndValue(psSpatialReference, "HighPrecision", "true");
     216         440 :     if (poSRS)
     217             :     {
     218          40 :         if (CPLTestBool(CPLGetConfigOption("OPENFILEGDB_WRITE_WKID", "YES")))
     219             :         {
     220          40 :             const char *pszKey = poSRS->IsProjected() ? "PROJCS" : "GEOGCS";
     221          40 :             const char *pszAuthorityName = poSRS->GetAuthorityName(pszKey);
     222          40 :             const char *pszAuthorityCode = poSRS->GetAuthorityCode(pszKey);
     223          40 :             if (pszAuthorityName && pszAuthorityCode &&
     224          40 :                 (EQUAL(pszAuthorityName, "EPSG") ||
     225           0 :                  EQUAL(pszAuthorityName, "ESRI")))
     226             :             {
     227          40 :                 CPLCreateXMLElementAndValue(psSpatialReference, "WKID",
     228             :                                             pszAuthorityCode);
     229          40 :                 if (CPLTestBool(CPLGetConfigOption(
     230             :                         "OPENFILEGDB_WRITE_LATESTWKID", "YES")))
     231             :                 {
     232          40 :                     CPLCreateXMLElementAndValue(psSpatialReference,
     233             :                                                 "LatestWKID", pszAuthorityCode);
     234             :                 }
     235             :             }
     236             :         }
     237             : 
     238          40 :         if (poSRS->IsCompound() &&
     239           0 :             CPLTestBool(CPLGetConfigOption("OPENFILEGDB_WRITE_VCSWKID", "YES")))
     240             :         {
     241           0 :             const char *pszAuthorityName = poSRS->GetAuthorityName("VERT_CS");
     242           0 :             const char *pszAuthorityCode = poSRS->GetAuthorityCode("VERT_CS");
     243           0 :             if (pszAuthorityName && pszAuthorityCode &&
     244           0 :                 (EQUAL(pszAuthorityName, "EPSG") ||
     245           0 :                  EQUAL(pszAuthorityName, "ESRI")))
     246             :             {
     247           0 :                 CPLCreateXMLElementAndValue(psSpatialReference, "VCSWKID",
     248             :                                             pszAuthorityCode);
     249           0 :                 if (CPLTestBool(CPLGetConfigOption(
     250             :                         "OPENFILEGDB_WRITE_LATESTVCSWKID", "YES")))
     251             :                 {
     252           0 :                     CPLCreateXMLElementAndValue(
     253             :                         psSpatialReference, "LatestVCSWKID", pszAuthorityCode);
     254             :                 }
     255             :             }
     256             :         }
     257             :     }
     258         440 : }
     259             : 
     260             : /***********************************************************************/
     261             : /*                    CreateFeatureDataset()                           */
     262             : /***********************************************************************/
     263             : 
     264           3 : bool OGROpenFileGDBLayer::CreateFeatureDataset(const char *pszFeatureDataset)
     265             : {
     266           6 :     std::string osPath("\\");
     267           3 :     osPath += pszFeatureDataset;
     268             : 
     269           6 :     CPLXMLTreeCloser oTree(CPLCreateXMLNode(nullptr, CXT_Element, "?xml"));
     270           3 :     CPLAddXMLAttributeAndValue(oTree.get(), "version", "1.0");
     271           3 :     CPLAddXMLAttributeAndValue(oTree.get(), "encoding", "UTF-8");
     272             : 
     273             :     CPLXMLNode *psRoot =
     274           3 :         CPLCreateXMLNode(nullptr, CXT_Element, "typens:DEFeatureDataset");
     275           3 :     CPLAddXMLSibling(oTree.get(), psRoot);
     276             : 
     277           3 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xsi",
     278             :                                "http://www.w3.org/2001/XMLSchema-instance");
     279           3 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xs",
     280             :                                "http://www.w3.org/2001/XMLSchema");
     281           3 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:typens",
     282             :                                "http://www.esri.com/schemas/ArcGIS/10.1");
     283           3 :     CPLAddXMLAttributeAndValue(psRoot, "xsi:type", "typens:DEFeatureDataset");
     284             : 
     285           3 :     CPLCreateXMLElementAndValue(psRoot, "CatalogPath", osPath.c_str());
     286           3 :     CPLCreateXMLElementAndValue(psRoot, "Name", pszFeatureDataset);
     287           3 :     CPLCreateXMLElementAndValue(psRoot, "ChildrenExpanded", "false");
     288           3 :     CPLCreateXMLElementAndValue(psRoot, "DatasetType", "esriDTFeatureDataset");
     289             : 
     290             :     {
     291           3 :         FileGDBTable oTable;
     292           3 :         if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), false))
     293           0 :             return false;
     294           3 :         CPLCreateXMLElementAndValue(
     295             :             psRoot, "DSID",
     296           3 :             CPLSPrintf("%" PRId64, 1 + oTable.GetTotalRecordCount()));
     297             :     }
     298             : 
     299           3 :     CPLCreateXMLElementAndValue(psRoot, "Versioned", "false");
     300           3 :     CPLCreateXMLElementAndValue(psRoot, "CanVersion", "false");
     301             : 
     302           3 :     if (m_eGeomType != wkbNone)
     303             :     {
     304           3 :         XMLSerializeGeomFieldBase(psRoot, m_poLyrTable->GetGeomField(),
     305           3 :                                   GetSpatialRef());
     306             :     }
     307             : 
     308           3 :     char *pszDefinition = CPLSerializeXMLTree(oTree.get());
     309           6 :     const std::string osDefinition = pszDefinition;
     310           3 :     CPLFree(pszDefinition);
     311             : 
     312           3 :     m_osFeatureDatasetGUID = OFGDBGenerateUUID();
     313             : 
     314           6 :     if (!m_poDS->RegisterInItemRelationships(
     315           3 :             m_poDS->m_osRootGUID, m_osFeatureDatasetGUID,
     316             :             "{dc78f1ab-34e4-43ac-ba47-1c4eabd0e7c7}"))
     317             :     {
     318           0 :         return false;
     319             :     }
     320             : 
     321           6 :     if (!m_poDS->RegisterFeatureDatasetInItems(
     322           3 :             m_osFeatureDatasetGUID, pszFeatureDataset, osDefinition.c_str()))
     323             :     {
     324           0 :         return false;
     325             :     }
     326             : 
     327           3 :     return true;
     328             : }
     329             : 
     330             : /***********************************************************************/
     331             : /*                      GetLaunderedLayerName()                        */
     332             : /***********************************************************************/
     333             : 
     334             : std::string
     335         278 : OGROpenFileGDBLayer::GetLaunderedLayerName(const std::string &osNameOri) const
     336             : {
     337         556 :     std::wstring wlayerName = StringToWString(osNameOri);
     338             : 
     339         278 :     wlayerName = LaunderName(wlayerName);
     340         278 :     wlayerName = EscapeReservedKeywords(wlayerName);
     341         278 :     wlayerName = EscapeUnsupportedPrefixes(wlayerName);
     342             : 
     343             :     // https://desktop.arcgis.com/en/arcmap/latest/manage-data/administer-file-gdbs/file-geodatabase-size-and-name-limits.htm
     344             :     // document 160 character limit but
     345             :     // https://desktop.arcgis.com/en/arcmap/latest/manage-data/tables/fundamentals-of-adding-and-deleting-fields.htm#GUID-8E190093-8F8F-4132-AF4F-B0C9220F76B3
     346             :     // mentions 64. let be optimistic and aim for 160
     347         278 :     constexpr size_t TABLE_NAME_MAX_SIZE = 160;
     348         278 :     if (wlayerName.size() > TABLE_NAME_MAX_SIZE)
     349           4 :         wlayerName.resize(TABLE_NAME_MAX_SIZE);
     350             : 
     351             :     /* Ensures uniqueness of layer name */
     352         278 :     int numRenames = 1;
     353         570 :     while ((m_poDS->GetLayerByName(WStringToString(wlayerName).c_str()) !=
     354         855 :             nullptr) &&
     355             :            (numRenames < 10))
     356             :     {
     357          21 :         wlayerName = StringToWString(CPLSPrintf(
     358             :             "%s_%d",
     359          14 :             WStringToString(wlayerName.substr(0, TABLE_NAME_MAX_SIZE - 2))
     360             :                 .c_str(),
     361           7 :             numRenames));
     362           7 :         numRenames++;
     363             :     }
     364         556 :     while ((m_poDS->GetLayerByName(WStringToString(wlayerName).c_str()) !=
     365         834 :             nullptr) &&
     366             :            (numRenames < 100))
     367             :     {
     368           0 :         wlayerName = StringToWString(CPLSPrintf(
     369             :             "%s_%d",
     370           0 :             WStringToString(wlayerName.substr(0, TABLE_NAME_MAX_SIZE - 3))
     371             :                 .c_str(),
     372           0 :             numRenames));
     373           0 :         numRenames++;
     374             :     }
     375             : 
     376         556 :     return WStringToString(wlayerName);
     377             : }
     378             : 
     379             : /***********************************************************************/
     380             : /*                            Create()                                 */
     381             : /***********************************************************************/
     382             : 
     383         270 : bool OGROpenFileGDBLayer::Create(const OGRGeomFieldDefn *poSrcGeomFieldDefn)
     384             : {
     385         270 :     FileGDBTableGeometryType eTableGeomType = FGTGT_NONE;
     386         270 :     const auto eFlattenType = wkbFlatten(OGR_GT_GetLinear(m_eGeomType));
     387         270 :     if (eFlattenType == wkbNone)
     388          73 :         eTableGeomType = FGTGT_NONE;
     389         197 :     else if (eFlattenType == wkbPoint)
     390          96 :         eTableGeomType = FGTGT_POINT;
     391         101 :     else if (eFlattenType == wkbMultiPoint)
     392          11 :         eTableGeomType = FGTGT_MULTIPOINT;
     393         175 :     else if (OGR_GT_IsCurve(eFlattenType) ||
     394          85 :              OGR_GT_IsSubClassOf(eFlattenType, wkbMultiCurve))
     395             :     {
     396          38 :         eTableGeomType = FGTGT_LINE;
     397             :     }
     398          94 :     else if (OGR_GT_IsSurface(eFlattenType) ||
     399          42 :              OGR_GT_IsSubClassOf(eFlattenType, wkbMultiSurface))
     400             :     {
     401          47 :         eTableGeomType = FGTGT_POLYGON;
     402             :     }
     403           5 :     else if (eFlattenType == wkbTIN || eFlattenType == wkbPolyhedralSurface ||
     404          12 :              m_eGeomType == wkbGeometryCollection25D ||
     405           2 :              m_eGeomType == wkbSetZ(wkbUnknown))
     406             :     {
     407           3 :         eTableGeomType = FGTGT_MULTIPATCH;
     408             :     }
     409             :     else
     410             :     {
     411           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported geometry type");
     412           2 :         return false;
     413             :     }
     414             : 
     415         536 :     const std::string osNameOri(m_osName);
     416             :     /* Launder the Layer name */
     417         268 :     m_osName = GetLaunderedLayerName(osNameOri);
     418         268 :     if (osNameOri != m_osName)
     419             :     {
     420          47 :         CPLError(CE_Warning, CPLE_NotSupported,
     421             :                  "Normalized/laundered layer name: '%s' to '%s'",
     422             :                  osNameOri.c_str(), m_osName.c_str());
     423             :     }
     424             : 
     425             :     const char *pszFeatureDataset =
     426         268 :         m_aosCreationOptions.FetchNameValue("FEATURE_DATASET");
     427         536 :     std::string osFeatureDatasetDef;
     428         268 :     std::unique_ptr<OGRSpatialReference> poFeatureDatasetSRS;
     429         268 :     if (pszFeatureDataset)
     430             :     {
     431             :         {
     432           7 :             FileGDBTable oTable;
     433           7 :             if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), false))
     434           0 :                 return false;
     435             : 
     436           7 :             FETCH_FIELD_IDX(iUUID, "UUID", FGFT_GLOBALID);
     437           7 :             FETCH_FIELD_IDX(iName, "Name", FGFT_STRING);
     438           7 :             FETCH_FIELD_IDX(iDefinition, "Definition", FGFT_XML);
     439             : 
     440          22 :             for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
     441             :                  ++iCurFeat)
     442             :             {
     443          19 :                 iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
     444          19 :                 if (iCurFeat < 0)
     445           0 :                     break;
     446          19 :                 const auto psName = oTable.GetFieldValue(iName);
     447          19 :                 if (psName && strcmp(psName->String, pszFeatureDataset) == 0)
     448             :                 {
     449           4 :                     const auto psDefinition = oTable.GetFieldValue(iDefinition);
     450           4 :                     if (psDefinition)
     451             :                     {
     452           4 :                         osFeatureDatasetDef = psDefinition->String;
     453             :                     }
     454             :                     else
     455             :                     {
     456           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     457             :                                  "Feature dataset found, but no definition");
     458           0 :                         return false;
     459             :                     }
     460             : 
     461           4 :                     const auto psUUID = oTable.GetFieldValue(iUUID);
     462           4 :                     if (psUUID == nullptr)
     463             :                     {
     464           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     465             :                                  "Feature dataset found, but no UUID");
     466           0 :                         return false;
     467             :                     }
     468             : 
     469           4 :                     m_osFeatureDatasetGUID = psUUID->String;
     470           4 :                     break;
     471             :                 }
     472             :             }
     473             :         }
     474             :         CPLXMLNode *psParentTree =
     475           7 :             CPLParseXMLString(osFeatureDatasetDef.c_str());
     476           7 :         if (psParentTree != nullptr)
     477             :         {
     478           4 :             CPLStripXMLNamespace(psParentTree, nullptr, TRUE);
     479             :             CPLXMLNode *psParentInfo =
     480           4 :                 CPLSearchXMLNode(psParentTree, "=DEFeatureDataset");
     481           4 :             if (psParentInfo != nullptr)
     482             :             {
     483           4 :                 poFeatureDatasetSRS.reset(m_poDS->BuildSRS(psParentInfo));
     484             :             }
     485           4 :             CPLDestroyXMLNode(psParentTree);
     486             :         }
     487             :     }
     488             : 
     489         268 :     m_poFeatureDefn =
     490         268 :         new OGROpenFileGDBFeatureDefn(this, m_osName.c_str(), true);
     491         268 :     SetDescription(m_poFeatureDefn->GetName());
     492         268 :     m_poFeatureDefn->SetGeomType(wkbNone);
     493         268 :     m_poFeatureDefn->Reference();
     494             : 
     495         268 :     m_osThisGUID = OFGDBGenerateUUID();
     496             : 
     497         268 :     m_bValidLayerDefn = true;
     498         268 :     m_bEditable = true;
     499         268 :     m_bRegisteredTable = false;
     500         268 :     m_bTimeInUTC = CPLTestBool(
     501             :         m_aosCreationOptions.FetchNameValueDef("TIME_IN_UTC", "YES"));
     502             : 
     503         268 :     int nTablxOffsetSize = 5;
     504         268 :     bool bTextUTF16 = false;
     505             :     const char *pszConfigurationKeyword =
     506         268 :         m_aosCreationOptions.FetchNameValue("CONFIGURATION_KEYWORD");
     507         268 :     if (pszConfigurationKeyword)
     508             :     {
     509           1 :         if (EQUAL(pszConfigurationKeyword, "MAX_FILE_SIZE_4GB"))
     510             :         {
     511           0 :             m_osConfigurationKeyword = "MAX_FILE_SIZE_4GB";
     512           0 :             nTablxOffsetSize = 4;
     513             :         }
     514           1 :         else if (EQUAL(pszConfigurationKeyword, "MAX_FILE_SIZE_256TB"))
     515             :         {
     516           0 :             m_osConfigurationKeyword = "MAX_FILE_SIZE_256TB";
     517           0 :             nTablxOffsetSize = 6;
     518             :         }
     519           1 :         else if (EQUAL(pszConfigurationKeyword, "TEXT_UTF16"))
     520             :         {
     521           1 :             m_osConfigurationKeyword = "TEXT_UTF16";
     522           1 :             bTextUTF16 = true;
     523             :         }
     524           0 :         else if (!EQUAL(pszConfigurationKeyword, "DEFAULTS"))
     525             :         {
     526           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     527             :                      "Unsupported value for CONFIGURATION_KEYWORD: %s",
     528             :                      pszConfigurationKeyword);
     529           0 :             return false;
     530             :         }
     531             :     }
     532             : 
     533         268 :     m_osPath = '\\';
     534         268 :     if (pszFeatureDataset)
     535             :     {
     536           7 :         m_osPath += pszFeatureDataset;
     537           7 :         m_osPath += '\\';
     538             :     }
     539         268 :     m_osPath += m_osName;
     540             : 
     541             :     const char *pszDocumentation =
     542         268 :         m_aosCreationOptions.FetchNameValue("DOCUMENTATION");
     543         268 :     if (pszDocumentation)
     544           1 :         m_osDocumentation = pszDocumentation;
     545             : 
     546         268 :     const bool bGeomTypeHasZ = CPL_TO_BOOL(OGR_GT_HasZ(m_eGeomType));
     547         268 :     const bool bGeomTypeHasM = CPL_TO_BOOL(OGR_GT_HasM(m_eGeomType));
     548             : 
     549         268 :     m_poLyrTable = new FileGDBTable();
     550         268 :     if (!m_poLyrTable->Create(m_osGDBFilename.c_str(), nTablxOffsetSize,
     551             :                               eTableGeomType, bGeomTypeHasZ, bGeomTypeHasM))
     552             :     {
     553           0 :         Close();
     554           0 :         return false;
     555             :     }
     556         268 :     if (bTextUTF16)
     557           1 :         m_poLyrTable->SetTextUTF16();
     558             : 
     559             :     // To be able to test this unusual situation of having an attribute field
     560             :     // before the geometry field
     561         268 :     if (CPLTestBool(CPLGetConfigOption(
     562             :             "OPENFILEGDB_CREATE_FIELD_BEFORE_GEOMETRY", "NO")))
     563             :     {
     564           4 :         OGRFieldDefn oField("field_before_geom", OFTString);
     565           2 :         m_poLyrTable->CreateField(std::make_unique<FileGDBField>(
     566           4 :             oField.GetNameRef(), std::string(), FGFT_STRING,
     567           0 :             /* bNullable = */ true,
     568           0 :             /* bRequired = */ true,
     569           2 :             /* bEditable = */ true, 0, FileGDBField::UNSET_FIELD));
     570           2 :         m_poFeatureDefn->AddFieldDefn(&oField);
     571             :     }
     572             : 
     573         268 :     if (m_eGeomType != wkbNone && poSrcGeomFieldDefn)
     574             :     {
     575         195 :         const auto poSRS = poSrcGeomFieldDefn->GetSpatialRef();
     576             : 
     577             :         auto poGeomFieldDefn = std::make_unique<OGROpenFileGDBGeomFieldDefn>(
     578             :             this,
     579           0 :             m_aosCreationOptions.FetchNameValueDef("GEOMETRY_NAME", "SHAPE"),
     580         195 :             m_eGeomType);
     581         195 :         poGeomFieldDefn->SetNullable(
     582         195 :             CPLTestBool(m_aosCreationOptions.FetchNameValueDef(
     583             :                 "GEOMETRY_NULLABLE", "YES")));
     584             : 
     585         195 :         if (poSRS)
     586             :         {
     587          14 :             const char *const apszOptions[] = {
     588             :                 "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
     589          16 :             if (poFeatureDatasetSRS &&
     590          16 :                 !poSRS->IsSame(poFeatureDatasetSRS.get(), apszOptions))
     591             :             {
     592           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
     593             :                          "Layer CRS does not match feature dataset CRS");
     594           1 :                 return false;
     595             :             }
     596             : 
     597          13 :             auto poSRSClone = poSRS->Clone();
     598          13 :             poGeomFieldDefn->SetSpatialRef(poSRSClone);
     599          13 :             poSRSClone->Release();
     600             :         }
     601         181 :         else if (poFeatureDatasetSRS)
     602             :         {
     603           1 :             auto poSRSClone = poFeatureDatasetSRS->Clone();
     604           1 :             poGeomFieldDefn->SetSpatialRef(poSRSClone);
     605           1 :             poSRSClone->Release();
     606             :         }
     607             : 
     608         194 :         std::string osWKT;
     609         194 :         if (poSRS)
     610             :         {
     611          13 :             const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
     612             :             char *pszWKT;
     613          13 :             poSRS->exportToWkt(&pszWKT, apszOptions);
     614          13 :             osWKT = pszWKT;
     615          13 :             CPLFree(pszWKT);
     616             :         }
     617             :         else
     618             :         {
     619         181 :             osWKT = "{B286C06B-0879-11D2-AACA-00C04FA33C20}";
     620             :         }
     621             : 
     622             :         const auto oCoordPrec = GDBGridSettingsFromOGR(
     623         194 :             poSrcGeomFieldDefn, m_aosCreationOptions.List());
     624         194 :         poGeomFieldDefn->SetCoordinatePrecision(oCoordPrec);
     625             :         const auto &oGridsOptions =
     626         194 :             oCoordPrec.oFormatSpecificOptions.find("FileGeodatabase")->second;
     627             :         const double dfXOrigin =
     628         194 :             CPLAtof(oGridsOptions.FetchNameValue("XOrigin"));
     629             :         const double dfYOrigin =
     630         194 :             CPLAtof(oGridsOptions.FetchNameValue("YOrigin"));
     631             :         const double dfXYScale =
     632         194 :             CPLAtof(oGridsOptions.FetchNameValue("XYScale"));
     633             :         const double dfXYTolerance =
     634         194 :             CPLAtof(oGridsOptions.FetchNameValue("XYTolerance"));
     635             :         const double dfZOrigin =
     636         194 :             CPLAtof(oGridsOptions.FetchNameValue("ZOrigin"));
     637         194 :         const double dfZScale = CPLAtof(oGridsOptions.FetchNameValue("ZScale"));
     638             :         const double dfZTolerance =
     639         194 :             CPLAtof(oGridsOptions.FetchNameValue("ZTolerance"));
     640             :         const double dfMOrigin =
     641         194 :             CPLAtof(oGridsOptions.FetchNameValue("MOrigin"));
     642         194 :         const double dfMScale = CPLAtof(oGridsOptions.FetchNameValue("MScale"));
     643             :         const double dfMTolerance =
     644         194 :             CPLAtof(oGridsOptions.FetchNameValue("MTolerance"));
     645             : 
     646         194 :         if (!m_poDS->GetExistingSpatialRef(
     647             :                 osWKT, dfXOrigin, dfYOrigin, dfXYScale, dfZOrigin, dfZScale,
     648             :                 dfMOrigin, dfMScale, dfXYTolerance, dfZTolerance, dfMTolerance))
     649             :         {
     650         170 :             m_poDS->AddNewSpatialRef(osWKT, dfXOrigin, dfYOrigin, dfXYScale,
     651             :                                      dfZOrigin, dfZScale, dfMOrigin, dfMScale,
     652             :                                      dfXYTolerance, dfZTolerance, dfMTolerance);
     653             :         }
     654             : 
     655             :         // Will be patched later
     656         194 :         constexpr double dfSpatialGridResolution = 0;
     657             :         auto poTableGeomField = std::make_unique<FileGDBGeomField>(
     658         388 :             poGeomFieldDefn->GetNameRef(),
     659           0 :             std::string(),  // alias
     660         388 :             CPL_TO_BOOL(poGeomFieldDefn->IsNullable()), osWKT, dfXOrigin,
     661             :             dfYOrigin, dfXYScale, dfXYTolerance,
     662         582 :             std::vector<double>{dfSpatialGridResolution});
     663         194 :         poTableGeomField->SetZOriginScaleTolerance(dfZOrigin, dfZScale,
     664             :                                                    dfZTolerance);
     665         194 :         poTableGeomField->SetMOriginScaleTolerance(dfMOrigin, dfMScale,
     666             :                                                    dfMTolerance);
     667             : 
     668         194 :         if (!m_poLyrTable->CreateField(std::move(poTableGeomField)))
     669             :         {
     670           0 :             Close();
     671           0 :             return false;
     672             :         }
     673             : 
     674         194 :         m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
     675         194 :         m_poGeomConverter.reset(FileGDBOGRGeometryConverter::BuildConverter(
     676         194 :             m_poLyrTable->GetGeomField()));
     677             : 
     678         194 :         m_poFeatureDefn->AddGeomFieldDefn(std::move(poGeomFieldDefn));
     679             :     }
     680             : 
     681             :     const std::string osFIDName =
     682         534 :         m_aosCreationOptions.FetchNameValueDef("FID", "OBJECTID");
     683         267 :     if (!m_poLyrTable->CreateField(std::make_unique<FileGDBField>(
     684         267 :             osFIDName, std::string(), FGFT_OBJECTID,
     685           0 :             /* bNullable = */ false,
     686           0 :             /* bRequired = */ true,
     687         534 :             /* bEditable = */ false, 0, FileGDBField::UNSET_FIELD)))
     688             :     {
     689           0 :         Close();
     690           0 :         return false;
     691             :     }
     692             : 
     693             :     const bool bCreateShapeLength =
     694         352 :         (eTableGeomType == FGTGT_LINE || eTableGeomType == FGTGT_POLYGON) &&
     695          85 :         CPLTestBool(m_aosCreationOptions.FetchNameValueDef(
     696         267 :             "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS", "NO"));
     697             :     // Setting a non-default value doesn't work
     698         267 :     const char *pszLengthFieldName = m_aosCreationOptions.FetchNameValueDef(
     699             :         "LENGTH_FIELD_NAME", "Shape_Length");
     700             : 
     701             :     const bool bCreateShapeArea =
     702         314 :         eTableGeomType == FGTGT_POLYGON &&
     703          47 :         CPLTestBool(m_aosCreationOptions.FetchNameValueDef(
     704         267 :             "CREATE_SHAPE_AREA_AND_LENGTH_FIELDS", "NO"));
     705             :     // Setting a non-default value doesn't work
     706             :     const char *pszAreaFieldName =
     707         267 :         m_aosCreationOptions.FetchNameValueDef("AREA_FIELD_NAME", "Shape_Area");
     708             : 
     709         267 :     m_poFeatureDefn->Seal(/* bSealFields = */ true);
     710             : 
     711         267 :     if (bCreateShapeArea)
     712             :     {
     713           2 :         OGRFieldDefn oField(pszAreaFieldName, OFTReal);
     714           2 :         oField.SetDefault("FILEGEODATABASE_SHAPE_AREA");
     715           2 :         if (CreateField(&oField, false) != OGRERR_NONE)
     716             :         {
     717           0 :             Close();
     718           0 :             return false;
     719             :         }
     720             :     }
     721         267 :     if (bCreateShapeLength)
     722             :     {
     723           4 :         OGRFieldDefn oField(pszLengthFieldName, OFTReal);
     724           4 :         oField.SetDefault("FILEGEODATABASE_SHAPE_LENGTH");
     725           4 :         if (CreateField(&oField, false) != OGRERR_NONE)
     726             :         {
     727           0 :             Close();
     728           0 :             return false;
     729             :         }
     730             :     }
     731             : 
     732         267 :     m_poLyrTable->CreateIndex("FDO_OBJECTID", osFIDName);
     733             : 
     734             :     // Just to imitate the FileGDB SDK which register the index on the
     735             :     // geometry column after the OBJECTID one, but the OBJECTID column is the
     736             :     // first one in .gdbtable
     737         267 :     if (m_iGeomFieldIdx >= 0)
     738         388 :         m_poLyrTable->CreateIndex(
     739         194 :             "FDO_SHAPE", m_poFeatureDefn->GetGeomFieldDefn(0)->GetNameRef());
     740             : 
     741         267 :     if (!m_poDS->RegisterLayerInSystemCatalog(m_osName))
     742             :     {
     743           0 :         Close();
     744           0 :         return false;
     745             :     }
     746             : 
     747         270 :     if (pszFeatureDataset != nullptr && m_osFeatureDatasetGUID.empty() &&
     748           3 :         !CreateFeatureDataset(pszFeatureDataset))
     749             :     {
     750           0 :         Close();
     751           0 :         return false;
     752             :     }
     753             : 
     754         267 :     RefreshXMLDefinitionInMemory();
     755             : 
     756         267 :     return true;
     757             : }
     758             : 
     759             : /************************************************************************/
     760             : /*                       CreateXMLFieldDefinition()                     */
     761             : /************************************************************************/
     762             : 
     763         889 : static CPLXMLNode *CreateXMLFieldDefinition(const OGRFieldDefn *poFieldDefn,
     764             :                                             const FileGDBField *poGDBFieldDefn,
     765             :                                             bool bArcGISPro32OrLater)
     766             : {
     767             :     auto GPFieldInfoEx =
     768         889 :         CPLCreateXMLNode(nullptr, CXT_Element, "GPFieldInfoEx");
     769         889 :     CPLAddXMLAttributeAndValue(GPFieldInfoEx, "xsi:type",
     770             :                                "typens:GPFieldInfoEx");
     771         889 :     CPLCreateXMLElementAndValue(GPFieldInfoEx, "Name",
     772         889 :                                 poGDBFieldDefn->GetName().c_str());
     773         889 :     if (!poGDBFieldDefn->GetAlias().empty())
     774             :     {
     775          18 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "AliasName",
     776          18 :                                     poGDBFieldDefn->GetAlias().c_str());
     777             :     }
     778         889 :     const auto *psDefault = poGDBFieldDefn->GetDefault();
     779         889 :     if (!OGR_RawField_IsNull(psDefault) && !OGR_RawField_IsUnset(psDefault))
     780             :     {
     781         225 :         if (poGDBFieldDefn->GetType() == FGFT_STRING)
     782             :         {
     783         122 :             auto psDefaultValue = CPLCreateXMLElementAndValue(
     784          61 :                 GPFieldInfoEx, "DefaultValueString", psDefault->String);
     785          61 :             if (!bArcGISPro32OrLater)
     786             :             {
     787          61 :                 CPLAddXMLAttributeAndValue(
     788             :                     psDefaultValue, "xmlns:typens",
     789             :                     "http://www.esri.com/schemas/ArcGIS/10.3");
     790             :             }
     791             :         }
     792         164 :         else if (poGDBFieldDefn->GetType() == FGFT_INT32)
     793             :         {
     794          44 :             auto psDefaultValue = CPLCreateXMLElementAndValue(
     795             :                 GPFieldInfoEx, "DefaultValue",
     796          44 :                 CPLSPrintf("%d", psDefault->Integer));
     797          44 :             CPLAddXMLAttributeAndValue(psDefaultValue, "xsi:type", "xs:int");
     798             :         }
     799         120 :         else if (poGDBFieldDefn->GetType() == FGFT_FLOAT64)
     800             :         {
     801          27 :             auto psDefaultValue = CPLCreateXMLElementAndValue(
     802             :                 GPFieldInfoEx, "DefaultValueNumeric",
     803          27 :                 CPLSPrintf("%.17g", psDefault->Real));
     804          27 :             if (!bArcGISPro32OrLater)
     805             :             {
     806          27 :                 CPLAddXMLAttributeAndValue(
     807             :                     psDefaultValue, "xmlns:typens",
     808             :                     "http://www.esri.com/schemas/ArcGIS/10.3");
     809             :             }
     810             :         }
     811          93 :         else if (poGDBFieldDefn->GetType() == FGFT_INT64)
     812             :         {
     813           1 :             CPLCreateXMLElementAndValue(
     814             :                 GPFieldInfoEx, "DefaultValueInteger",
     815           1 :                 CPLSPrintf(CPL_FRMT_GIB, psDefault->Integer64));
     816             :         }
     817         172 :         else if (poGDBFieldDefn->GetType() == FGFT_DATETIME ||
     818          80 :                  poGDBFieldDefn->GetType() == FGFT_DATE)
     819             :         {
     820          18 :             CPLCreateXMLElementAndValue(
     821             :                 GPFieldInfoEx, "DefaultValueNumeric",
     822             :                 CPLSPrintf("%.17g", FileGDBOGRDateToDoubleDate(
     823             :                                         psDefault, /* bConvertToUTC = */ true,
     824          18 :                                         poGDBFieldDefn->IsHighPrecision())));
     825             :         }
     826          74 :         else if (poGDBFieldDefn->GetType() == FGFT_TIME)
     827             :         {
     828           4 :             CPLCreateXMLElementAndValue(
     829             :                 GPFieldInfoEx, "DefaultValueNumeric",
     830             :                 CPLSPrintf("%.0f", FileGDBOGRTimeToDoubleTime(psDefault)));
     831             :         }
     832          70 :         else if (poGDBFieldDefn->GetType() == FGFT_DATETIME_WITH_OFFSET)
     833             :         {
     834             :             /*
     835             :              <DefaultValueTimestampOffset xsi:type="typens:TimestampOffset">
     836             :                 <Timestamp>2023-02-01T04:05:06</Timestamp>
     837             :                 <HoursOffset>6</HoursOffset>
     838             :                 <MinutesOffset>0</MinutesOffset>
     839             :               </DefaultValueTimestampOffset>
     840             :             */
     841          10 :             auto psDefaultValue = CPLCreateXMLNode(
     842             :                 GPFieldInfoEx, CXT_Element, "DefaultValueTimestampOffset");
     843          10 :             CPLAddXMLAttributeAndValue(psDefaultValue, "xsi:type",
     844             :                                        "typens:TimestampOffset");
     845          10 :             CPLCreateXMLElementAndValue(
     846             :                 psDefaultValue, "Timestamp",
     847             :                 CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
     848          10 :                            psDefault->Date.Year, psDefault->Date.Month,
     849          10 :                            psDefault->Date.Day, psDefault->Date.Hour,
     850          10 :                            psDefault->Date.Minute,
     851          10 :                            static_cast<int>(psDefault->Date.Second)));
     852          10 :             if (psDefault->Date.TZFlag > 1)
     853             :             {
     854           2 :                 const int nOffsetInMin = (psDefault->Date.TZFlag - 100) * 15;
     855           2 :                 CPLCreateXMLElementAndValue(
     856             :                     psDefaultValue, "HoursOffset",
     857             :                     CPLSPrintf("%d", nOffsetInMin / 60));
     858           2 :                 CPLCreateXMLElementAndValue(
     859             :                     psDefaultValue, "MinutesOffset",
     860           2 :                     CPLSPrintf("%d", std::abs(nOffsetInMin) % 60));
     861             :             }
     862             :         }
     863             :     }
     864         889 :     const char *pszFieldType = "";
     865         889 :     int nLength = 0;
     866         889 :     switch (poGDBFieldDefn->GetType())
     867             :     {
     868           0 :         case FGFT_UNDEFINED:
     869           0 :             CPLAssert(false);
     870             :             break;
     871          59 :         case FGFT_INT16:
     872          59 :             nLength = 2;
     873          59 :             pszFieldType = "esriFieldTypeSmallInteger";
     874          59 :             break;
     875         215 :         case FGFT_INT32:
     876         215 :             nLength = 4;
     877         215 :             pszFieldType = "esriFieldTypeInteger";
     878         215 :             break;
     879          38 :         case FGFT_FLOAT32:
     880          38 :             nLength = 4;
     881          38 :             pszFieldType = "esriFieldTypeSingle";
     882          38 :             break;
     883         108 :         case FGFT_FLOAT64:
     884         108 :             nLength = 8;
     885         108 :             pszFieldType = "esriFieldTypeDouble";
     886         108 :             break;
     887         366 :         case FGFT_STRING:
     888         366 :             nLength = poGDBFieldDefn->GetMaxWidth();
     889         366 :             pszFieldType = "esriFieldTypeString";
     890         366 :             break;
     891          68 :         case FGFT_DATETIME:
     892          68 :             nLength = 8;
     893          68 :             pszFieldType = "esriFieldTypeDate";
     894          68 :             break;
     895           0 :         case FGFT_OBJECTID:
     896           0 :             pszFieldType = "esriFieldTypeOID";
     897           0 :             break;  // shouldn't happen
     898           0 :         case FGFT_GEOMETRY:
     899           0 :             pszFieldType = "esriFieldTypeGeometry";
     900           0 :             break;  // shouldn't happen
     901           5 :         case FGFT_BINARY:
     902           5 :             pszFieldType = "esriFieldTypeBlob";
     903           5 :             break;
     904           0 :         case FGFT_RASTER:
     905           0 :             pszFieldType = "esriFieldTypeRaster";
     906           0 :             break;
     907           2 :         case FGFT_GUID:
     908           2 :             pszFieldType = "esriFieldTypeGUID";
     909           2 :             break;
     910           3 :         case FGFT_GLOBALID:
     911           3 :             pszFieldType = "esriFieldTypeGlobalID";
     912           3 :             break;
     913           4 :         case FGFT_XML:
     914           4 :             pszFieldType = "esriFieldTypeXML";
     915           4 :             break;
     916           1 :         case FGFT_INT64:
     917           1 :             nLength = 8;
     918           1 :             pszFieldType = "esriFieldTypeBigInteger";
     919           1 :             break;
     920           6 :         case FGFT_DATE:
     921           6 :             nLength = 8;
     922           6 :             pszFieldType = "esriFieldTypeDateOnly";
     923           6 :             break;
     924           4 :         case FGFT_TIME:
     925           4 :             nLength = 8;
     926           4 :             pszFieldType = "esriFieldTypeTimeOnly";
     927           4 :             break;
     928          10 :         case FGFT_DATETIME_WITH_OFFSET:
     929          10 :             nLength = 8 + 2;
     930          10 :             pszFieldType = "esriFieldTypeTimestampOffset";
     931          10 :             break;
     932             :     }
     933             :     auto psFieldType =
     934         889 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "FieldType", pszFieldType);
     935         889 :     if (!bArcGISPro32OrLater)
     936             :     {
     937         868 :         CPLAddXMLAttributeAndValue(psFieldType, "xmlns:typens",
     938             :                                    "http://www.esri.com/schemas/ArcGIS/10.3");
     939             :     }
     940         889 :     if (poGDBFieldDefn->IsNullable())
     941             :     {
     942         700 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "IsNullable", "true");
     943             :     }
     944         889 :     if (poGDBFieldDefn->IsRequired())
     945             :     {
     946          33 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "Required", "true");
     947             :     }
     948         889 :     if (!poGDBFieldDefn->IsEditable())
     949             :     {
     950          29 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "Editable", "false");
     951             :     }
     952         889 :     if (poGDBFieldDefn->IsHighPrecision())
     953             :     {
     954           0 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "HighPrecision", "true");
     955             :     }
     956         889 :     CPLCreateXMLElementAndValue(GPFieldInfoEx, "Length",
     957             :                                 CPLSPrintf("%d", nLength));
     958         889 :     CPLCreateXMLElementAndValue(GPFieldInfoEx, "Precision", "0");
     959         889 :     CPLCreateXMLElementAndValue(GPFieldInfoEx, "Scale", "0");
     960         889 :     if (!poFieldDefn->GetDomainName().empty())
     961             :     {
     962          10 :         CPLCreateXMLElementAndValue(GPFieldInfoEx, "DomainName",
     963          10 :                                     poFieldDefn->GetDomainName().c_str());
     964             :     }
     965         889 :     return GPFieldInfoEx;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                            GetDefault()                              */
     970             : /************************************************************************/
     971             : 
     972         345 : static bool GetDefault(const OGRFieldDefn *poField, FileGDBFieldType eType,
     973             :                        OGRField &sDefault, std::string &osDefaultVal,
     974             :                        bool bApproxOK)
     975             : {
     976         345 :     sDefault = FileGDBField::UNSET_FIELD;
     977         345 :     const char *pszDefault = poField->GetDefault();
     978         345 :     if (pszDefault != nullptr && !poField->IsDefaultDriverSpecific())
     979             :     {
     980          78 :         if (eType == FGFT_STRING)
     981             :         {
     982          16 :             osDefaultVal = pszDefault;
     983          16 :             if (osDefaultVal[0] == '\'' && osDefaultVal.back() == '\'')
     984             :             {
     985          16 :                 osDefaultVal = osDefaultVal.substr(1);
     986          16 :                 osDefaultVal.pop_back();
     987             :                 char *pszTmp =
     988          16 :                     CPLUnescapeString(osDefaultVal.c_str(), nullptr, CPLES_SQL);
     989          16 :                 osDefaultVal = pszTmp;
     990          16 :                 CPLFree(pszTmp);
     991             :             }
     992          16 :             sDefault.String = &osDefaultVal[0];
     993             :         }
     994          62 :         else if (eType == FGFT_INT16 || eType == FGFT_INT32)
     995          15 :             sDefault.Integer = atoi(pszDefault);
     996          47 :         else if (eType == FGFT_FLOAT32 || eType == FGFT_FLOAT64)
     997          14 :             sDefault.Real = CPLAtof(pszDefault);
     998          33 :         else if (eType == FGFT_DATETIME || eType == FGFT_DATE ||
     999           5 :                  eType == FGFT_TIME || eType == FGFT_DATETIME_WITH_OFFSET)
    1000             :         {
    1001          32 :             osDefaultVal = pszDefault;
    1002          52 :             if (osDefaultVal == "CURRENT_TIMESTAMP" ||
    1003          52 :                 osDefaultVal == "CURRENT_TIME" ||
    1004          20 :                 osDefaultVal == "CURRENT_DATE")
    1005             :             {
    1006          12 :                 CPLError(bApproxOK ? CE_Warning : CE_Failure, CPLE_AppDefined,
    1007             :                          "%s is not supported as a default value in File "
    1008             :                          "Geodatabase",
    1009             :                          osDefaultVal.c_str());
    1010          12 :                 return bApproxOK;
    1011             :             }
    1012          20 :             if (osDefaultVal[0] == '\'' && osDefaultVal.back() == '\'')
    1013             :             {
    1014          20 :                 osDefaultVal = osDefaultVal.substr(1);
    1015          20 :                 osDefaultVal.pop_back();
    1016             :                 char *pszTmp =
    1017          20 :                     CPLUnescapeString(osDefaultVal.c_str(), nullptr, CPLES_SQL);
    1018          20 :                 osDefaultVal = pszTmp;
    1019          20 :                 CPLFree(pszTmp);
    1020             :             }
    1021          20 :             if (!OGRParseDate(osDefaultVal.c_str(), &sDefault, 0))
    1022             :             {
    1023           6 :                 CPLError(bApproxOK ? CE_Warning : CE_Failure, CPLE_AppDefined,
    1024             :                          "Cannot parse %s as a date time",
    1025             :                          osDefaultVal.c_str());
    1026           6 :                 return bApproxOK;
    1027          14 :             }
    1028             :         }
    1029           1 :         else if (eType == FGFT_INT64)
    1030           1 :             sDefault.Integer64 = CPLAtoGIntBig(pszDefault);
    1031             :     }
    1032         327 :     return true;
    1033             : }
    1034             : 
    1035             : /************************************************************************/
    1036             : /*                         GetGDBFieldType()                            */
    1037             : /************************************************************************/
    1038             : 
    1039         339 : static FileGDBFieldType GetGDBFieldType(const OGRFieldDefn *poField,
    1040             :                                         bool bArcGISPro32OrLater)
    1041             : {
    1042         339 :     FileGDBFieldType eType = FGFT_UNDEFINED;
    1043         339 :     switch (poField->GetType())
    1044             :     {
    1045         112 :         case OFTInteger:
    1046         112 :             eType =
    1047         112 :                 poField->GetSubType() == OFSTInt16 ? FGFT_INT16 : FGFT_INT32;
    1048         112 :             break;
    1049          41 :         case OFTReal:
    1050          41 :             eType = poField->GetSubType() == OFSTFloat32 ? FGFT_FLOAT32
    1051             :                                                          : FGFT_FLOAT64;
    1052          41 :             break;
    1053           3 :         case OFTInteger64:
    1054           3 :             eType = bArcGISPro32OrLater ? FGFT_INT64 : FGFT_FLOAT64;
    1055           3 :             break;
    1056         118 :         case OFTString:
    1057             :         case OFTWideString:
    1058             :         case OFTStringList:
    1059             :         case OFTWideStringList:
    1060             :         case OFTIntegerList:
    1061             :         case OFTInteger64List:
    1062             :         case OFTRealList:
    1063         118 :             eType = FGFT_STRING;
    1064         118 :             break;
    1065           2 :         case OFTBinary:
    1066           2 :             eType = FGFT_BINARY;
    1067           2 :             break;
    1068          16 :         case OFTDate:
    1069          16 :             eType = bArcGISPro32OrLater ? FGFT_DATE : FGFT_DATETIME;
    1070          16 :             break;
    1071           2 :         case OFTTime:
    1072           2 :             eType = bArcGISPro32OrLater ? FGFT_TIME : FGFT_DATETIME;
    1073           2 :             break;
    1074          45 :         case OFTDateTime:
    1075          45 :             eType =
    1076          45 :                 bArcGISPro32OrLater ? FGFT_DATETIME_WITH_OFFSET : FGFT_DATETIME;
    1077          45 :             break;
    1078             :     }
    1079         339 :     return eType;
    1080             : }
    1081             : 
    1082             : /************************************************************************/
    1083             : /*                        GetGPFieldInfoExsNode()                       */
    1084             : /************************************************************************/
    1085             : 
    1086          31 : static CPLXMLNode *GetGPFieldInfoExsNode(CPLXMLNode *psParent)
    1087             : {
    1088          31 :     CPLXMLNode *psInfo = CPLSearchXMLNode(psParent, "=DEFeatureClassInfo");
    1089          31 :     if (psInfo == nullptr)
    1090          31 :         psInfo = CPLSearchXMLNode(psParent, "=typens:DEFeatureClassInfo");
    1091          31 :     if (psInfo == nullptr)
    1092           4 :         psInfo = CPLSearchXMLNode(psParent, "=DETableInfo");
    1093          31 :     if (psInfo == nullptr)
    1094           4 :         psInfo = CPLSearchXMLNode(psParent, "=typens:DETableInfo");
    1095          31 :     if (psInfo != nullptr)
    1096             :     {
    1097          31 :         return CPLGetXMLNode(psInfo, "GPFieldInfoExs");
    1098             :     }
    1099           0 :     return nullptr;
    1100             : }
    1101             : 
    1102             : /************************************************************************/
    1103             : /*                      GetLaunderedFieldName()                         */
    1104             : /************************************************************************/
    1105             : 
    1106             : std::string
    1107         345 : OGROpenFileGDBLayer::GetLaunderedFieldName(const std::string &osNameOri) const
    1108             : {
    1109         690 :     std::wstring osName = LaunderName(StringToWString(osNameOri));
    1110         345 :     osName = EscapeReservedKeywords(osName);
    1111             : 
    1112             :     /* Truncate to 64 characters */
    1113         345 :     constexpr size_t FIELD_NAME_MAX_SIZE = 64;
    1114         345 :     if (osName.size() > FIELD_NAME_MAX_SIZE)
    1115           2 :         osName.resize(FIELD_NAME_MAX_SIZE);
    1116             : 
    1117             :     /* Ensures uniqueness of field name */
    1118         345 :     int numRenames = 1;
    1119         698 :     while ((m_poFeatureDefn->GetFieldIndex(WStringToString(osName).c_str()) >=
    1120        1047 :             0) &&
    1121             :            (numRenames < 10))
    1122             :     {
    1123          12 :         osName = StringToWString(CPLSPrintf(
    1124             :             "%s_%d",
    1125           8 :             WStringToString(osName.substr(0, FIELD_NAME_MAX_SIZE - 2)).c_str(),
    1126           4 :             numRenames));
    1127           4 :         numRenames++;
    1128             :     }
    1129         690 :     while ((m_poFeatureDefn->GetFieldIndex(WStringToString(osName).c_str()) >=
    1130        1035 :             0) &&
    1131             :            (numRenames < 100))
    1132             :     {
    1133           0 :         osName = StringToWString(CPLSPrintf(
    1134             :             "%s_%d",
    1135           0 :             WStringToString(osName.substr(0, FIELD_NAME_MAX_SIZE - 3)).c_str(),
    1136           0 :             numRenames));
    1137           0 :         numRenames++;
    1138             :     }
    1139             : 
    1140         690 :     return WStringToString(osName);
    1141             : }
    1142             : 
    1143             : /************************************************************************/
    1144             : /*                            CreateField()                             */
    1145             : /************************************************************************/
    1146             : 
    1147         341 : OGRErr OGROpenFileGDBLayer::CreateField(const OGRFieldDefn *poFieldIn,
    1148             :                                         int bApproxOK)
    1149             : {
    1150         341 :     if (!m_bEditable)
    1151           0 :         return OGRERR_FAILURE;
    1152             : 
    1153         341 :     if (!BuildLayerDefinition())
    1154           0 :         return OGRERR_FAILURE;
    1155             : 
    1156         344 :     if (m_poDS->IsInTransaction() &&
    1157           3 :         ((!m_bHasCreatedBackupForTransaction && !BeginEmulatedTransaction()) ||
    1158           3 :          !m_poDS->BackupSystemTablesForTransaction()))
    1159             :     {
    1160           0 :         return OGRERR_FAILURE;
    1161             :     }
    1162             : 
    1163             :     /* Clean field names */
    1164         682 :     OGRFieldDefn oField(poFieldIn);
    1165         341 :     OGRFieldDefn *poField = &oField;
    1166             : 
    1167         341 :     if (poField->GetType() == OFTInteger64 && !m_bArcGISPro32OrLater)
    1168             :     {
    1169           3 :         CPLError(CE_Warning, CPLE_AppDefined,
    1170             :                  "Field %s of type Integer64 will be written as a Float64. "
    1171             :                  "To get Integer64, use layer creation option "
    1172             :                  "TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER",
    1173             :                  poField->GetNameRef());
    1174             :     }
    1175         338 :     else if (poField->GetType() == OFTDate && !m_bArcGISPro32OrLater)
    1176             :     {
    1177          14 :         CPLError(CE_Warning, CPLE_AppDefined,
    1178             :                  "Field %s of type Date will be written as a DateTime. "
    1179             :                  "To get DateTime, use layer creation option "
    1180             :                  "TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER",
    1181             :                  poField->GetNameRef());
    1182             :     }
    1183         324 :     else if (poField->GetType() == OFTTime && !m_bArcGISPro32OrLater)
    1184             :     {
    1185           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1186             :                  "Field %s of type Time will be written as a DateTime. "
    1187             :                  "To get DateTime, use layer creation option "
    1188             :                  "TARGET_ARCGIS_VERSION=ARCGIS_PRO_3_2_OR_LATER",
    1189             :                  poField->GetNameRef());
    1190             :     }
    1191             : 
    1192         682 :     const std::string osFidColumn = GetFIDColumn();
    1193         682 :     if (!osFidColumn.empty() &&
    1194         341 :         EQUAL(poField->GetNameRef(), osFidColumn.c_str()))
    1195             :     {
    1196           5 :         if (poField->GetType() != OFTInteger &&
    1197           6 :             poField->GetType() != OFTInteger64 &&
    1198             :             // typically a GeoPackage exported with QGIS as a shapefile and
    1199             :             // re-imported See https://github.com/qgis/QGIS/pull/43118
    1200           3 :             !(poField->GetType() == OFTReal && poField->GetWidth() <= 20 &&
    1201           1 :               poField->GetPrecision() == 0))
    1202             :         {
    1203           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1204             :                      "Wrong field type for %s : %d", poField->GetNameRef(),
    1205           0 :                      poField->GetType());
    1206           0 :             return OGRERR_FAILURE;
    1207             :         }
    1208             : 
    1209           3 :         m_iFIDAsRegularColumnIndex = m_poFeatureDefn->GetFieldCount();
    1210           3 :         whileUnsealing(m_poFeatureDefn)->AddFieldDefn(poField);
    1211           3 :         return OGRERR_NONE;
    1212             :     }
    1213             : 
    1214         676 :     const std::string osFieldNameOri(poField->GetNameRef());
    1215         676 :     const std::string osFieldName = GetLaunderedFieldName(osFieldNameOri);
    1216         338 :     if (osFieldName != osFieldNameOri)
    1217             :     {
    1218          10 :         if (!bApproxOK ||
    1219           5 :             (m_poFeatureDefn->GetFieldIndex(osFieldName.c_str()) >= 0))
    1220             :         {
    1221           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1222             :                      "Failed to add field named '%s'", osFieldNameOri.c_str());
    1223           0 :             return OGRERR_FAILURE;
    1224             :         }
    1225           5 :         CPLError(CE_Warning, CPLE_NotSupported,
    1226             :                  "Normalized/laundered field name: '%s' to '%s'",
    1227             :                  osFieldNameOri.c_str(), osFieldName.c_str());
    1228             : 
    1229           5 :         poField->SetName(osFieldName.c_str());
    1230             :     }
    1231             : 
    1232             :     const char *pszColumnTypes =
    1233         338 :         m_aosCreationOptions.FetchNameValue("COLUMN_TYPES");
    1234         676 :     std::string gdbFieldType;
    1235         338 :     if (pszColumnTypes != nullptr)
    1236             :     {
    1237          28 :         char **papszTokens = CSLTokenizeString2(pszColumnTypes, ",", 0);
    1238             :         const char *pszFieldType =
    1239          28 :             CSLFetchNameValue(papszTokens, poField->GetNameRef());
    1240          28 :         if (pszFieldType != nullptr)
    1241             :         {
    1242             :             OGRFieldType fldtypeCheck;
    1243             :             OGRFieldSubType eSubType;
    1244           6 :             if (GDBToOGRFieldType(pszFieldType, &fldtypeCheck, &eSubType))
    1245             :             {
    1246           6 :                 if (fldtypeCheck != poField->GetType())
    1247             :                 {
    1248           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1249             :                              "Ignoring COLUMN_TYPES=%s=%s : %s not consistent "
    1250             :                              "with OGR data type",
    1251             :                              poField->GetNameRef(), pszFieldType, pszFieldType);
    1252             :                 }
    1253             :                 else
    1254           6 :                     gdbFieldType = pszFieldType;
    1255             :             }
    1256             :             else
    1257           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1258             :                          "Ignoring COLUMN_TYPES=%s=%s : %s not recognized",
    1259             :                          poField->GetNameRef(), pszFieldType, pszFieldType);
    1260             :         }
    1261          28 :         CSLDestroy(papszTokens);
    1262             :     }
    1263             : 
    1264         338 :     FileGDBFieldType eType = FGFT_UNDEFINED;
    1265         338 :     if (!gdbFieldType.empty())
    1266             :     {
    1267           6 :         if (gdbFieldType == "esriFieldTypeSmallInteger")
    1268           0 :             eType = FGFT_INT16;
    1269           6 :         else if (gdbFieldType == "esriFieldTypeInteger")
    1270           0 :             eType = FGFT_INT32;
    1271           6 :         else if (gdbFieldType == "esriFieldTypeBigInteger")
    1272           0 :             eType = FGFT_INT64;
    1273           6 :         else if (gdbFieldType == "esriFieldTypeSingle")
    1274           0 :             eType = FGFT_FLOAT32;
    1275           6 :         else if (gdbFieldType == "esriFieldTypeDouble")
    1276           0 :             eType = FGFT_FLOAT64;
    1277           6 :         else if (gdbFieldType == "esriFieldTypeString")
    1278           0 :             eType = FGFT_STRING;
    1279           6 :         else if (gdbFieldType == "esriFieldTypeDate")
    1280           0 :             eType = FGFT_DATETIME;
    1281           6 :         else if (gdbFieldType == "esriFieldTypeBlob")
    1282           0 :             eType = FGFT_BINARY;
    1283           6 :         else if (gdbFieldType == "esriFieldTypeGUID")
    1284           2 :             eType = FGFT_GUID;
    1285           4 :         else if (gdbFieldType == "esriFieldTypeGlobalID")
    1286           2 :             eType = FGFT_GLOBALID;
    1287           2 :         else if (gdbFieldType == "esriFieldTypeXML")
    1288           2 :             eType = FGFT_XML;
    1289           0 :         else if (gdbFieldType == "esriFieldTypeDateOnly")
    1290           0 :             eType = FGFT_DATE;
    1291           0 :         else if (gdbFieldType == "esriFieldTypeTimeOnly")
    1292           0 :             eType = FGFT_TIME;
    1293           0 :         else if (gdbFieldType == "esriFieldTypeTimestampOffset")
    1294           0 :             eType = FGFT_DATETIME_WITH_OFFSET;
    1295             :         else
    1296             :         {
    1297           0 :             CPLAssert(false);
    1298             :         }
    1299             :     }
    1300             :     else
    1301             :     {
    1302         332 :         eType = GetGDBFieldType(poField, m_bArcGISPro32OrLater);
    1303             :     }
    1304             : 
    1305         338 :     int nWidth = 0;
    1306         338 :     if (eType == FGFT_GLOBALID || eType == FGFT_GUID)
    1307             :     {
    1308           4 :         nWidth = 38;
    1309             :     }
    1310         334 :     else if (poField->GetType() == OFTString)
    1311             :     {
    1312         117 :         nWidth = poField->GetWidth();
    1313         117 :         if (nWidth == 0)
    1314             :         {
    1315             :             // Hard-coded non-zero default string width if the user doesn't
    1316             :             // override it with the below configuration option.
    1317             :             // See comment at declaration of DEFAULT_STRING_WIDTH for more
    1318             :             // details
    1319         111 :             nWidth = DEFAULT_STRING_WIDTH;
    1320         111 :             if (const char *pszVal = CPLGetConfigOption(
    1321             :                     "OPENFILEGDB_DEFAULT_STRING_WIDTH", nullptr))
    1322             :             {
    1323           2 :                 const int nVal = atoi(pszVal);
    1324           2 :                 if (nVal >= 0)
    1325           2 :                     nWidth = nVal;
    1326             :             }
    1327             :             // Advertise a non-zero user-modified width back to the created
    1328             :             // OGRFieldDefn, only if it is less than the hard-coded default
    1329             :             // value (this will avoid potential issues with excessively large
    1330             :             // field width afterwards)
    1331         111 :             if (nWidth < DEFAULT_STRING_WIDTH)
    1332           2 :                 poField->SetWidth(nWidth);
    1333             :         }
    1334             :     }
    1335             : 
    1336         338 :     OGRField sDefault = FileGDBField::UNSET_FIELD;
    1337         676 :     std::string osDefaultVal;
    1338         338 :     if (!GetDefault(poField, eType, sDefault, osDefaultVal,
    1339         338 :                     CPL_TO_BOOL(bApproxOK)))
    1340          12 :         return OGRERR_FAILURE;
    1341             : 
    1342         331 :     if (!poField->GetDomainName().empty() &&
    1343           5 :         (!m_osThisGUID.empty() ||
    1344         326 :          m_poDS->FindUUIDFromName(GetName(), m_osThisGUID)))
    1345             :     {
    1346           5 :         if (!m_poDS->LinkDomainToTable(poField->GetDomainName(), m_osThisGUID))
    1347             :         {
    1348           0 :             poField->SetDomainName(std::string());
    1349             :         }
    1350             :     }
    1351             : 
    1352             :     const bool bNullable =
    1353         326 :         CPL_TO_BOOL(poField->IsNullable()) && eType != FGFT_GLOBALID;
    1354         326 :     bool bRequired = (eType == FGFT_GLOBALID);
    1355         326 :     bool bEditable = (eType != FGFT_GLOBALID);
    1356             : 
    1357         326 :     if (poField->GetType() == OFTReal)
    1358             :     {
    1359          39 :         const char *pszDefault = poField->GetDefault();
    1360          39 :         if (pszDefault && EQUAL(pszDefault, "FILEGEODATABASE_SHAPE_AREA"))
    1361             :         {
    1362           2 :             m_iAreaField = m_poFeatureDefn->GetFieldCount();
    1363           2 :             bRequired = true;
    1364           2 :             bEditable = false;
    1365             :         }
    1366          37 :         else if (pszDefault &&
    1367          18 :                  EQUAL(pszDefault, "FILEGEODATABASE_SHAPE_LENGTH"))
    1368             :         {
    1369           4 :             m_iLengthField = m_poFeatureDefn->GetFieldCount();
    1370           4 :             bRequired = true;
    1371           4 :             bEditable = false;
    1372             :         }
    1373             :     }
    1374             : 
    1375         326 :     const char *pszAlias = poField->GetAlternativeNameRef();
    1376         326 :     if (!m_poLyrTable->CreateField(std::make_unique<FileGDBField>(
    1377         326 :             poField->GetNameRef(),
    1378         652 :             pszAlias ? std::string(pszAlias) : std::string(), eType, bNullable,
    1379             :             bRequired, bEditable, nWidth, sDefault)))
    1380             :     {
    1381           9 :         return OGRERR_FAILURE;
    1382             :     }
    1383             : 
    1384         317 :     whileUnsealing(m_poFeatureDefn)->AddFieldDefn(poField);
    1385             : 
    1386         317 :     if (m_bRegisteredTable)
    1387             :     {
    1388             :         // If the table is already registered (that is updating an existing
    1389             :         // layer), patch the XML definition to add the new field
    1390          28 :         CPLXMLTreeCloser oTree(CPLParseXMLString(m_osDefinition.c_str()));
    1391          14 :         if (oTree)
    1392             :         {
    1393          14 :             CPLXMLNode *psGPFieldInfoExs = GetGPFieldInfoExsNode(oTree.get());
    1394          14 :             if (psGPFieldInfoExs)
    1395             :             {
    1396          14 :                 CPLAddXMLChild(psGPFieldInfoExs,
    1397             :                                CreateXMLFieldDefinition(
    1398             :                                    poField,
    1399          14 :                                    m_poLyrTable->GetField(
    1400          14 :                                        m_poLyrTable->GetFieldCount() - 1),
    1401          14 :                                    m_bArcGISPro32OrLater));
    1402             : 
    1403          14 :                 char *pszDefinition = CPLSerializeXMLTree(oTree.get());
    1404          14 :                 m_osDefinition = pszDefinition;
    1405          14 :                 CPLFree(pszDefinition);
    1406             : 
    1407          14 :                 m_poDS->UpdateXMLDefinition(m_osName.c_str(),
    1408             :                                             m_osDefinition.c_str());
    1409             :             }
    1410             :         }
    1411             :     }
    1412             :     else
    1413             :     {
    1414         303 :         RefreshXMLDefinitionInMemory();
    1415             :     }
    1416             : 
    1417         317 :     return OGRERR_NONE;
    1418             : }
    1419             : 
    1420             : /************************************************************************/
    1421             : /*                           AlterFieldDefn()                           */
    1422             : /************************************************************************/
    1423             : 
    1424          16 : OGRErr OGROpenFileGDBLayer::AlterFieldDefn(int iFieldToAlter,
    1425             :                                            OGRFieldDefn *poNewFieldDefn,
    1426             :                                            int nFlagsIn)
    1427             : {
    1428          16 :     if (!m_bEditable)
    1429           0 :         return OGRERR_FAILURE;
    1430             : 
    1431          16 :     if (!BuildLayerDefinition())
    1432           0 :         return OGRERR_FAILURE;
    1433             : 
    1434          16 :     if (m_poDS->IsInTransaction() &&
    1435           0 :         ((!m_bHasCreatedBackupForTransaction && !BeginEmulatedTransaction()) ||
    1436           0 :          !m_poDS->BackupSystemTablesForTransaction()))
    1437             :     {
    1438           0 :         return OGRERR_FAILURE;
    1439             :     }
    1440             : 
    1441          16 :     if (iFieldToAlter < 0 || iFieldToAlter >= m_poFeatureDefn->GetFieldCount())
    1442             :     {
    1443           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1444           2 :         return OGRERR_FAILURE;
    1445             :     }
    1446             : 
    1447          14 :     if (iFieldToAlter == m_iFIDAsRegularColumnIndex)
    1448             :     {
    1449           3 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot alter field %s",
    1450             :                  GetFIDColumn());
    1451           3 :         return OGRERR_FAILURE;
    1452             :     }
    1453             : 
    1454          22 :     const int nGDBIdx = m_poLyrTable->GetFieldIdx(
    1455          11 :         m_poFeatureDefn->GetFieldDefn(iFieldToAlter)->GetNameRef());
    1456          11 :     if (nGDBIdx < 0)
    1457           0 :         return OGRERR_FAILURE;
    1458             : 
    1459          11 :     OGRFieldDefn *poFieldDefn = m_poFeatureDefn->GetFieldDefn(iFieldToAlter);
    1460          22 :     auto oTemporaryUnsealer(poFieldDefn->GetTemporaryUnsealer());
    1461          22 :     OGRFieldDefn oField(poFieldDefn);
    1462          22 :     const std::string osOldFieldName(poFieldDefn->GetNameRef());
    1463             :     const std::string osOldDomainName(
    1464          22 :         std::string(poFieldDefn->GetDomainName()));
    1465          22 :     const bool bRenamedField = (nFlagsIn & ALTER_NAME_FLAG) != 0 &&
    1466          11 :                                poNewFieldDefn->GetNameRef() != osOldFieldName;
    1467             : 
    1468          11 :     if (nFlagsIn & ALTER_TYPE_FLAG)
    1469             :     {
    1470          17 :         if (poFieldDefn->GetType() != poNewFieldDefn->GetType() ||
    1471           8 :             poFieldDefn->GetSubType() != poNewFieldDefn->GetSubType())
    1472             :         {
    1473           2 :             CPLError(CE_Failure, CPLE_NotSupported,
    1474             :                      "Altering the field type is not supported");
    1475           2 :             return OGRERR_FAILURE;
    1476             :         }
    1477             :     }
    1478           9 :     if (nFlagsIn & ALTER_NAME_FLAG)
    1479             :     {
    1480           9 :         if (bRenamedField)
    1481             :         {
    1482           6 :             const std::string osFieldNameOri(poNewFieldDefn->GetNameRef());
    1483             :             const std::string osFieldNameLaundered =
    1484           6 :                 GetLaunderedFieldName(osFieldNameOri);
    1485           6 :             if (osFieldNameLaundered != osFieldNameOri)
    1486             :             {
    1487           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1488             :                          "Invalid field name: %s. "
    1489             :                          "A potential valid name would be: %s",
    1490             :                          osFieldNameOri.c_str(), osFieldNameLaundered.c_str());
    1491           1 :                 return OGRERR_FAILURE;
    1492             :             }
    1493             : 
    1494           5 :             oField.SetName(poNewFieldDefn->GetNameRef());
    1495             :         }
    1496             :     }
    1497           8 :     if (nFlagsIn & ALTER_WIDTH_PRECISION_FLAG)
    1498             :     {
    1499           6 :         if (oField.GetType() == OFTString)
    1500           4 :             oField.SetWidth(poNewFieldDefn->GetWidth());
    1501             :     }
    1502           8 :     if (nFlagsIn & ALTER_DEFAULT_FLAG)
    1503             :     {
    1504           6 :         oField.SetDefault(poNewFieldDefn->GetDefault());
    1505             :     }
    1506           8 :     if (nFlagsIn & ALTER_NULLABLE_FLAG)
    1507             :     {
    1508             :         // could be potentially done, but involves .gdbtable rewriting
    1509           6 :         if (poFieldDefn->IsNullable() != poNewFieldDefn->IsNullable())
    1510             :         {
    1511           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1512             :                      "Altering the nullable state of a field "
    1513             :                      "is not currently supported for OpenFileGDB");
    1514           1 :             return OGRERR_FAILURE;
    1515             :         }
    1516             :     }
    1517           7 :     if (nFlagsIn & ALTER_DOMAIN_FLAG)
    1518             :     {
    1519           5 :         oField.SetDomainName(poNewFieldDefn->GetDomainName());
    1520             :     }
    1521           7 :     if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
    1522             :     {
    1523           5 :         oField.SetAlternativeName(poNewFieldDefn->GetAlternativeNameRef());
    1524             :     }
    1525             : 
    1526           7 :     const auto eType = GetGDBFieldType(&oField, m_bArcGISPro32OrLater);
    1527             : 
    1528           7 :     int nWidth = 0;
    1529           7 :     if (eType == FGFT_GLOBALID || eType == FGFT_GUID)
    1530             :     {
    1531           0 :         nWidth = 38;
    1532             :     }
    1533           7 :     else if (oField.GetType() == OFTString)
    1534             :     {
    1535           3 :         nWidth = oField.GetWidth();
    1536           3 :         if (nWidth == 0)
    1537             :         {
    1538             :             // Can be useful to try to replicate FileGDB driver, but do
    1539             :             // not use its 65536 default value.
    1540           2 :             nWidth = atoi(CPLGetConfigOption("OPENFILEGDB_STRING_WIDTH", "0"));
    1541             :         }
    1542             :     }
    1543             : 
    1544           7 :     OGRField sDefault = FileGDBField::UNSET_FIELD;
    1545          14 :     std::string osDefaultVal;
    1546           7 :     if (!GetDefault(&oField, eType, sDefault, osDefaultVal,
    1547             :                     /*bApproxOK=*/false))
    1548           0 :         return OGRERR_FAILURE;
    1549             : 
    1550           7 :     const char *pszAlias = oField.GetAlternativeNameRef();
    1551           7 :     if (!m_poLyrTable->AlterField(
    1552             :             nGDBIdx, oField.GetNameRef(),
    1553          14 :             pszAlias ? std::string(pszAlias) : std::string(), eType,
    1554           7 :             CPL_TO_BOOL(oField.IsNullable()), nWidth, sDefault))
    1555             :     {
    1556           1 :         return OGRERR_FAILURE;
    1557             :     }
    1558             : 
    1559           6 :     poFieldDefn->SetSubType(OFSTNone);
    1560           6 :     poFieldDefn->SetName(oField.GetNameRef());
    1561           6 :     poFieldDefn->SetAlternativeName(oField.GetAlternativeNameRef());
    1562           6 :     poFieldDefn->SetType(oField.GetType());
    1563           6 :     poFieldDefn->SetSubType(oField.GetSubType());
    1564           6 :     poFieldDefn->SetWidth(oField.GetWidth());
    1565           6 :     poFieldDefn->SetDefault(oField.GetDefault());
    1566           6 :     poFieldDefn->SetNullable(oField.IsNullable());
    1567           6 :     poFieldDefn->SetDomainName(oField.GetDomainName());
    1568             : 
    1569           6 :     if (m_bRegisteredTable)
    1570             :     {
    1571             :         // If the table is already registered (that is updating an existing
    1572             :         // layer), patch the XML definition
    1573          10 :         CPLXMLTreeCloser oTree(CPLParseXMLString(m_osDefinition.c_str()));
    1574           5 :         if (oTree)
    1575             :         {
    1576           5 :             CPLXMLNode *psGPFieldInfoExs = GetGPFieldInfoExsNode(oTree.get());
    1577           5 :             if (psGPFieldInfoExs)
    1578             :             {
    1579           5 :                 CPLXMLNode *psLastChild = nullptr;
    1580          21 :                 for (CPLXMLNode *psIter = psGPFieldInfoExs->psChild; psIter;
    1581          16 :                      psIter = psIter->psNext)
    1582             :                 {
    1583          58 :                     if (psIter->eType == CXT_Element &&
    1584          37 :                         strcmp(psIter->pszValue, "GPFieldInfoEx") == 0 &&
    1585          16 :                         CPLGetXMLValue(psIter, "Name", "") == osOldFieldName)
    1586             :                     {
    1587           5 :                         CPLXMLNode *psNext = psIter->psNext;
    1588           5 :                         psIter->psNext = nullptr;
    1589           5 :                         CPLDestroyXMLNode(psIter);
    1590           5 :                         psIter = CreateXMLFieldDefinition(
    1591           5 :                             poFieldDefn, m_poLyrTable->GetField(nGDBIdx),
    1592           5 :                             m_bArcGISPro32OrLater);
    1593           5 :                         psIter->psNext = psNext;
    1594           5 :                         if (psLastChild == nullptr)
    1595           0 :                             psGPFieldInfoExs->psChild = psIter;
    1596             :                         else
    1597           5 :                             psLastChild->psNext = psIter;
    1598           5 :                         break;
    1599             :                     }
    1600          16 :                     psLastChild = psIter;
    1601             :                 }
    1602             : 
    1603           5 :                 if (bRenamedField && m_iAreaField == iFieldToAlter)
    1604             :                 {
    1605             :                     CPLXMLNode *psNode =
    1606           1 :                         CPLSearchXMLNode(oTree.get(), "=AreaFieldName");
    1607           1 :                     if (psNode)
    1608             :                     {
    1609           1 :                         CPLSetXMLValue(psNode, "", poFieldDefn->GetNameRef());
    1610           1 :                     }
    1611             :                 }
    1612           4 :                 else if (bRenamedField && m_iLengthField == iFieldToAlter)
    1613             :                 {
    1614             :                     CPLXMLNode *psNode =
    1615           1 :                         CPLSearchXMLNode(oTree.get(), "=LengthFieldName");
    1616           1 :                     if (psNode)
    1617             :                     {
    1618           1 :                         CPLSetXMLValue(psNode, "", poFieldDefn->GetNameRef());
    1619             :                     }
    1620             :                 }
    1621             : 
    1622           5 :                 char *pszDefinition = CPLSerializeXMLTree(oTree.get());
    1623           5 :                 m_osDefinition = pszDefinition;
    1624           5 :                 CPLFree(pszDefinition);
    1625             : 
    1626           5 :                 m_poDS->UpdateXMLDefinition(m_osName.c_str(),
    1627             :                                             m_osDefinition.c_str());
    1628             :             }
    1629             :         }
    1630             :     }
    1631             :     else
    1632             :     {
    1633           1 :         RefreshXMLDefinitionInMemory();
    1634             :     }
    1635             : 
    1636           7 :     if (osOldDomainName != oField.GetDomainName() &&
    1637           1 :         (!m_osThisGUID.empty() ||
    1638           7 :          m_poDS->FindUUIDFromName(GetName(), m_osThisGUID)))
    1639             :     {
    1640           1 :         if (osOldDomainName.empty())
    1641             :         {
    1642           1 :             if (!m_poDS->LinkDomainToTable(oField.GetDomainName(),
    1643           1 :                                            m_osThisGUID))
    1644             :             {
    1645           0 :                 poFieldDefn->SetDomainName(std::string());
    1646             :             }
    1647             :         }
    1648             :         else
    1649             :         {
    1650           0 :             bool bDomainStillUsed = false;
    1651           0 :             for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    1652             :             {
    1653           0 :                 if (m_poFeatureDefn->GetFieldDefn(i)->GetDomainName() ==
    1654             :                     osOldDomainName)
    1655             :                 {
    1656           0 :                     bDomainStillUsed = true;
    1657           0 :                     break;
    1658             :                 }
    1659             :             }
    1660           0 :             if (!bDomainStillUsed)
    1661             :             {
    1662           0 :                 m_poDS->UnlinkDomainToTable(osOldDomainName, m_osThisGUID);
    1663             :             }
    1664             :         }
    1665             :     }
    1666             : 
    1667           6 :     return OGRERR_NONE;
    1668             : }
    1669             : 
    1670             : /************************************************************************/
    1671             : /*                         AlterGeomFieldDefn()                         */
    1672             : /************************************************************************/
    1673             : 
    1674           6 : OGRErr OGROpenFileGDBLayer::AlterGeomFieldDefn(
    1675             :     int iGeomFieldToAlter, const OGRGeomFieldDefn *poNewGeomFieldDefn,
    1676             :     int nFlagsIn)
    1677             : {
    1678           6 :     if (!m_bEditable)
    1679           0 :         return OGRERR_FAILURE;
    1680             : 
    1681           6 :     if (!BuildLayerDefinition())
    1682           0 :         return OGRERR_FAILURE;
    1683             : 
    1684           6 :     if (m_poDS->IsInTransaction() &&
    1685           0 :         ((!m_bHasCreatedBackupForTransaction && !BeginEmulatedTransaction()) ||
    1686           0 :          !m_poDS->BackupSystemTablesForTransaction()))
    1687             :     {
    1688           0 :         return OGRERR_FAILURE;
    1689             :     }
    1690             : 
    1691          12 :     if (iGeomFieldToAlter < 0 ||
    1692           6 :         iGeomFieldToAlter >= m_poFeatureDefn->GetGeomFieldCount())
    1693             :     {
    1694           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1695           0 :         return OGRERR_FAILURE;
    1696             :     }
    1697             : 
    1698          12 :     const int nGDBIdx = m_poLyrTable->GetFieldIdx(
    1699           6 :         m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter)->GetNameRef());
    1700           6 :     if (nGDBIdx < 0)
    1701           0 :         return OGRERR_FAILURE;
    1702             : 
    1703             :     const auto poGeomFieldDefn =
    1704           6 :         m_poFeatureDefn->GetGeomFieldDefn(iGeomFieldToAlter);
    1705          12 :     auto oTemporaryUnsealer(poGeomFieldDefn->GetTemporaryUnsealer());
    1706          12 :     OGRGeomFieldDefn oField(poGeomFieldDefn);
    1707             : 
    1708           6 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_TYPE_FLAG) != 0)
    1709             :     {
    1710           1 :         if (poGeomFieldDefn->GetType() != poNewGeomFieldDefn->GetType())
    1711             :         {
    1712           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1713             :                      "Altering the geometry field type is not supported for "
    1714             :                      "the FileGeodatabase format");
    1715           1 :             return OGRERR_FAILURE;
    1716             :         }
    1717             :     }
    1718             : 
    1719          10 :     const std::string osOldFieldName = poGeomFieldDefn->GetNameRef();
    1720             :     const bool bRenamedField =
    1721           6 :         (nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0 &&
    1722           1 :         poNewGeomFieldDefn->GetNameRef() != osOldFieldName;
    1723           5 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NAME_FLAG) != 0)
    1724             :     {
    1725           1 :         if (bRenamedField)
    1726             :         {
    1727           1 :             const std::string osFieldNameOri(poNewGeomFieldDefn->GetNameRef());
    1728             :             const std::string osFieldNameLaundered =
    1729           1 :                 GetLaunderedFieldName(osFieldNameOri);
    1730           1 :             if (osFieldNameLaundered != osFieldNameOri)
    1731             :             {
    1732           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1733             :                          "Invalid field name: %s. "
    1734             :                          "A potential valid name would be: %s",
    1735             :                          osFieldNameOri.c_str(), osFieldNameLaundered.c_str());
    1736           0 :                 return OGRERR_FAILURE;
    1737             :             }
    1738             : 
    1739           1 :             oField.SetName(poNewGeomFieldDefn->GetNameRef());
    1740             :         }
    1741             :         // oField.SetAlternativeName(poNewGeomFieldDefn->GetAlternativeNameRef());
    1742             :     }
    1743             : 
    1744           5 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_NULLABLE_FLAG) != 0)
    1745             :     {
    1746             :         // could be potentially done, but involves .gdbtable rewriting
    1747           1 :         if (poGeomFieldDefn->IsNullable() != poNewGeomFieldDefn->IsNullable())
    1748             :         {
    1749           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    1750             :                      "Altering the nullable state of the geometry field "
    1751             :                      "is not currently supported for OpenFileGDB");
    1752           1 :             return OGRERR_FAILURE;
    1753             :         }
    1754             :     }
    1755             : 
    1756           4 :     if ((nFlagsIn & ALTER_GEOM_FIELD_DEFN_SRS_FLAG) != 0)
    1757             :     {
    1758           3 :         const auto poOldSRS = poGeomFieldDefn->GetSpatialRef();
    1759           3 :         const auto poNewSRS = poNewGeomFieldDefn->GetSpatialRef();
    1760             : 
    1761           3 :         const char *const apszOptions[] = {
    1762             :             "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES", nullptr};
    1763           3 :         if ((poOldSRS == nullptr && poNewSRS != nullptr) ||
    1764           7 :             (poOldSRS != nullptr && poNewSRS == nullptr) ||
    1765           1 :             (poOldSRS != nullptr && poNewSRS != nullptr &&
    1766           1 :              !poOldSRS->IsSame(poNewSRS, apszOptions)))
    1767             :         {
    1768           3 :             if (!m_osFeatureDatasetGUID.empty())
    1769             :             {
    1770             :                 // Could potentially be done (would require changing the SRS
    1771             :                 // in all layers of the feature dataset)
    1772           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    1773             :                          "Altering the SRS of the geometry field of a layer "
    1774             :                          "in a feature daaset is not currently supported "
    1775             :                          "for OpenFileGDB");
    1776           0 :                 return OGRERR_FAILURE;
    1777             :             }
    1778             : 
    1779           3 :             if (poNewSRS)
    1780             :             {
    1781           2 :                 auto poNewSRSClone = poNewSRS->Clone();
    1782           2 :                 oField.SetSpatialRef(poNewSRSClone);
    1783           2 :                 poNewSRSClone->Release();
    1784             :             }
    1785             :             else
    1786             :             {
    1787           1 :                 oField.SetSpatialRef(nullptr);
    1788             :             }
    1789             :         }
    1790             :     }
    1791             : 
    1792           8 :     std::string osWKT = "{B286C06B-0879-11D2-AACA-00C04FA33C20}";  // No SRS
    1793           4 :     if (oField.GetSpatialRef())
    1794             :     {
    1795           3 :         const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr};
    1796             :         char *pszWKT;
    1797           3 :         oField.GetSpatialRef()->exportToWkt(&pszWKT, apszOptions);
    1798           3 :         osWKT = pszWKT;
    1799           3 :         CPLFree(pszWKT);
    1800             :     }
    1801             : 
    1802           4 :     if (!m_poLyrTable->AlterGeomField(oField.GetNameRef(),
    1803           8 :                                       std::string(),  // Alias
    1804           4 :                                       CPL_TO_BOOL(oField.IsNullable()), osWKT))
    1805             :     {
    1806           0 :         return OGRERR_FAILURE;
    1807             :     }
    1808             : 
    1809           4 :     poGeomFieldDefn->SetName(oField.GetNameRef());
    1810           4 :     poGeomFieldDefn->SetSpatialRef(oField.GetSpatialRef());
    1811             : 
    1812           4 :     if (m_bRegisteredTable)
    1813             :     {
    1814             :         // If the table is already registered (that is updating an existing
    1815             :         // layer), patch the XML definition
    1816           8 :         CPLXMLTreeCloser oTree(CPLParseXMLString(m_osDefinition.c_str()));
    1817           4 :         if (oTree)
    1818             :         {
    1819           4 :             CPLXMLNode *psGPFieldInfoExs = GetGPFieldInfoExsNode(oTree.get());
    1820           4 :             if (psGPFieldInfoExs)
    1821             :             {
    1822           8 :                 for (CPLXMLNode *psIter = psGPFieldInfoExs->psChild; psIter;
    1823           4 :                      psIter = psIter->psNext)
    1824             :                 {
    1825          20 :                     if (psIter->eType == CXT_Element &&
    1826          12 :                         strcmp(psIter->pszValue, "GPFieldInfoEx") == 0 &&
    1827           4 :                         CPLGetXMLValue(psIter, "Name", "") == osOldFieldName)
    1828             :                     {
    1829           4 :                         CPLXMLNode *psNode = CPLGetXMLNode(psIter, "Name");
    1830           4 :                         if (psNode && psNode->psChild &&
    1831           4 :                             psNode->psChild->eType == CXT_Text)
    1832             :                         {
    1833           4 :                             CPLFree(psNode->psChild->pszValue);
    1834           8 :                             psNode->psChild->pszValue =
    1835           4 :                                 CPLStrdup(poGeomFieldDefn->GetNameRef());
    1836             :                         }
    1837           4 :                         break;
    1838             :                     }
    1839             :                 }
    1840             : 
    1841             :                 CPLXMLNode *psNode =
    1842           4 :                     CPLSearchXMLNode(oTree.get(), "=ShapeFieldName");
    1843           4 :                 if (psNode)
    1844             :                 {
    1845           4 :                     CPLSetXMLValue(psNode, "", poGeomFieldDefn->GetNameRef());
    1846             :                 }
    1847             : 
    1848             :                 CPLXMLNode *psFeatureClassInfo =
    1849           4 :                     CPLSearchXMLNode(oTree.get(), "=DEFeatureClassInfo");
    1850           4 :                 if (psFeatureClassInfo == nullptr)
    1851           4 :                     psFeatureClassInfo = CPLSearchXMLNode(
    1852             :                         oTree.get(), "=typens:DEFeatureClassInfo");
    1853           4 :                 if (psFeatureClassInfo)
    1854             :                 {
    1855           4 :                     psNode = CPLGetXMLNode(psFeatureClassInfo, "Extent");
    1856           4 :                     if (psNode)
    1857             :                     {
    1858           4 :                         if (CPLRemoveXMLChild(psFeatureClassInfo, psNode))
    1859           4 :                             CPLDestroyXMLNode(psNode);
    1860             :                     }
    1861             : 
    1862             :                     psNode =
    1863           4 :                         CPLGetXMLNode(psFeatureClassInfo, "SpatialReference");
    1864           4 :                     if (psNode)
    1865             :                     {
    1866           4 :                         if (CPLRemoveXMLChild(psFeatureClassInfo, psNode))
    1867           4 :                             CPLDestroyXMLNode(psNode);
    1868             :                     }
    1869             : 
    1870           4 :                     XMLSerializeGeomFieldBase(psFeatureClassInfo,
    1871           4 :                                               m_poLyrTable->GetGeomField(),
    1872           4 :                                               GetSpatialRef());
    1873             :                 }
    1874             : 
    1875           4 :                 char *pszDefinition = CPLSerializeXMLTree(oTree.get());
    1876           4 :                 m_osDefinition = pszDefinition;
    1877           4 :                 CPLFree(pszDefinition);
    1878             : 
    1879           4 :                 m_poDS->UpdateXMLDefinition(m_osName.c_str(),
    1880             :                                             m_osDefinition.c_str());
    1881             :             }
    1882             :         }
    1883             :     }
    1884             :     else
    1885             :     {
    1886           0 :         RefreshXMLDefinitionInMemory();
    1887             :     }
    1888             : 
    1889           4 :     return OGRERR_NONE;
    1890             : }
    1891             : 
    1892             : /************************************************************************/
    1893             : /*                             DeleteField()                            */
    1894             : /************************************************************************/
    1895             : 
    1896          17 : OGRErr OGROpenFileGDBLayer::DeleteField(int iFieldToDelete)
    1897             : {
    1898          17 :     if (!m_bEditable)
    1899           0 :         return OGRERR_FAILURE;
    1900             : 
    1901          17 :     if (!BuildLayerDefinition())
    1902           0 :         return OGRERR_FAILURE;
    1903             : 
    1904          18 :     if (m_poDS->IsInTransaction() &&
    1905           1 :         ((!m_bHasCreatedBackupForTransaction && !BeginEmulatedTransaction()) ||
    1906           1 :          !m_poDS->BackupSystemTablesForTransaction()))
    1907             :     {
    1908           0 :         return OGRERR_FAILURE;
    1909             :     }
    1910             : 
    1911          34 :     if (iFieldToDelete < 0 ||
    1912          17 :         iFieldToDelete >= m_poFeatureDefn->GetFieldCount())
    1913             :     {
    1914           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
    1915           0 :         return OGRERR_FAILURE;
    1916             :     }
    1917             : 
    1918          17 :     if (iFieldToDelete == m_iFIDAsRegularColumnIndex)
    1919             :     {
    1920           3 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot delete field %s",
    1921             :                  GetFIDColumn());
    1922           3 :         return OGRERR_FAILURE;
    1923             :     }
    1924             : 
    1925          14 :     const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(iFieldToDelete);
    1926          14 :     const int nGDBIdx = m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
    1927          14 :     if (nGDBIdx < 0)
    1928           0 :         return OGRERR_FAILURE;
    1929          14 :     const bool bRet = m_poLyrTable->DeleteField(nGDBIdx);
    1930          14 :     m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
    1931             : 
    1932          14 :     if (!bRet)
    1933           0 :         return OGRERR_FAILURE;
    1934             : 
    1935          28 :     const std::string osDeletedFieldName = poFieldDefn->GetNameRef();
    1936             :     const std::string osOldDomainName =
    1937          14 :         std::string(poFieldDefn->GetDomainName());
    1938             : 
    1939          14 :     whileUnsealing(m_poFeatureDefn)->DeleteFieldDefn(iFieldToDelete);
    1940             : 
    1941          14 :     if (m_iFIDAsRegularColumnIndex > iFieldToDelete)
    1942           3 :         m_iFIDAsRegularColumnIndex--;
    1943             : 
    1944          14 :     if (iFieldToDelete < m_iAreaField)
    1945           0 :         m_iAreaField--;
    1946          14 :     if (iFieldToDelete < m_iLengthField)
    1947           0 :         m_iLengthField--;
    1948             : 
    1949          14 :     bool bEmptyAreaFieldName = false;
    1950          14 :     bool bEmptyLengthFieldName = false;
    1951          14 :     if (m_iAreaField == iFieldToDelete)
    1952             :     {
    1953           1 :         bEmptyAreaFieldName = true;
    1954           1 :         m_iAreaField = -1;
    1955             :     }
    1956          13 :     else if (m_iLengthField == iFieldToDelete)
    1957             :     {
    1958           1 :         bEmptyLengthFieldName = true;
    1959           1 :         m_iLengthField = -1;
    1960             :     }
    1961             : 
    1962          14 :     if (m_bRegisteredTable)
    1963             :     {
    1964             :         // If the table is already registered (that is updating an existing
    1965             :         // layer), patch the XML definition to add the new field
    1966          16 :         CPLXMLTreeCloser oTree(CPLParseXMLString(m_osDefinition.c_str()));
    1967           8 :         if (oTree)
    1968             :         {
    1969           8 :             CPLXMLNode *psGPFieldInfoExs = GetGPFieldInfoExsNode(oTree.get());
    1970           8 :             if (psGPFieldInfoExs)
    1971             :             {
    1972           8 :                 CPLXMLNode *psLastChild = nullptr;
    1973          31 :                 for (CPLXMLNode *psIter = psGPFieldInfoExs->psChild; psIter;
    1974          23 :                      psIter = psIter->psNext)
    1975             :                 {
    1976          85 :                     if (psIter->eType == CXT_Element &&
    1977          54 :                         strcmp(psIter->pszValue, "GPFieldInfoEx") == 0 &&
    1978          23 :                         CPLGetXMLValue(psIter, "Name", "") ==
    1979             :                             osDeletedFieldName)
    1980             :                     {
    1981           8 :                         if (psLastChild == nullptr)
    1982           0 :                             psGPFieldInfoExs->psChild = psIter->psNext;
    1983             :                         else
    1984           8 :                             psLastChild->psNext = psIter->psNext;
    1985           8 :                         psIter->psNext = nullptr;
    1986           8 :                         CPLDestroyXMLNode(psIter);
    1987           8 :                         break;
    1988             :                     }
    1989          23 :                     psLastChild = psIter;
    1990             :                 }
    1991             : 
    1992           8 :                 if (bEmptyAreaFieldName)
    1993             :                 {
    1994             :                     CPLXMLNode *psNode =
    1995           1 :                         CPLSearchXMLNode(oTree.get(), "=AreaFieldName");
    1996           1 :                     if (psNode && psNode->psChild)
    1997             :                     {
    1998           1 :                         CPLDestroyXMLNode(psNode->psChild);
    1999           1 :                         psNode->psChild = nullptr;
    2000             :                     }
    2001             :                 }
    2002           7 :                 else if (bEmptyLengthFieldName)
    2003             :                 {
    2004             :                     CPLXMLNode *psNode =
    2005           1 :                         CPLSearchXMLNode(oTree.get(), "=LengthFieldName");
    2006           1 :                     if (psNode && psNode->psChild)
    2007             :                     {
    2008           1 :                         CPLDestroyXMLNode(psNode->psChild);
    2009           1 :                         psNode->psChild = nullptr;
    2010             :                     }
    2011             :                 }
    2012             : 
    2013           8 :                 char *pszDefinition = CPLSerializeXMLTree(oTree.get());
    2014           8 :                 m_osDefinition = pszDefinition;
    2015           8 :                 CPLFree(pszDefinition);
    2016             : 
    2017           8 :                 m_poDS->UpdateXMLDefinition(m_osName.c_str(),
    2018             :                                             m_osDefinition.c_str());
    2019             :             }
    2020             :         }
    2021             :     }
    2022             :     else
    2023             :     {
    2024           6 :         RefreshXMLDefinitionInMemory();
    2025             :     }
    2026             : 
    2027          14 :     if (!osOldDomainName.empty())
    2028             :     {
    2029           2 :         bool bDomainStillUsed = false;
    2030           2 :         for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    2031             :         {
    2032           1 :             if (m_poFeatureDefn->GetFieldDefn(i)->GetDomainName() ==
    2033             :                 osOldDomainName)
    2034             :             {
    2035           1 :                 bDomainStillUsed = true;
    2036           1 :                 break;
    2037             :             }
    2038             :         }
    2039           2 :         if (!bDomainStillUsed)
    2040             :         {
    2041           2 :             if (!m_osThisGUID.empty() ||
    2042           2 :                 m_poDS->FindUUIDFromName(GetName(), m_osThisGUID))
    2043             :             {
    2044           1 :                 m_poDS->UnlinkDomainToTable(osOldDomainName, m_osThisGUID);
    2045             :             }
    2046             :         }
    2047             :     }
    2048             : 
    2049          14 :     return OGRERR_NONE;
    2050             : }
    2051             : 
    2052             : /************************************************************************/
    2053             : /*                            GetLength()                               */
    2054             : /************************************************************************/
    2055             : 
    2056           7 : static double GetLength(const OGRCurvePolygon *poPoly)
    2057             : {
    2058           7 :     double dfLength = 0;
    2059          18 :     for (const auto *poRing : *poPoly)
    2060          11 :         dfLength += poRing->get_Length();
    2061           7 :     return dfLength;
    2062             : }
    2063             : 
    2064           3 : static double GetLength(const OGRMultiSurface *poMS)
    2065             : {
    2066           3 :     double dfLength = 0;
    2067           8 :     for (const auto *poPoly : *poMS)
    2068             :     {
    2069           5 :         auto poCurvePolygon = dynamic_cast<const OGRCurvePolygon *>(poPoly);
    2070           5 :         if (poCurvePolygon)
    2071           5 :             dfLength += GetLength(poCurvePolygon);
    2072             :     }
    2073           3 :     return dfLength;
    2074             : }
    2075             : 
    2076             : /************************************************************************/
    2077             : /*                      PrepareFileGDBFeature()                         */
    2078             : /************************************************************************/
    2079             : 
    2080        9308 : bool OGROpenFileGDBLayer::PrepareFileGDBFeature(OGRFeature *poFeature,
    2081             :                                                 std::vector<OGRField> &fields,
    2082             :                                                 const OGRGeometry *&poGeom,
    2083             :                                                 bool bUpdate)
    2084             : {
    2085             :     // Check geometry type
    2086        9308 :     poGeom = poFeature->GetGeometryRef();
    2087             :     const auto eFlattenType =
    2088        9308 :         poGeom ? wkbFlatten(poGeom->getGeometryType()) : wkbNone;
    2089        9308 :     if (poGeom)
    2090             :     {
    2091        3903 :         switch (m_poLyrTable->GetGeometryType())
    2092             :         {
    2093           0 :             case FGTGT_NONE:
    2094           0 :                 break;
    2095             : 
    2096        3788 :             case FGTGT_POINT:
    2097             :             {
    2098        3788 :                 if (eFlattenType != wkbPoint)
    2099             :                 {
    2100           3 :                     CPLError(
    2101             :                         CE_Failure, CPLE_NotSupported,
    2102             :                         "Can only insert a Point in a esriGeometryPoint layer");
    2103           3 :                     return false;
    2104             :                 }
    2105        3785 :                 break;
    2106             :             }
    2107             : 
    2108          11 :             case FGTGT_MULTIPOINT:
    2109             :             {
    2110          11 :                 if (eFlattenType != wkbMultiPoint)
    2111             :                 {
    2112           2 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2113             :                              "Can only insert a MultiPoint in a "
    2114             :                              "esriGeometryMultiPoint layer");
    2115           2 :                     return false;
    2116             :                 }
    2117           9 :                 break;
    2118             :             }
    2119             : 
    2120          40 :             case FGTGT_LINE:
    2121             :             {
    2122          40 :                 if (eFlattenType != wkbLineString &&
    2123          13 :                     eFlattenType != wkbMultiLineString &&
    2124          11 :                     eFlattenType != wkbCircularString &&
    2125           7 :                     eFlattenType != wkbCompoundCurve &&
    2126             :                     eFlattenType != wkbMultiCurve)
    2127             :                 {
    2128           5 :                     CPLError(
    2129             :                         CE_Failure, CPLE_NotSupported,
    2130             :                         "Can only insert a "
    2131             :                         "LineString/MultiLineString/CircularString/"
    2132             :                         "CompoundCurve/MultiCurve in a esriGeometryLine layer");
    2133           5 :                     return false;
    2134             :                 }
    2135          35 :                 break;
    2136             :             }
    2137             : 
    2138          60 :             case FGTGT_POLYGON:
    2139             :             {
    2140          60 :                 if (eFlattenType != wkbPolygon &&
    2141          19 :                     eFlattenType != wkbMultiPolygon &&
    2142           9 :                     eFlattenType != wkbCurvePolygon &&
    2143             :                     eFlattenType != wkbMultiSurface)
    2144             :                 {
    2145           6 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2146             :                              "Can only insert a "
    2147             :                              "Polygon/MultiPolygon/CurvePolygon/MultiSurface "
    2148             :                              "in a esriGeometryPolygon layer");
    2149           6 :                     return false;
    2150             :                 }
    2151          54 :                 break;
    2152             :             }
    2153             : 
    2154           4 :             case FGTGT_MULTIPATCH:
    2155             :             {
    2156           4 :                 if (eFlattenType != wkbTIN &&
    2157           4 :                     eFlattenType != wkbPolyhedralSurface &&
    2158             :                     eFlattenType != wkbGeometryCollection)
    2159             :                 {
    2160           1 :                     CPLError(CE_Failure, CPLE_NotSupported,
    2161             :                              "Can only insert a "
    2162             :                              "TIN/PolyhedralSurface/GeometryCollection in a "
    2163             :                              "esriGeometryMultiPatch layer");
    2164           1 :                     return false;
    2165             :                 }
    2166           3 :                 break;
    2167             :             }
    2168             :         }
    2169             : 
    2170             :         // Treat empty geometries as NULL, like the FileGDB driver
    2171        3903 :         if (poGeom->IsEmpty() &&
    2172          17 :             !CPLTestBool(CPLGetConfigOption(
    2173             :                 "OGR_OPENFILEGDB_WRITE_EMPTY_GEOMETRY", "NO")))
    2174             :         {
    2175           5 :             poGeom = nullptr;
    2176             :         }
    2177             :     }
    2178             : 
    2179        9291 :     if (m_iAreaField >= 0)
    2180             :     {
    2181           6 :         const int i = m_iAreaField;
    2182           6 :         if (poGeom != nullptr)
    2183             :         {
    2184           5 :             if (eFlattenType == wkbPolygon || eFlattenType == wkbCurvePolygon)
    2185           2 :                 poFeature->SetField(i, poGeom->toCurvePolygon()->get_Area());
    2186           3 :             else if (eFlattenType == wkbMultiPolygon ||
    2187             :                      eFlattenType == wkbMultiSurface)
    2188           3 :                 poFeature->SetField(i, poGeom->toMultiSurface()->get_Area());
    2189             :             else
    2190           0 :                 poFeature->SetFieldNull(
    2191             :                     i);  // shouldn't happen in nominal situation
    2192             :         }
    2193             :         else
    2194             :         {
    2195           1 :             poFeature->SetFieldNull(i);
    2196             :         }
    2197             :     }
    2198             : 
    2199        9291 :     if (m_iLengthField >= 0)
    2200             :     {
    2201          11 :         const int i = m_iLengthField;
    2202          11 :         if (poGeom != nullptr)
    2203             :         {
    2204           9 :             if (OGR_GT_IsCurve(eFlattenType))
    2205           2 :                 poFeature->SetField(i, poGeom->toCurve()->get_Length());
    2206           7 :             else if (OGR_GT_IsSubClassOf(eFlattenType, wkbMultiCurve))
    2207           2 :                 poFeature->SetField(i, poGeom->toMultiCurve()->get_Length());
    2208           5 :             else if (eFlattenType == wkbPolygon ||
    2209             :                      eFlattenType == wkbCurvePolygon)
    2210           2 :                 poFeature->SetField(i, GetLength(poGeom->toCurvePolygon()));
    2211           3 :             else if (eFlattenType == wkbMultiPolygon ||
    2212             :                      eFlattenType == wkbMultiSurface)
    2213           3 :                 poFeature->SetField(i, GetLength(poGeom->toMultiSurface()));
    2214             :             else
    2215           0 :                 poFeature->SetFieldNull(
    2216             :                     i);  // shouldn't happen in nominal situation
    2217             :         }
    2218             :         else
    2219             :         {
    2220           2 :             poFeature->SetFieldNull(i);
    2221             :         }
    2222             :     }
    2223             : 
    2224        9291 :     fields.resize(m_poLyrTable->GetFieldCount(), FileGDBField::UNSET_FIELD);
    2225        9291 :     m_aosTempStrings.clear();
    2226       15103 :     for (int i = 0; i < m_poFeatureDefn->GetFieldCount(); ++i)
    2227             :     {
    2228        5812 :         if (i == m_iFIDAsRegularColumnIndex)
    2229           9 :             continue;
    2230        5803 :         const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(i);
    2231             :         const int idxFileGDB =
    2232        5803 :             m_poLyrTable->GetFieldIdx(poFieldDefn->GetNameRef());
    2233        5803 :         if (idxFileGDB < 0)
    2234           0 :             continue;
    2235        5803 :         if (!poFeature->IsFieldSetAndNotNull(i))
    2236             :         {
    2237         144 :             if (m_poLyrTable->GetField(idxFileGDB)->GetType() == FGFT_GLOBALID)
    2238             :             {
    2239           6 :                 m_aosTempStrings.emplace_back(OFGDBGenerateUUID());
    2240           6 :                 fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2241             :             }
    2242         144 :             continue;
    2243             :         }
    2244        5659 :         memset(&fields[idxFileGDB], 0, sizeof(OGRField));
    2245        5659 :         switch (m_poLyrTable->GetField(idxFileGDB)->GetType())
    2246             :         {
    2247           0 :             case FGFT_UNDEFINED:
    2248           0 :                 CPLAssert(false);
    2249             :                 break;
    2250           3 :             case FGFT_INT16:
    2251           6 :                 fields[idxFileGDB].Integer =
    2252           3 :                     poFeature->GetRawFieldRef(i)->Integer;
    2253           3 :                 break;
    2254         151 :             case FGFT_INT32:
    2255         302 :                 fields[idxFileGDB].Integer =
    2256         151 :                     poFeature->GetRawFieldRef(i)->Integer;
    2257         151 :                 break;
    2258           3 :             case FGFT_FLOAT32:
    2259           3 :                 fields[idxFileGDB].Real = poFeature->GetRawFieldRef(i)->Real;
    2260           3 :                 break;
    2261          62 :             case FGFT_FLOAT64:
    2262             :             {
    2263          62 :                 if (poFieldDefn->GetType() == OFTReal)
    2264             :                 {
    2265          60 :                     fields[idxFileGDB].Real =
    2266          60 :                         poFeature->GetRawFieldRef(i)->Real;
    2267             :                 }
    2268             :                 else
    2269             :                 {
    2270           2 :                     fields[idxFileGDB].Real = poFeature->GetFieldAsDouble(i);
    2271             :                 }
    2272          62 :                 break;
    2273             :             }
    2274        5351 :             case FGFT_STRING:
    2275             :             case FGFT_GUID:
    2276             :             case FGFT_XML:
    2277             :             {
    2278        5351 :                 if (poFieldDefn->GetType() == OFTString)
    2279             :                 {
    2280        5351 :                     fields[idxFileGDB].String =
    2281        5351 :                         poFeature->GetRawFieldRef(i)->String;
    2282             :                 }
    2283             :                 else
    2284             :                 {
    2285             :                     m_aosTempStrings.emplace_back(
    2286           0 :                         poFeature->GetFieldAsString(i));
    2287           0 :                     fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2288             :                 }
    2289        5351 :                 break;
    2290             :             }
    2291          67 :             case FGFT_DATETIME:
    2292             :             case FGFT_DATE:
    2293             :             {
    2294          67 :                 fields[idxFileGDB].Date = poFeature->GetRawFieldRef(i)->Date;
    2295          67 :                 if (m_bTimeInUTC && fields[idxFileGDB].Date.TZFlag <= 1)
    2296             :                 {
    2297          60 :                     if (!m_bRegisteredTable &&
    2298         120 :                         m_poLyrTable->GetTotalRecordCount() == 0 &&
    2299           2 :                         m_aosCreationOptions.FetchNameValue("TIME_IN_UTC") ==
    2300             :                             nullptr)
    2301             :                     {
    2302             :                         // If the user didn't explicitly set TIME_IN_UTC, and
    2303             :                         // this is the first feature written, automatically
    2304             :                         // adjust m_bTimeInUTC from the first value
    2305           2 :                         m_bTimeInUTC = false;
    2306             :                     }
    2307          58 :                     else if (!m_bWarnedDateNotConvertibleUTC)
    2308             :                     {
    2309          14 :                         m_bWarnedDateNotConvertibleUTC = true;
    2310          14 :                         CPLError(
    2311             :                             CE_Warning, CPLE_AppDefined,
    2312             :                             "Attempt at writing a datetime with a unknown time "
    2313             :                             "zone "
    2314             :                             "or local time in a layer that expects dates "
    2315             :                             "to be convertible to UTC. It will be written as "
    2316             :                             "if it was expressed in UTC.");
    2317             :                     }
    2318             :                 }
    2319          67 :                 break;
    2320             :             }
    2321           0 :             case FGFT_OBJECTID:
    2322           0 :                 CPLAssert(false);
    2323             :                 break;  // shouldn't happen
    2324           0 :             case FGFT_GEOMETRY:
    2325           0 :                 CPLAssert(false);
    2326             :                 break;  // shouldn't happen
    2327           0 :             case FGFT_RASTER:
    2328           0 :                 CPLAssert(false);
    2329             :                 break;  // shouldn't happen
    2330           2 :             case FGFT_BINARY:
    2331           2 :                 fields[idxFileGDB].Binary =
    2332           2 :                     poFeature->GetRawFieldRef(i)->Binary;
    2333           2 :                 break;
    2334           0 :             case FGFT_GLOBALID:
    2335             :             {
    2336           0 :                 if (bUpdate)
    2337             :                 {
    2338             :                     m_aosTempStrings.emplace_back(
    2339           0 :                         poFeature->GetFieldAsString(i));
    2340           0 :                     fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2341             :                 }
    2342           0 :                 else if (poFeature->GetRawFieldRef(i)->String[0] != '\0')
    2343             :                 {
    2344           0 :                     if (CPLTestBool(CPLGetConfigOption(
    2345             :                             "OPENFILEGDB_REGENERATE_GLOBALID", "YES")))
    2346             :                     {
    2347           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2348             :                                  "Value found in a GlobalID field. It will be "
    2349             :                                  "replaced by a "
    2350             :                                  "newly generated UUID.");
    2351           0 :                         m_aosTempStrings.emplace_back(OFGDBGenerateUUID());
    2352           0 :                         fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2353             :                     }
    2354             :                     else
    2355             :                     {
    2356             :                         m_aosTempStrings.emplace_back(
    2357           0 :                             poFeature->GetFieldAsString(i));
    2358           0 :                         fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2359             :                     }
    2360             :                 }
    2361             :                 else
    2362             :                 {
    2363           0 :                     m_aosTempStrings.emplace_back(OFGDBGenerateUUID());
    2364           0 :                     fields[idxFileGDB].String = &m_aosTempStrings.back()[0];
    2365             :                 }
    2366           0 :                 break;
    2367             :             }
    2368           2 :             case FGFT_INT64:
    2369             :             {
    2370           4 :                 fields[idxFileGDB].Integer64 =
    2371           2 :                     poFeature->GetRawFieldRef(i)->Integer64;
    2372           2 :                 break;
    2373             :             }
    2374          18 :             case FGFT_TIME:
    2375             :             case FGFT_DATETIME_WITH_OFFSET:
    2376             :             {
    2377          18 :                 fields[idxFileGDB].Date = poFeature->GetRawFieldRef(i)->Date;
    2378          18 :                 break;
    2379             :             }
    2380             :         }
    2381             :     }
    2382             : 
    2383        9291 :     return true;
    2384             : }
    2385             : 
    2386             : /************************************************************************/
    2387             : /*                   CheckFIDAndFIDColumnConsistency()                  */
    2388             : /************************************************************************/
    2389             : 
    2390           9 : static bool CheckFIDAndFIDColumnConsistency(const OGRFeature *poFeature,
    2391             :                                             int iFIDAsRegularColumnIndex)
    2392             : {
    2393           9 :     bool ok = false;
    2394           9 :     if (!poFeature->IsFieldSetAndNotNull(iFIDAsRegularColumnIndex))
    2395             :     {
    2396             :         // nothing to do
    2397             :     }
    2398           9 :     else if (poFeature->GetDefnRef()
    2399           9 :                  ->GetFieldDefn(iFIDAsRegularColumnIndex)
    2400           9 :                  ->GetType() == OFTReal)
    2401             :     {
    2402             :         const double dfFID =
    2403           3 :             poFeature->GetFieldAsDouble(iFIDAsRegularColumnIndex);
    2404           3 :         if (GDALIsValueInRange<int64_t>(dfFID))
    2405             :         {
    2406           3 :             const auto nFID = static_cast<GIntBig>(dfFID);
    2407           3 :             if (nFID == poFeature->GetFID())
    2408             :             {
    2409           2 :                 ok = true;
    2410             :             }
    2411             :         }
    2412             :     }
    2413          12 :     else if (poFeature->GetFieldAsInteger64(iFIDAsRegularColumnIndex) ==
    2414           6 :              poFeature->GetFID())
    2415             :     {
    2416           4 :         ok = true;
    2417             :     }
    2418           9 :     if (!ok)
    2419             :     {
    2420           3 :         CPLError(CE_Failure, CPLE_AppDefined,
    2421             :                  "Inconsistent values of FID and field of same name");
    2422             :     }
    2423           9 :     return ok;
    2424             : }
    2425             : 
    2426             : /************************************************************************/
    2427             : /*                           ICreateFeature()                           */
    2428             : /************************************************************************/
    2429             : 
    2430        9308 : OGRErr OGROpenFileGDBLayer::ICreateFeature(OGRFeature *poFeature)
    2431             : {
    2432        9308 :     if (!m_bEditable)
    2433           0 :         return OGRERR_FAILURE;
    2434             : 
    2435        9308 :     if (!BuildLayerDefinition())
    2436           0 :         return OGRERR_FAILURE;
    2437             : 
    2438        9310 :     if (m_poDS->IsInTransaction() && !m_bHasCreatedBackupForTransaction &&
    2439           2 :         !BeginEmulatedTransaction())
    2440             :     {
    2441           0 :         return OGRERR_FAILURE;
    2442             :     }
    2443             : 
    2444             :     /* In case the FID column has also been created as a regular field */
    2445        9308 :     if (m_iFIDAsRegularColumnIndex >= 0)
    2446             :     {
    2447           9 :         if (poFeature->GetFID() == OGRNullFID)
    2448             :         {
    2449           3 :             if (poFeature->IsFieldSetAndNotNull(m_iFIDAsRegularColumnIndex))
    2450             :             {
    2451           6 :                 if (m_poFeatureDefn->GetFieldDefn(m_iFIDAsRegularColumnIndex)
    2452           3 :                         ->GetType() == OFTReal)
    2453             :                 {
    2454           1 :                     bool ok = false;
    2455             :                     const double dfFID =
    2456           1 :                         poFeature->GetFieldAsDouble(m_iFIDAsRegularColumnIndex);
    2457           2 :                     if (dfFID >= static_cast<double>(
    2458           2 :                                      std::numeric_limits<int64_t>::min()) &&
    2459           1 :                         dfFID <= static_cast<double>(
    2460           1 :                                      std::numeric_limits<int64_t>::max()))
    2461             :                     {
    2462           1 :                         const auto nFID = static_cast<GIntBig>(dfFID);
    2463           1 :                         if (static_cast<double>(nFID) == dfFID)
    2464             :                         {
    2465           1 :                             poFeature->SetFID(nFID);
    2466           1 :                             ok = true;
    2467             :                         }
    2468             :                     }
    2469           1 :                     if (!ok)
    2470             :                     {
    2471           0 :                         CPLError(
    2472             :                             CE_Failure, CPLE_AppDefined,
    2473             :                             "Value of FID %g cannot be parsed to an Integer64",
    2474             :                             dfFID);
    2475           0 :                         return OGRERR_FAILURE;
    2476             :                     }
    2477             :                 }
    2478             :                 else
    2479             :                 {
    2480           2 :                     poFeature->SetFID(poFeature->GetFieldAsInteger64(
    2481           2 :                         m_iFIDAsRegularColumnIndex));
    2482             :                 }
    2483             :             }
    2484             :         }
    2485           6 :         else if (!CheckFIDAndFIDColumnConsistency(poFeature,
    2486             :                                                   m_iFIDAsRegularColumnIndex))
    2487             :         {
    2488           3 :             return OGRERR_FAILURE;
    2489             :         }
    2490             :     }
    2491             : 
    2492        9305 :     const auto nFID64Bit = poFeature->GetFID();
    2493        9305 :     if (nFID64Bit < -1 || nFID64Bit == 0 || nFID64Bit > INT_MAX)
    2494             :     {
    2495           6 :         CPLError(CE_Failure, CPLE_NotSupported,
    2496             :                  "Only 32 bit positive integers FID supported by FileGDB");
    2497           6 :         return OGRERR_FAILURE;
    2498             :     }
    2499             : 
    2500        9299 :     int nFID32Bit = (nFID64Bit > 0) ? static_cast<int>(nFID64Bit) : 0;
    2501             : 
    2502        9299 :     poFeature->FillUnsetWithDefault(FALSE, nullptr);
    2503             : 
    2504        9299 :     const OGRGeometry *poGeom = nullptr;
    2505       18598 :     std::vector<OGRField> fields;
    2506        9299 :     if (!PrepareFileGDBFeature(poFeature, fields, poGeom, /*bUpdate=*/false))
    2507          17 :         return OGRERR_FAILURE;
    2508             : 
    2509        9282 :     m_eSpatialIndexState = SPI_INVALID;
    2510        9282 :     m_nFilteredFeatureCount = -1;
    2511             : 
    2512        9282 :     if (!m_poLyrTable->CreateFeature(fields, poGeom, &nFID32Bit))
    2513           6 :         return OGRERR_FAILURE;
    2514             : 
    2515        9276 :     poFeature->SetFID(nFID32Bit);
    2516        9276 :     return OGRERR_NONE;
    2517             : }
    2518             : 
    2519             : /************************************************************************/
    2520             : /*                           ISetFeature()                              */
    2521             : /************************************************************************/
    2522             : 
    2523          98 : OGRErr OGROpenFileGDBLayer::ISetFeature(OGRFeature *poFeature)
    2524             : {
    2525          98 :     if (!m_bEditable)
    2526          86 :         return OGRERR_FAILURE;
    2527             : 
    2528          12 :     if (!BuildLayerDefinition())
    2529           0 :         return OGRERR_FAILURE;
    2530             : 
    2531          12 :     if (m_poDS->IsInTransaction() && !m_bHasCreatedBackupForTransaction &&
    2532           0 :         !BeginEmulatedTransaction())
    2533             :     {
    2534           0 :         return OGRERR_FAILURE;
    2535             :     }
    2536             : 
    2537             :     /* In case the FID column has also been created as a regular field */
    2538          15 :     if (m_iFIDAsRegularColumnIndex >= 0 &&
    2539           3 :         !CheckFIDAndFIDColumnConsistency(poFeature, m_iFIDAsRegularColumnIndex))
    2540             :     {
    2541           0 :         return OGRERR_FAILURE;
    2542             :     }
    2543             : 
    2544          12 :     const GIntBig nFID = poFeature->GetFID();
    2545          12 :     if (nFID <= 0 || !CPL_INT64_FITS_ON_INT32(nFID))
    2546           1 :         return OGRERR_NON_EXISTING_FEATURE;
    2547             : 
    2548          11 :     const int nFID32Bit = static_cast<int>(nFID);
    2549          11 :     if (nFID32Bit > m_poLyrTable->GetTotalRecordCount())
    2550           1 :         return OGRERR_NON_EXISTING_FEATURE;
    2551          10 :     if (!m_poLyrTable->SelectRow(nFID32Bit - 1))
    2552           1 :         return OGRERR_NON_EXISTING_FEATURE;
    2553             : 
    2554           9 :     const OGRGeometry *poGeom = nullptr;
    2555          18 :     std::vector<OGRField> fields;
    2556           9 :     if (!PrepareFileGDBFeature(poFeature, fields, poGeom, /*bUpdate=*/true))
    2557           0 :         return OGRERR_FAILURE;
    2558             : 
    2559           9 :     m_eSpatialIndexState = SPI_INVALID;
    2560           9 :     m_nFilteredFeatureCount = -1;
    2561             : 
    2562           9 :     if (!m_poLyrTable->UpdateFeature(nFID32Bit, fields, poGeom))
    2563           0 :         return OGRERR_FAILURE;
    2564             : 
    2565           9 :     return OGRERR_NONE;
    2566             : }
    2567             : 
    2568             : /************************************************************************/
    2569             : /*                           DeleteFeature()                            */
    2570             : /************************************************************************/
    2571             : 
    2572        2793 : OGRErr OGROpenFileGDBLayer::DeleteFeature(GIntBig nFID)
    2573             : 
    2574             : {
    2575        2793 :     if (!m_bEditable)
    2576         178 :         return OGRERR_FAILURE;
    2577             : 
    2578        2615 :     if (!BuildLayerDefinition())
    2579           0 :         return OGRERR_FAILURE;
    2580             : 
    2581        2615 :     if (m_poDS->IsInTransaction() && !m_bHasCreatedBackupForTransaction &&
    2582           0 :         !BeginEmulatedTransaction())
    2583             :     {
    2584           0 :         return OGRERR_FAILURE;
    2585             :     }
    2586             : 
    2587        2615 :     if (nFID <= 0 || !CPL_INT64_FITS_ON_INT32(nFID))
    2588           2 :         return OGRERR_NON_EXISTING_FEATURE;
    2589             : 
    2590        2613 :     const int nFID32Bit = static_cast<int>(nFID);
    2591        2613 :     if (nFID32Bit > m_poLyrTable->GetTotalRecordCount())
    2592           1 :         return OGRERR_NON_EXISTING_FEATURE;
    2593        2612 :     if (!m_poLyrTable->SelectRow(nFID32Bit - 1))
    2594           1 :         return OGRERR_NON_EXISTING_FEATURE;
    2595             : 
    2596        2611 :     m_eSpatialIndexState = SPI_INVALID;
    2597        2611 :     m_nFilteredFeatureCount = -1;
    2598             : 
    2599        2611 :     return m_poLyrTable->DeleteFeature(nFID32Bit) ? OGRERR_NONE
    2600        2611 :                                                   : OGRERR_FAILURE;
    2601             : }
    2602             : 
    2603             : /************************************************************************/
    2604             : /*                     RefreshXMLDefinitionInMemory()                   */
    2605             : /************************************************************************/
    2606             : 
    2607         581 : void OGROpenFileGDBLayer::RefreshXMLDefinitionInMemory()
    2608             : {
    2609         581 :     CPLXMLTreeCloser oTree(CPLCreateXMLNode(nullptr, CXT_Element, "?xml"));
    2610         581 :     CPLAddXMLAttributeAndValue(oTree.get(), "version", "1.0");
    2611         581 :     CPLAddXMLAttributeAndValue(oTree.get(), "encoding", "UTF-8");
    2612             : 
    2613             :     CPLXMLNode *psRoot =
    2614         581 :         CPLCreateXMLNode(nullptr, CXT_Element,
    2615         581 :                          m_eGeomType == wkbNone ? "typens:DETableInfo"
    2616             :                                                 : "typens:DEFeatureClassInfo");
    2617         581 :     CPLAddXMLSibling(oTree.get(), psRoot);
    2618             : 
    2619         581 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:typens",
    2620         581 :                                m_bArcGISPro32OrLater
    2621             :                                    ? "http://www.esri.com/schemas/ArcGIS/10.8"
    2622             :                                    : "http://www.esri.com/schemas/ArcGIS/10.3");
    2623         581 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xsi",
    2624             :                                "http://www.w3.org/2001/XMLSchema-instance");
    2625         581 :     CPLAddXMLAttributeAndValue(psRoot, "xmlns:xs",
    2626             :                                "http://www.w3.org/2001/XMLSchema");
    2627         581 :     CPLAddXMLAttributeAndValue(psRoot, "xsi:type",
    2628         581 :                                m_eGeomType == wkbNone
    2629             :                                    ? "typens:DETableInfo"
    2630             :                                    : "typens:DEFeatureClassInfo");
    2631         581 :     CPLCreateXMLElementAndValue(psRoot, "CatalogPath", m_osPath.c_str());
    2632         581 :     CPLCreateXMLElementAndValue(psRoot, "Name", m_osName.c_str());
    2633         581 :     CPLCreateXMLElementAndValue(psRoot, "ChildrenExpanded", "false");
    2634         581 :     CPLCreateXMLElementAndValue(psRoot, "DatasetType",
    2635         581 :                                 m_eGeomType == wkbNone ? "esriDTTable"
    2636             :                                                        : "esriDTFeatureClass");
    2637             : 
    2638             :     {
    2639         581 :         FileGDBTable oTable;
    2640         581 :         if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), false))
    2641           0 :             return;
    2642         581 :         CPLCreateXMLElementAndValue(
    2643             :             psRoot, "DSID",
    2644         581 :             CPLSPrintf("%" PRId64, 1 + oTable.GetTotalRecordCount()));
    2645             :     }
    2646             : 
    2647         581 :     CPLCreateXMLElementAndValue(psRoot, "Versioned", "false");
    2648         581 :     CPLCreateXMLElementAndValue(psRoot, "CanVersion", "false");
    2649         581 :     if (!m_osConfigurationKeyword.empty())
    2650             :     {
    2651           2 :         CPLCreateXMLElementAndValue(psRoot, "ConfigurationKeyword",
    2652             :                                     m_osConfigurationKeyword.c_str());
    2653             :     }
    2654         581 :     if (m_bArcGISPro32OrLater)
    2655             :     {
    2656          12 :         CPLCreateXMLElementAndValue(psRoot, "RequiredGeodatabaseClientVersion",
    2657             :                                     "13.2");
    2658             :     }
    2659         581 :     CPLCreateXMLElementAndValue(psRoot, "HasOID", "true");
    2660         581 :     CPLCreateXMLElementAndValue(psRoot, "OIDFieldName", GetFIDColumn());
    2661             :     auto GPFieldInfoExs =
    2662         581 :         CPLCreateXMLNode(psRoot, CXT_Element, "GPFieldInfoExs");
    2663         581 :     CPLAddXMLAttributeAndValue(GPFieldInfoExs, "xsi:type",
    2664             :                                "typens:ArrayOfGPFieldInfoEx");
    2665             : 
    2666        2465 :     for (int i = 0; i < m_poLyrTable->GetFieldCount(); ++i)
    2667             :     {
    2668        1884 :         const auto *poGDBFieldDefn = m_poLyrTable->GetField(i);
    2669        1884 :         if (poGDBFieldDefn->GetType() == FGFT_OBJECTID)
    2670             :         {
    2671             :             auto GPFieldInfoEx =
    2672         581 :                 CPLCreateXMLNode(GPFieldInfoExs, CXT_Element, "GPFieldInfoEx");
    2673         581 :             CPLAddXMLAttributeAndValue(GPFieldInfoEx, "xsi:type",
    2674             :                                        "typens:GPFieldInfoEx");
    2675         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Name",
    2676         581 :                                         poGDBFieldDefn->GetName().c_str());
    2677         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "FieldType",
    2678             :                                         "esriFieldTypeOID");
    2679         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "IsNullable", "false");
    2680         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Length", "4");
    2681         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Precision", "0");
    2682         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Scale", "0");
    2683         581 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Required", "true");
    2684             :         }
    2685        1303 :         else if (poGDBFieldDefn->GetType() == FGFT_GEOMETRY)
    2686             :         {
    2687             :             auto GPFieldInfoEx =
    2688         433 :                 CPLCreateXMLNode(GPFieldInfoExs, CXT_Element, "GPFieldInfoEx");
    2689         433 :             CPLAddXMLAttributeAndValue(GPFieldInfoEx, "xsi:type",
    2690             :                                        "typens:GPFieldInfoEx");
    2691         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Name",
    2692         433 :                                         poGDBFieldDefn->GetName().c_str());
    2693         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "FieldType",
    2694             :                                         "esriFieldTypeGeometry");
    2695         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "IsNullable",
    2696         433 :                                         poGDBFieldDefn->IsNullable() ? "true"
    2697             :                                                                      : "false");
    2698         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Length", "0");
    2699         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Precision", "0");
    2700         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Scale", "0");
    2701         433 :             CPLCreateXMLElementAndValue(GPFieldInfoEx, "Required", "true");
    2702             :         }
    2703             :         else
    2704             :         {
    2705         870 :             const int nOGRIdx = m_poFeatureDefn->GetFieldIndex(
    2706         870 :                 poGDBFieldDefn->GetName().c_str());
    2707         870 :             if (nOGRIdx >= 0)
    2708             :             {
    2709         870 :                 const auto poFieldDefn = m_poFeatureDefn->GetFieldDefn(nOGRIdx);
    2710         870 :                 CPLAddXMLChild(GPFieldInfoExs, CreateXMLFieldDefinition(
    2711             :                                                    poFieldDefn, poGDBFieldDefn,
    2712         870 :                                                    m_bArcGISPro32OrLater));
    2713             :             }
    2714             :         }
    2715             :     }
    2716             : 
    2717         581 :     CPLCreateXMLElementAndValue(psRoot, "CLSID",
    2718         581 :                                 m_eGeomType == wkbNone
    2719             :                                     ? "{7A566981-C114-11D2-8A28-006097AFF44E}"
    2720             :                                     : "{52353152-891A-11D0-BEC6-00805F7C4268}");
    2721         581 :     CPLCreateXMLElementAndValue(psRoot, "EXTCLSID", "");
    2722             : 
    2723             :     const char *pszLayerAlias =
    2724         581 :         m_aosCreationOptions.FetchNameValue("LAYER_ALIAS");
    2725         581 :     if (pszLayerAlias != nullptr)
    2726             :     {
    2727           1 :         CPLCreateXMLElementAndValue(psRoot, "AliasName", pszLayerAlias);
    2728             :     }
    2729             : 
    2730         581 :     CPLCreateXMLElementAndValue(psRoot, "IsTimeInUTC",
    2731         581 :                                 m_bTimeInUTC ? "true" : " false");
    2732             : 
    2733         581 :     if (m_eGeomType != wkbNone)
    2734             :     {
    2735         433 :         const auto poGeomFieldDefn = m_poLyrTable->GetGeomField();
    2736         433 :         CPLCreateXMLElementAndValue(psRoot, "FeatureType", "esriFTSimple");
    2737             : 
    2738         433 :         const char *pszShapeType = "";
    2739         433 :         switch (m_poLyrTable->GetGeometryType())
    2740             :         {
    2741           0 :             case FGTGT_NONE:
    2742           0 :                 break;
    2743         254 :             case FGTGT_POINT:
    2744         254 :                 pszShapeType = "esriGeometryPoint";
    2745         254 :                 break;
    2746          23 :             case FGTGT_MULTIPOINT:
    2747          23 :                 pszShapeType = "esriGeometryMultipoint";
    2748          23 :                 break;
    2749          68 :             case FGTGT_LINE:
    2750          68 :                 pszShapeType = "esriGeometryPolyline";
    2751          68 :                 break;
    2752          79 :             case FGTGT_POLYGON:
    2753          79 :                 pszShapeType = "esriGeometryPolygon";
    2754          79 :                 break;
    2755           9 :             case FGTGT_MULTIPATCH:
    2756           9 :                 pszShapeType = "esriGeometryMultiPatch";
    2757           9 :                 break;
    2758             :         }
    2759         433 :         CPLCreateXMLElementAndValue(psRoot, "ShapeType", pszShapeType);
    2760         433 :         CPLCreateXMLElementAndValue(psRoot, "ShapeFieldName",
    2761         433 :                                     poGeomFieldDefn->GetName().c_str());
    2762             : 
    2763         433 :         const bool bGeomTypeHasZ = CPL_TO_BOOL(OGR_GT_HasZ(m_eGeomType));
    2764         433 :         const bool bGeomTypeHasM = CPL_TO_BOOL(OGR_GT_HasM(m_eGeomType));
    2765         433 :         CPLCreateXMLElementAndValue(psRoot, "HasM",
    2766             :                                     bGeomTypeHasM ? "true" : "false");
    2767         433 :         CPLCreateXMLElementAndValue(psRoot, "HasZ",
    2768             :                                     bGeomTypeHasZ ? "true" : "false");
    2769         433 :         CPLCreateXMLElementAndValue(psRoot, "HasSpatialIndex", "false");
    2770             :         const char *pszAreaFieldName =
    2771         433 :             m_iAreaField >= 0
    2772         433 :                 ? m_poFeatureDefn->GetFieldDefn(m_iAreaField)->GetNameRef()
    2773         433 :                 : "";
    2774         433 :         CPLCreateXMLElementAndValue(psRoot, "AreaFieldName", pszAreaFieldName);
    2775             :         const char *pszLengthFieldName =
    2776         433 :             m_iLengthField >= 0
    2777         433 :                 ? m_poFeatureDefn->GetFieldDefn(m_iLengthField)->GetNameRef()
    2778         433 :                 : "";
    2779         433 :         CPLCreateXMLElementAndValue(psRoot, "LengthFieldName",
    2780             :                                     pszLengthFieldName);
    2781             : 
    2782         433 :         XMLSerializeGeomFieldBase(psRoot, poGeomFieldDefn, GetSpatialRef());
    2783             :     }
    2784             : 
    2785         581 :     char *pszDefinition = CPLSerializeXMLTree(oTree.get());
    2786         581 :     m_osDefinition = pszDefinition;
    2787         581 :     CPLFree(pszDefinition);
    2788             : }
    2789             : 
    2790             : /************************************************************************/
    2791             : /*                            RegisterTable()                           */
    2792             : /************************************************************************/
    2793             : 
    2794         268 : bool OGROpenFileGDBLayer::RegisterTable()
    2795             : {
    2796         268 :     m_bRegisteredTable = true;
    2797             : 
    2798         268 :     CPLAssert(!m_osThisGUID.empty());
    2799             : 
    2800             :     const char *pszFeatureDataset =
    2801         268 :         m_aosCreationOptions.FetchNameValue("FEATURE_DATASET");
    2802         268 :     if (pszFeatureDataset)
    2803             :     {
    2804          14 :         if (!m_poDS->RegisterInItemRelationships(
    2805           7 :                 m_osFeatureDatasetGUID, m_osThisGUID,
    2806             :                 pszDatasetInFeatureDatasetUUID))
    2807             :         {
    2808           0 :             return false;
    2809             :         }
    2810             :     }
    2811             :     else
    2812             :     {
    2813         522 :         if (!m_poDS->RegisterInItemRelationships(m_poDS->m_osRootGUID,
    2814         261 :                                                  m_osThisGUID,
    2815             :                                                  // DatasetInFolder
    2816             :                                                  pszDatasetInFolderUUID))
    2817             :         {
    2818           0 :             return false;
    2819             :         }
    2820             :     }
    2821             : 
    2822         268 :     if (m_eGeomType != wkbNone)
    2823             :     {
    2824         195 :         return m_poDS->RegisterFeatureClassInItems(
    2825         195 :             m_osThisGUID, m_osName, m_osPath, m_poLyrTable,
    2826         195 :             m_osDefinition.c_str(), m_osDocumentation.c_str());
    2827             :     }
    2828             :     else
    2829             :     {
    2830          73 :         return m_poDS->RegisterASpatialTableInItems(
    2831          73 :             m_osThisGUID, m_osName, m_osPath, m_osDefinition.c_str(),
    2832          73 :             m_osDocumentation.c_str());
    2833             :     }
    2834             : }
    2835             : 
    2836             : /************************************************************************/
    2837             : /*                             SyncToDisk()                             */
    2838             : /************************************************************************/
    2839             : 
    2840       11525 : OGRErr OGROpenFileGDBLayer::SyncToDisk()
    2841             : {
    2842       11525 :     if (!m_bEditable || m_poLyrTable == nullptr)
    2843       10806 :         return OGRERR_NONE;
    2844             : 
    2845         719 :     if (!m_bRegisteredTable && !RegisterTable())
    2846           0 :         return OGRERR_FAILURE;
    2847             : 
    2848         719 :     return m_poLyrTable->Sync() ? OGRERR_NONE : OGRERR_FAILURE;
    2849             : }
    2850             : 
    2851             : /************************************************************************/
    2852             : /*                        CreateSpatialIndex()                          */
    2853             : /************************************************************************/
    2854             : 
    2855           0 : void OGROpenFileGDBLayer::CreateSpatialIndex()
    2856             : {
    2857           0 :     if (!m_bEditable)
    2858           0 :         return;
    2859             : 
    2860           0 :     if (!BuildLayerDefinition())
    2861           0 :         return;
    2862             : 
    2863           0 :     m_poLyrTable->CreateSpatialIndex();
    2864             : }
    2865             : 
    2866             : /************************************************************************/
    2867             : /*                           CreateIndex()                              */
    2868             : /************************************************************************/
    2869             : 
    2870          19 : void OGROpenFileGDBLayer::CreateIndex(const std::string &osIdxName,
    2871             :                                       const std::string &osExpression)
    2872             : {
    2873          19 :     if (!m_bEditable)
    2874           1 :         return;
    2875             : 
    2876          19 :     if (!BuildLayerDefinition())
    2877           0 :         return;
    2878             : 
    2879          19 :     const auto wIdxName = StringToWString(osIdxName);
    2880          19 :     if (EscapeReservedKeywords(wIdxName) != wIdxName)
    2881             :     {
    2882           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2883             :                  "Invalid index name: must not be a reserved keyword");
    2884           1 :         return;
    2885             :     }
    2886             : 
    2887          18 :     m_poLyrTable->CreateIndex(osIdxName, osExpression);
    2888             : }
    2889             : 
    2890             : /************************************************************************/
    2891             : /*                                Repack()                              */
    2892             : /************************************************************************/
    2893             : 
    2894           4 : bool OGROpenFileGDBLayer::Repack()
    2895             : {
    2896           4 :     if (!m_bEditable)
    2897           0 :         return false;
    2898             : 
    2899           4 :     if (!BuildLayerDefinition())
    2900           0 :         return false;
    2901             : 
    2902           4 :     return m_poLyrTable->Repack();
    2903             : }
    2904             : 
    2905             : /************************************************************************/
    2906             : /*                        RecomputeExtent()                             */
    2907             : /************************************************************************/
    2908             : 
    2909           2 : void OGROpenFileGDBLayer::RecomputeExtent()
    2910             : {
    2911           2 :     if (!m_bEditable)
    2912           0 :         return;
    2913             : 
    2914           2 :     if (!BuildLayerDefinition())
    2915           0 :         return;
    2916             : 
    2917           2 :     m_poLyrTable->RecomputeExtent();
    2918             : }
    2919             : 
    2920             : /************************************************************************/
    2921             : /*                        CheckFreeListConsistency()                    */
    2922             : /************************************************************************/
    2923             : 
    2924           8 : bool OGROpenFileGDBLayer::CheckFreeListConsistency()
    2925             : {
    2926           8 :     if (!BuildLayerDefinition())
    2927           0 :         return false;
    2928             : 
    2929           8 :     return m_poLyrTable->CheckFreeListConsistency();
    2930             : }
    2931             : 
    2932             : /************************************************************************/
    2933             : /*                        BeginEmulatedTransaction()                    */
    2934             : /************************************************************************/
    2935             : 
    2936           8 : bool OGROpenFileGDBLayer::BeginEmulatedTransaction()
    2937             : {
    2938           8 :     if (!BuildLayerDefinition())
    2939           0 :         return false;
    2940             : 
    2941           8 :     if (SyncToDisk() != OGRERR_NONE)
    2942           0 :         return false;
    2943             : 
    2944           8 :     bool bRet = true;
    2945             : 
    2946          16 :     const std::string osThisDirname = CPLGetPath(m_osGDBFilename.c_str());
    2947           8 :     const std::string osThisBasename = CPLGetBasename(m_osGDBFilename.c_str());
    2948           8 :     char **papszFiles = VSIReadDir(osThisDirname.c_str());
    2949         193 :     for (char **papszIter = papszFiles;
    2950         193 :          papszIter != nullptr && *papszIter != nullptr; ++papszIter)
    2951             :     {
    2952         370 :         const std::string osBasename = CPLGetBasename(*papszIter);
    2953         185 :         if (osBasename == osThisBasename)
    2954             :         {
    2955             :             std::string osDestFilename = CPLFormFilename(
    2956          48 :                 m_poDS->GetBackupDirName().c_str(), *papszIter, nullptr);
    2957             :             std::string osSourceFilename =
    2958          48 :                 CPLFormFilename(osThisDirname.c_str(), *papszIter, nullptr);
    2959          24 :             if (CPLCopyFile(osDestFilename.c_str(), osSourceFilename.c_str()) !=
    2960             :                 0)
    2961             :             {
    2962           0 :                 bRet = false;
    2963             :             }
    2964             :         }
    2965             :     }
    2966           8 :     CSLDestroy(papszFiles);
    2967             : 
    2968           8 :     m_bHasCreatedBackupForTransaction = true;
    2969             : 
    2970           8 :     m_poFeatureDefnBackup.reset(m_poFeatureDefn->Clone());
    2971             : 
    2972           8 :     return bRet;
    2973             : }
    2974             : 
    2975             : /************************************************************************/
    2976             : /*                        CommitEmulatedTransaction()                   */
    2977             : /************************************************************************/
    2978             : 
    2979           3 : bool OGROpenFileGDBLayer::CommitEmulatedTransaction()
    2980             : {
    2981           3 :     m_poFeatureDefnBackup.reset();
    2982             : 
    2983           3 :     m_bHasCreatedBackupForTransaction = false;
    2984           3 :     return true;
    2985             : }
    2986             : 
    2987             : /************************************************************************/
    2988             : /*                        RollbackEmulatedTransaction()                 */
    2989             : /************************************************************************/
    2990             : 
    2991           6 : bool OGROpenFileGDBLayer::RollbackEmulatedTransaction()
    2992             : {
    2993           6 :     if (!m_bHasCreatedBackupForTransaction)
    2994           2 :         return true;
    2995             : 
    2996           4 :     SyncToDisk();
    2997             : 
    2998             :     // Restore feature definition
    2999           8 :     if (m_poFeatureDefnBackup != nullptr &&
    3000           4 :         !m_poFeatureDefn->IsSame(m_poFeatureDefnBackup.get()))
    3001             :     {
    3002           2 :         auto oTemporaryUnsealer(m_poFeatureDefn->GetTemporaryUnsealer());
    3003             :         {
    3004           1 :             const int nFieldCount = m_poFeatureDefn->GetFieldCount();
    3005           2 :             for (int i = nFieldCount - 1; i >= 0; i--)
    3006           1 :                 m_poFeatureDefn->DeleteFieldDefn(i);
    3007             :         }
    3008             :         {
    3009           1 :             const int nFieldCount = m_poFeatureDefnBackup->GetFieldCount();
    3010           3 :             for (int i = 0; i < nFieldCount; i++)
    3011           4 :                 m_poFeatureDefn->AddFieldDefn(
    3012           2 :                     m_poFeatureDefnBackup->GetFieldDefn(i));
    3013             :         }
    3014             :     }
    3015           4 :     m_poFeatureDefnBackup.reset();
    3016             : 
    3017           4 :     Close();
    3018             : 
    3019           4 :     bool bRet = true;
    3020             : 
    3021           8 :     const std::string osThisDirname = CPLGetPath(m_osGDBFilename.c_str());
    3022           4 :     const std::string osThisBasename = CPLGetBasename(m_osGDBFilename.c_str());
    3023             : 
    3024             :     // Delete files in working directory that match our basename
    3025             :     {
    3026           4 :         char **papszFiles = VSIReadDir(osThisDirname.c_str());
    3027          93 :         for (char **papszIter = papszFiles;
    3028          93 :              papszIter != nullptr && *papszIter != nullptr; ++papszIter)
    3029             :         {
    3030         178 :             const std::string osBasename = CPLGetBasename(*papszIter);
    3031          89 :             if (osBasename == osThisBasename)
    3032             :             {
    3033             :                 std::string osDestFilename =
    3034          18 :                     CPLFormFilename(osThisDirname.c_str(), *papszIter, nullptr);
    3035           9 :                 VSIUnlink(osDestFilename.c_str());
    3036             :             }
    3037             :         }
    3038           4 :         CSLDestroy(papszFiles);
    3039             :     }
    3040             : 
    3041             :     // Restore backup files
    3042           4 :     bool bBackupFound = false;
    3043             :     {
    3044           4 :         char **papszFiles = VSIReadDir(m_poDS->GetBackupDirName().c_str());
    3045          66 :         for (char **papszIter = papszFiles;
    3046          66 :              papszIter != nullptr && *papszIter != nullptr; ++papszIter)
    3047             :         {
    3048         124 :             const std::string osBasename = CPLGetBasename(*papszIter);
    3049          62 :             if (osBasename == osThisBasename)
    3050             :             {
    3051          12 :                 bBackupFound = true;
    3052             :                 std::string osDestFilename =
    3053          24 :                     CPLFormFilename(osThisDirname.c_str(), *papszIter, nullptr);
    3054             :                 std::string osSourceFilename = CPLFormFilename(
    3055          24 :                     m_poDS->GetBackupDirName().c_str(), *papszIter, nullptr);
    3056          12 :                 if (CPLCopyFile(osDestFilename.c_str(),
    3057          12 :                                 osSourceFilename.c_str()) != 0)
    3058             :                 {
    3059           0 :                     bRet = false;
    3060             :                 }
    3061             :             }
    3062             :         }
    3063           4 :         CSLDestroy(papszFiles);
    3064             :     }
    3065             : 
    3066           4 :     if (bBackupFound)
    3067             :     {
    3068           4 :         m_poLyrTable = new FileGDBTable();
    3069           4 :         if (m_poLyrTable->Open(m_osGDBFilename, m_bEditable, GetDescription()))
    3070             :         {
    3071           4 :             if (m_iGeomFieldIdx >= 0)
    3072             :             {
    3073           1 :                 m_iGeomFieldIdx = m_poLyrTable->GetGeomFieldIdx();
    3074           1 :                 if (m_iGeomFieldIdx < 0)
    3075             :                 {
    3076           0 :                     Close();
    3077           0 :                     bRet = false;
    3078             :                 }
    3079             :                 else
    3080             :                 {
    3081           1 :                     m_bValidLayerDefn = TRUE;
    3082             :                 }
    3083             :             }
    3084             :             else
    3085             :             {
    3086           3 :                 m_bValidLayerDefn = TRUE;
    3087             :             }
    3088             :         }
    3089             :         else
    3090             :         {
    3091           0 :             Close();
    3092           0 :             bRet = false;
    3093             :         }
    3094             :     }
    3095             : 
    3096           4 :     m_bHasCreatedBackupForTransaction = false;
    3097             : 
    3098           4 :     delete m_poAttributeIterator;
    3099           4 :     m_poAttributeIterator = nullptr;
    3100             : 
    3101           4 :     delete m_poIterMinMax;
    3102           4 :     m_poIterMinMax = nullptr;
    3103             : 
    3104           4 :     delete m_poSpatialIndexIterator;
    3105           4 :     m_poSpatialIndexIterator = nullptr;
    3106             : 
    3107           4 :     delete m_poCombinedIterator;
    3108           4 :     m_poCombinedIterator = nullptr;
    3109             : 
    3110           4 :     if (m_pQuadTree != nullptr)
    3111           0 :         CPLQuadTreeDestroy(m_pQuadTree);
    3112           4 :     m_pQuadTree = nullptr;
    3113             : 
    3114           4 :     CPLFree(m_pahFilteredFeatures);
    3115           4 :     m_pahFilteredFeatures = nullptr;
    3116             : 
    3117           4 :     m_nFilteredFeatureCount = -1;
    3118             : 
    3119           4 :     m_eSpatialIndexState = SPI_INVALID;
    3120             : 
    3121           4 :     if (m_poLyrTable && m_iGeomFieldIdx >= 0)
    3122             :     {
    3123           1 :         m_poGeomConverter.reset(FileGDBOGRGeometryConverter::BuildConverter(
    3124           1 :             m_poLyrTable->GetGeomField()));
    3125             :     }
    3126             : 
    3127           4 :     return bRet;
    3128             : }
    3129             : 
    3130             : /************************************************************************/
    3131             : /*                           Rename()                                   */
    3132             : /************************************************************************/
    3133             : 
    3134          10 : OGRErr OGROpenFileGDBLayer::Rename(const char *pszDstTableName)
    3135             : {
    3136          10 :     if (!m_bEditable)
    3137           0 :         return OGRERR_FAILURE;
    3138             : 
    3139          10 :     if (!BuildLayerDefinition())
    3140           0 :         return OGRERR_FAILURE;
    3141             : 
    3142          10 :     if (SyncToDisk() != OGRERR_NONE)
    3143           0 :         return OGRERR_FAILURE;
    3144             : 
    3145          10 :     if (m_poDS->IsInTransaction() &&
    3146           0 :         ((!m_bHasCreatedBackupForTransaction && !BeginEmulatedTransaction()) ||
    3147           0 :          !m_poDS->BackupSystemTablesForTransaction()))
    3148             :     {
    3149           0 :         return OGRERR_FAILURE;
    3150             :     }
    3151             : 
    3152          30 :     const std::string osLaunderedName(GetLaunderedLayerName(pszDstTableName));
    3153          10 :     if (pszDstTableName != osLaunderedName)
    3154             :     {
    3155           6 :         CPLError(CE_Failure, CPLE_AppDefined,
    3156             :                  "%s is not a valid layer name. %s would be a valid one.",
    3157             :                  pszDstTableName, osLaunderedName.c_str());
    3158           6 :         return OGRERR_FAILURE;
    3159             :     }
    3160             : 
    3161           4 :     if (m_poDS->GetLayerByName(pszDstTableName) != nullptr)
    3162             :     {
    3163           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Layer %s already exists",
    3164             :                  pszDstTableName);
    3165           0 :         return OGRERR_FAILURE;
    3166             :     }
    3167             : 
    3168           8 :     const std::string osOldName(m_osName);
    3169             : 
    3170           4 :     m_osName = pszDstTableName;
    3171           4 :     SetDescription(pszDstTableName);
    3172           4 :     whileUnsealing(m_poFeatureDefn)->SetName(pszDstTableName);
    3173             : 
    3174           4 :     auto nLastSlashPos = m_osPath.rfind('\\');
    3175           4 :     if (nLastSlashPos != std::string::npos)
    3176             :     {
    3177           4 :         m_osPath.resize(nLastSlashPos + 1);
    3178             :     }
    3179             :     else
    3180             :     {
    3181           0 :         m_osPath = '\\';
    3182             :     }
    3183           4 :     m_osPath += m_osName;
    3184             : 
    3185           4 :     RefreshXMLDefinitionInMemory();
    3186             : 
    3187             :     // Update GDB_SystemCatalog
    3188             :     {
    3189           4 :         FileGDBTable oTable;
    3190           4 :         if (!oTable.Open(m_poDS->m_osGDBSystemCatalogFilename.c_str(), true))
    3191           0 :             return OGRERR_FAILURE;
    3192             : 
    3193           4 :         FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
    3194             : 
    3195          40 :         for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
    3196             :              ++iCurFeat)
    3197             :         {
    3198          40 :             iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
    3199          40 :             if (iCurFeat < 0)
    3200           0 :                 break;
    3201          40 :             const auto psName = oTable.GetFieldValue(iName);
    3202          40 :             if (psName && psName->String == osOldName)
    3203             :             {
    3204           4 :                 auto asFields = oTable.GetAllFieldValues();
    3205             : 
    3206           4 :                 CPLFree(asFields[iName].String);
    3207           4 :                 asFields[iName].String = CPLStrdup(m_osName.c_str());
    3208             : 
    3209             :                 bool bRet =
    3210           8 :                     oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr) &&
    3211           4 :                     oTable.Sync();
    3212           4 :                 oTable.FreeAllFieldValues(asFields);
    3213           4 :                 if (!bRet)
    3214           0 :                     return OGRERR_FAILURE;
    3215           4 :                 break;
    3216             :             }
    3217             :         }
    3218             :     }
    3219             : 
    3220             :     // Update GDB_Items
    3221             :     {
    3222           4 :         FileGDBTable oTable;
    3223           4 :         if (!oTable.Open(m_poDS->m_osGDBItemsFilename.c_str(), true))
    3224           0 :             return OGRERR_FAILURE;
    3225             : 
    3226           4 :         FETCH_FIELD_IDX_WITH_RET(iName, "Name", FGFT_STRING, OGRERR_FAILURE);
    3227           4 :         FETCH_FIELD_IDX_WITH_RET(iPath, "Path", FGFT_STRING, OGRERR_FAILURE);
    3228           4 :         FETCH_FIELD_IDX_WITH_RET(iPhysicalName, "PhysicalName", FGFT_STRING,
    3229             :                                  OGRERR_FAILURE);
    3230           4 :         FETCH_FIELD_IDX_WITH_RET(iDefinition, "Definition", FGFT_XML,
    3231             :                                  OGRERR_FAILURE);
    3232             : 
    3233          18 :         for (int64_t iCurFeat = 0; iCurFeat < oTable.GetTotalRecordCount();
    3234             :              ++iCurFeat)
    3235             :         {
    3236          18 :             iCurFeat = oTable.GetAndSelectNextNonEmptyRow(iCurFeat);
    3237          18 :             if (iCurFeat < 0)
    3238           0 :                 break;
    3239          18 :             const auto psName = oTable.GetFieldValue(iName);
    3240          18 :             if (psName && psName->String == osOldName)
    3241             :             {
    3242           4 :                 auto asFields = oTable.GetAllFieldValues();
    3243             : 
    3244           4 :                 CPLFree(asFields[iName].String);
    3245           4 :                 asFields[iName].String = CPLStrdup(m_osName.c_str());
    3246             : 
    3247           8 :                 if (!OGR_RawField_IsNull(&asFields[iPath]) &&
    3248           4 :                     !OGR_RawField_IsUnset(&asFields[iPath]))
    3249             :                 {
    3250           4 :                     CPLFree(asFields[iPath].String);
    3251             :                 }
    3252           4 :                 asFields[iPath].String = CPLStrdup(m_osPath.c_str());
    3253             : 
    3254           8 :                 if (!OGR_RawField_IsNull(&asFields[iPhysicalName]) &&
    3255           4 :                     !OGR_RawField_IsUnset(&asFields[iPhysicalName]))
    3256             :                 {
    3257           4 :                     CPLFree(asFields[iPhysicalName].String);
    3258             :                 }
    3259           4 :                 CPLString osUCName(m_osName);
    3260           4 :                 osUCName.toupper();
    3261           4 :                 asFields[iPhysicalName].String = CPLStrdup(osUCName.c_str());
    3262             : 
    3263           8 :                 if (!OGR_RawField_IsNull(&asFields[iDefinition]) &&
    3264           4 :                     !OGR_RawField_IsUnset(&asFields[iDefinition]))
    3265             :                 {
    3266           4 :                     CPLFree(asFields[iDefinition].String);
    3267             :                 }
    3268           8 :                 asFields[iDefinition].String =
    3269           4 :                     CPLStrdup(m_osDefinition.c_str());
    3270             : 
    3271             :                 bool bRet =
    3272           8 :                     oTable.UpdateFeature(iCurFeat + 1, asFields, nullptr) &&
    3273           4 :                     oTable.Sync();
    3274           4 :                 oTable.FreeAllFieldValues(asFields);
    3275           4 :                 if (!bRet)
    3276           0 :                     return OGRERR_FAILURE;
    3277           4 :                 break;
    3278             :             }
    3279             :         }
    3280             :     }
    3281             : 
    3282           4 :     return OGRERR_NONE;
    3283             : }

Generated by: LCOV version 1.14