LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdbtable_write_fields.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 452 527 85.8 %
Date: 2024-05-02 22:57:13 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements management of FileGDB field write support
       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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_port.h"
      30             : 
      31             : #include "filegdbtable.h"
      32             : #include "filegdbtable_priv.h"
      33             : 
      34             : #include <algorithm>
      35             : #include <limits>
      36             : 
      37             : #include "cpl_string.h"
      38             : #include "cpl_time.h"
      39             : 
      40             : #include "ogr_core.h"
      41             : #include "ogr_api.h"
      42             : 
      43             : namespace OpenFileGDB
      44             : {
      45             : 
      46             : /************************************************************************/
      47             : /*                            CreateField()                             */
      48             : /************************************************************************/
      49             : 
      50       12932 : bool FileGDBTable::CreateField(std::unique_ptr<FileGDBField> &&psField)
      51             : {
      52       12932 :     if (!m_bUpdate)
      53           0 :         return false;
      54             : 
      55             :     // Encoded on a uint16_t
      56       12932 :     if (m_apoFields.size() == 65535)
      57             :     {
      58           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Too many fields");
      59           0 :         return false;
      60             :     }
      61             : 
      62       12932 :     if (psField->GetType() == FGFT_RASTER)
      63             :     {
      64           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled field type");
      65           0 :         return false;
      66             :     }
      67             : 
      68       12932 :     if (GetFieldIdx(psField->GetName()) >= 0)
      69             :     {
      70           2 :         CPLError(CE_Failure, CPLE_NotSupported, "Field %s already exists",
      71           2 :                  psField->GetName().c_str());
      72           2 :         return false;
      73             :     }
      74             : 
      75       12930 :     if (psField->GetType() == FGFT_GEOMETRY)
      76             :     {
      77         417 :         if (m_iGeomField >= 0)
      78             :         {
      79           0 :             CPLError(CE_Failure, CPLE_NotSupported,
      80             :                      "Only one geometry field supported");
      81           0 :             return false;
      82             :         }
      83         417 :         m_iGeomField = static_cast<int>(m_apoFields.size());
      84             :         m_adfSpatialIndexGridResolution =
      85             :             cpl::down_cast<const FileGDBGeomField *>(psField.get())
      86         417 :                 ->GetSpatialIndexGridResolution();
      87             :     }
      88             : 
      89       12930 :     if (psField->GetType() == FGFT_OBJECTID)
      90             :     {
      91        1614 :         if (m_iObjectIdField >= 0)
      92             :         {
      93           0 :             CPLError(CE_Failure, CPLE_NotSupported,
      94             :                      "Only one ObjectId field supported");
      95           0 :             return false;
      96             :         }
      97        1614 :         m_iObjectIdField = static_cast<int>(m_apoFields.size());
      98             :     }
      99             : 
     100       12930 :     bool bRewriteTable = false;
     101       12930 :     if (m_nTotalRecordCount != 0)
     102             :     {
     103         134 :         const bool bHasDefault = !OGR_RawField_IsNull(psField->GetDefault()) &&
     104          67 :                                  !OGR_RawField_IsUnset(psField->GetDefault());
     105          67 :         if (psField->GetType() == FGFT_GEOMETRY)
     106             :         {
     107           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     108             :                      "Cannot add a geometry field to a non-empty table");
     109           0 :             return false;
     110             :         }
     111          67 :         else if (psField->GetType() == FGFT_OBJECTID)
     112             :         {
     113             :             // nothing to do but rewrite the feature definition
     114             :         }
     115          67 :         else if ((m_nCountNullableFields % 8) != 0 && psField->IsNullable())
     116             :         {
     117             :             // Adding a nullable field to a feature definition that has already
     118             :             // nullable fields, with the last bitmap byte not completely filled.
     119             :             // We just need to rewrite the feature definition, not the features.
     120             :         }
     121          45 :         else if (!psField->IsNullable() && !bHasDefault)
     122             :         {
     123           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     124             :                      "Cannot add non-nullable field without default value to "
     125             :                      "a non-empty table");
     126           1 :             return false;
     127             :         }
     128             :         else
     129             :         {
     130          44 :             bRewriteTable = true;
     131             :         }
     132             :     }
     133             : 
     134       12929 :     m_nCurRow = -1;
     135       12929 :     m_bDirtyFieldDescriptors = true;
     136       12929 :     const bool bIsNullable = psField->IsNullable();
     137       12929 :     if (bIsNullable)
     138             :     {
     139        7442 :         m_nCountNullableFields++;
     140        7442 :         m_nNullableFieldsSizeInBytes =
     141        7442 :             BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
     142             :     }
     143       12929 :     psField->SetParent(this);
     144       12929 :     m_apoFields.emplace_back(std::move(psField));
     145             : 
     146       12929 :     if (bRewriteTable && !RewriteTableToAddLastAddedField())
     147             :     {
     148           6 :         if (bIsNullable)
     149             :         {
     150           0 :             m_nCountNullableFields--;
     151           0 :             m_nNullableFieldsSizeInBytes =
     152           0 :                 BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
     153             :         }
     154           6 :         m_apoFields.resize(m_apoFields.size() - 1);
     155           6 :         m_bDirtyFieldDescriptors = true;
     156           6 :         return false;
     157             :     }
     158             : 
     159       12923 :     return true;
     160             : }
     161             : 
     162             : /************************************************************************/
     163             : /*                  RewriteTableToAddLastAddedField()                   */
     164             : /************************************************************************/
     165             : 
     166          44 : bool FileGDBTable::RewriteTableToAddLastAddedField()
     167             : {
     168          44 :     int nOldCountNullableFields = m_nCountNullableFields;
     169          44 :     if (m_apoFields.back()->IsNullable())
     170             :     {
     171           2 :         nOldCountNullableFields--;
     172             :     }
     173          44 :     const int nOldNullableFieldsSizeInBytes =
     174          44 :         BIT_ARRAY_SIZE_IN_BYTES(nOldCountNullableFields);
     175          44 :     int nExtraBytes = 0;
     176          44 :     if (nOldNullableFieldsSizeInBytes != m_nNullableFieldsSizeInBytes)
     177           2 :         nExtraBytes++;
     178          88 :     std::vector<GByte> abyDefaultVal;
     179             : 
     180          44 :     const auto &psLastField = m_apoFields.back();
     181          44 :     if (!psLastField->IsNullable())
     182             :     {
     183             :         const bool bHasDefault =
     184          84 :             !OGR_RawField_IsNull(psLastField->GetDefault()) &&
     185          42 :             !OGR_RawField_IsUnset(psLastField->GetDefault());
     186          42 :         CPL_IGNORE_RET_VAL(bHasDefault);
     187          42 :         CPLAssert(bHasDefault);
     188          42 :         if (psLastField->GetType() == FGFT_STRING)
     189             :         {
     190          12 :             const std::string osDefaultVal(psLastField->GetDefault()->String);
     191          12 :             WriteVarUInt(abyDefaultVal, osDefaultVal.size());
     192             :             abyDefaultVal.insert(
     193          12 :                 abyDefaultVal.end(),
     194          12 :                 reinterpret_cast<const GByte *>(osDefaultVal.c_str()),
     195          12 :                 reinterpret_cast<const GByte *>(osDefaultVal.c_str()) +
     196          24 :                     osDefaultVal.size());
     197             :         }
     198          30 :         else if (psLastField->GetType() == FGFT_INT16)
     199             :         {
     200           6 :             WriteInt16(abyDefaultVal, static_cast<int16_t>(
     201           6 :                                           psLastField->GetDefault()->Integer));
     202             :         }
     203          24 :         else if (psLastField->GetType() == FGFT_INT32)
     204             :         {
     205           6 :             WriteInt32(abyDefaultVal, psLastField->GetDefault()->Integer);
     206             :         }
     207          18 :         else if (psLastField->GetType() == FGFT_INT64)
     208             :         {
     209           0 :             WriteInt64(abyDefaultVal, psLastField->GetDefault()->Integer64);
     210             :         }
     211          18 :         else if (psLastField->GetType() == FGFT_FLOAT32)
     212             :         {
     213           6 :             WriteFloat32(abyDefaultVal,
     214           6 :                          static_cast<float>(psLastField->GetDefault()->Real));
     215             :         }
     216          12 :         else if (psLastField->GetType() == FGFT_FLOAT64)
     217             :         {
     218           6 :             WriteFloat64(abyDefaultVal, psLastField->GetDefault()->Real);
     219             :         }
     220           6 :         else if (psLastField->GetType() == FGFT_DATETIME ||
     221           0 :                  psLastField->GetType() == FGFT_DATE)
     222             :         {
     223           6 :             WriteFloat64(abyDefaultVal, FileGDBOGRDateToDoubleDate(
     224             :                                             psLastField->GetDefault(),
     225             :                                             /* bConvertToUTC = */ true,
     226           6 :                                             psLastField->IsHighPrecision()));
     227             :         }
     228           0 :         else if (psLastField->GetType() == FGFT_TIME)
     229             :         {
     230           0 :             WriteFloat64(abyDefaultVal,
     231             :                          FileGDBOGRTimeToDoubleTime(psLastField->GetDefault()));
     232             :         }
     233           0 :         else if (psLastField->GetType() == FGFT_DATETIME_WITH_OFFSET)
     234             :         {
     235           0 :             const auto psDefault = psLastField->GetDefault();
     236           0 :             WriteFloat64(abyDefaultVal,
     237             :                          FileGDBOGRDateToDoubleDate(
     238             :                              psDefault, /* bConvertToUTC = */ false,
     239             :                              /* bIsHighPrecision= */ true));
     240           0 :             if (psDefault->Date.TZFlag > 1)
     241             :             {
     242           0 :                 WriteInt16(
     243             :                     abyDefaultVal,
     244           0 :                     static_cast<int16_t>((psDefault->Date.TZFlag - 100) * 15));
     245             :             }
     246             :             else
     247             :             {
     248           0 :                 WriteInt16(abyDefaultVal, 0);
     249             :             }
     250             :         }
     251          42 :         nExtraBytes += static_cast<int>(abyDefaultVal.size());
     252             :     }
     253          44 :     CPLAssert(nExtraBytes != 0);
     254             : 
     255          88 :     std::vector<GByte> abyBufferOffsets;
     256          44 :     abyBufferOffsets.resize(1024 * m_nTablxOffsetSize);
     257             : 
     258          88 :     WholeFileRewriter oWholeFileRewriter(*this);
     259          44 :     if (!oWholeFileRewriter.Begin())
     260           0 :         return false;
     261             : 
     262          44 :     if (CPLTestBool(CPLGetConfigOption(
     263             :             "OPENFILEGDB_SIMUL_ERROR_IN_RewriteTableToAddLastAddedField",
     264             :             "FALSE")))
     265             :     {
     266           6 :         return false;
     267             :     }
     268             : 
     269          38 :     uint32_t nRowBufferMaxSize = 0;
     270          38 :     m_nCurRow = -1;
     271             : 
     272             :     // Rewrite all features
     273          76 :     for (uint32_t iPage = 0; iPage < m_n1024BlocksPresent; ++iPage)
     274             :     {
     275          38 :         const vsi_l_offset nOffsetInTableX =
     276          38 :             16 + m_nTablxOffsetSize * static_cast<vsi_l_offset>(iPage) * 1024;
     277          38 :         VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtablx, nOffsetInTableX,
     278             :                   SEEK_SET);
     279          38 :         if (VSIFReadL(abyBufferOffsets.data(), m_nTablxOffsetSize * 1024, 1,
     280          38 :                       oWholeFileRewriter.m_fpOldGdbtablx) != 1)
     281           0 :             return false;
     282             : 
     283          38 :         GByte *pabyBufferOffsets = abyBufferOffsets.data();
     284       38950 :         for (int i = 0; i < 1024; i++, pabyBufferOffsets += m_nTablxOffsetSize)
     285             :         {
     286       38912 :             const uint64_t nOffset = ReadFeatureOffset(pabyBufferOffsets);
     287       38912 :             if (nOffset != 0)
     288             :             {
     289             :                 // Read feature size
     290          77 :                 VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtable, nOffset,
     291             :                           SEEK_SET);
     292          77 :                 uint32_t nFeatureSize = 0;
     293          77 :                 if (!ReadUInt32(oWholeFileRewriter.m_fpOldGdbtable,
     294             :                                 nFeatureSize))
     295           0 :                     return false;
     296             : 
     297             :                 // Read feature data
     298          77 :                 if (nFeatureSize > m_abyBuffer.size())
     299             :                 {
     300             :                     try
     301             :                     {
     302          18 :                         m_abyBuffer.resize(nFeatureSize);
     303             :                     }
     304           0 :                     catch (const std::exception &e)
     305             :                     {
     306           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     307           0 :                         return false;
     308             :                     }
     309             :                 }
     310          77 :                 if (VSIFReadL(m_abyBuffer.data(), nFeatureSize, 1,
     311          77 :                               oWholeFileRewriter.m_fpOldGdbtable) != 1)
     312           0 :                     return false;
     313             : 
     314             :                 // Update offset of updated feature
     315          77 :                 WriteFeatureOffset(m_nFileSize, pabyBufferOffsets);
     316             : 
     317             :                 // Write updated feature size
     318          77 :                 const uint32_t nNewFeatureSize = nFeatureSize + nExtraBytes;
     319          77 :                 if (!WriteUInt32(oWholeFileRewriter.m_fpTable, nNewFeatureSize))
     320           0 :                     return false;
     321             : 
     322             :                 // Write updated feature data
     323          77 :                 if (nOldNullableFieldsSizeInBytes > 0)
     324             :                 {
     325          77 :                     if (VSIFWriteL(m_abyBuffer.data(),
     326             :                                    nOldNullableFieldsSizeInBytes, 1,
     327          77 :                                    oWholeFileRewriter.m_fpTable) != 1)
     328           0 :                         return false;
     329             :                 }
     330          77 :                 if (nOldNullableFieldsSizeInBytes !=
     331          77 :                     m_nNullableFieldsSizeInBytes)
     332             :                 {
     333           5 :                     CPLAssert(psLastField->IsNullable());
     334           5 :                     const GByte byNewNullableFieldByte = 0xFF;
     335           5 :                     if (VSIFWriteL(&byNewNullableFieldByte, 1, 1,
     336           5 :                                    oWholeFileRewriter.m_fpTable) != 1)
     337           0 :                         return false;
     338             :                 }
     339          77 :                 if (nFeatureSize - nOldNullableFieldsSizeInBytes > 0)
     340             :                 {
     341         231 :                     if (VSIFWriteL(m_abyBuffer.data() +
     342          77 :                                        nOldNullableFieldsSizeInBytes,
     343          77 :                                    nFeatureSize - nOldNullableFieldsSizeInBytes,
     344          77 :                                    1, oWholeFileRewriter.m_fpTable) != 1)
     345           0 :                         return false;
     346             :                 }
     347          77 :                 if (!abyDefaultVal.empty())
     348             :                 {
     349          72 :                     if (VSIFWriteL(abyDefaultVal.data(), abyDefaultVal.size(),
     350          72 :                                    1, oWholeFileRewriter.m_fpTable) != 1)
     351           0 :                         return false;
     352             :                 }
     353             : 
     354          77 :                 if (nNewFeatureSize > nRowBufferMaxSize)
     355          40 :                     nRowBufferMaxSize = nNewFeatureSize;
     356          77 :                 m_nFileSize += sizeof(uint32_t) + nNewFeatureSize;
     357             :             }
     358             :         }
     359          38 :         VSIFSeekL(oWholeFileRewriter.m_fpTableX, nOffsetInTableX, SEEK_SET);
     360          38 :         if (VSIFWriteL(abyBufferOffsets.data(), m_nTablxOffsetSize * 1024, 1,
     361          38 :                        oWholeFileRewriter.m_fpTableX) != 1)
     362           0 :             return false;
     363             :     }
     364             : 
     365          38 :     m_nRowBufferMaxSize = nRowBufferMaxSize;
     366          38 :     m_nHeaderBufferMaxSize = std::max(m_nFieldDescLength, m_nRowBufferMaxSize);
     367             : 
     368          38 :     return oWholeFileRewriter.Commit();
     369             : }
     370             : 
     371             : /************************************************************************/
     372             : /*                       WriteFieldDescriptor()                         */
     373             : /************************************************************************/
     374             : 
     375             : static void
     376       13597 : WriteFieldDescriptor(std::vector<GByte> &abyBuffer, const FileGDBField *psField,
     377             :                      bool bGeomTypeHasZ, bool bGeomTypeHasM,
     378             :                      bool bStringsAreUTF8, uint32_t &nGeomFieldBBoxOffsetOut,
     379             :                      uint32_t &nGeomFieldSpatialIndexGridResOffsetOut)
     380             : {
     381       13597 :     WriteUTF16String(abyBuffer, psField->GetName().c_str(),
     382             :                      NUMBER_OF_CHARS_ON_UINT8);
     383       13597 :     WriteUTF16String(abyBuffer, psField->GetAlias().c_str(),
     384             :                      NUMBER_OF_CHARS_ON_UINT8);
     385       13597 :     WriteUInt8(abyBuffer, static_cast<uint8_t>(psField->GetType()));
     386       13597 :     constexpr int UNKNOWN_FIELD_FLAG = 4;
     387       13597 :     const auto &sDefault = *(psField->GetDefault());
     388       13597 :     switch (psField->GetType())
     389             :     {
     390           0 :         case FGFT_UNDEFINED:
     391             :         {
     392           0 :             CPLAssert(false);
     393             :             break;
     394             :         }
     395             : 
     396         300 :         case FGFT_INT16:
     397             :         {
     398         300 :             WriteUInt8(abyBuffer, 2);  // sizeof(int16)
     399         300 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     400         300 :                                       UNKNOWN_FIELD_FLAG |
     401         300 :                                       static_cast<int>(psField->IsNullable())));
     402         600 :             if (!OGR_RawField_IsNull(&sDefault) &&
     403         300 :                 !OGR_RawField_IsUnset(&sDefault))
     404             :             {
     405          66 :                 WriteUInt8(abyBuffer, 2);  // sizeof(int16)
     406          66 :                 WriteInt16(abyBuffer, static_cast<int16_t>(sDefault.Integer));
     407             :             }
     408             :             else
     409             :             {
     410         234 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     411             :             }
     412         300 :             break;
     413             :         }
     414             : 
     415        1280 :         case FGFT_INT32:
     416             :         {
     417        1280 :             WriteUInt8(abyBuffer, 4);  // sizeof(int32)
     418        1280 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     419        1280 :                                       UNKNOWN_FIELD_FLAG |
     420        1280 :                                       static_cast<int>(psField->IsNullable())));
     421        2560 :             if (!OGR_RawField_IsNull(&sDefault) &&
     422        1280 :                 !OGR_RawField_IsUnset(&sDefault))
     423             :             {
     424          58 :                 WriteUInt8(abyBuffer, 4);  // sizeof(int32)
     425          58 :                 WriteInt32(abyBuffer, sDefault.Integer);
     426             :             }
     427             :             else
     428             :             {
     429        1222 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     430             :             }
     431        1280 :             break;
     432             :         }
     433             : 
     434          49 :         case FGFT_FLOAT32:
     435             :         {
     436          49 :             WriteUInt8(abyBuffer, 4);  // sizeof(float32)
     437          49 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     438          49 :                                       UNKNOWN_FIELD_FLAG |
     439          49 :                                       static_cast<int>(psField->IsNullable())));
     440          98 :             if (!OGR_RawField_IsNull(&sDefault) &&
     441          49 :                 !OGR_RawField_IsUnset(&sDefault))
     442             :             {
     443          42 :                 WriteUInt8(abyBuffer, 4);  // sizeof(float32)
     444          42 :                 WriteFloat32(abyBuffer, static_cast<float>(sDefault.Real));
     445             :             }
     446             :             else
     447             :             {
     448           7 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     449             :             }
     450          49 :             break;
     451             :         }
     452             : 
     453        2312 :         case FGFT_FLOAT64:
     454             :         {
     455        2312 :             WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     456        2312 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     457        2312 :                                       UNKNOWN_FIELD_FLAG |
     458        2312 :                                       static_cast<int>(psField->IsNullable())));
     459        4624 :             if (!OGR_RawField_IsNull(&sDefault) &&
     460        2312 :                 !OGR_RawField_IsUnset(&sDefault))
     461             :             {
     462          32 :                 WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     463          32 :                 WriteFloat64(abyBuffer, sDefault.Real);
     464             :             }
     465             :             else
     466             :             {
     467        2280 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     468             :             }
     469        2312 :             break;
     470             :         }
     471             : 
     472        3700 :         case FGFT_STRING:
     473             :         {
     474        3700 :             WriteUInt32(abyBuffer, psField->GetMaxWidth());
     475        3700 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     476        3700 :                                       UNKNOWN_FIELD_FLAG |
     477        3700 :                                       static_cast<int>(psField->IsNullable())));
     478        7400 :             if (!OGR_RawField_IsNull(&sDefault) &&
     479        3700 :                 !OGR_RawField_IsUnset(&sDefault))
     480             :             {
     481          95 :                 if (bStringsAreUTF8)
     482             :                 {
     483          94 :                     const auto nLen = strlen(sDefault.String);
     484          94 :                     WriteVarUInt(abyBuffer, nLen);
     485          94 :                     if (nLen > 0)
     486             :                     {
     487           0 :                         abyBuffer.insert(abyBuffer.end(), sDefault.String,
     488          94 :                                          sDefault.String + nLen);
     489             :                     }
     490             :                 }
     491             :                 else
     492             :                 {
     493           1 :                     WriteUTF16String(abyBuffer, sDefault.String,
     494             :                                      NUMBER_OF_BYTES_ON_VARUINT);
     495             :                 }
     496             :             }
     497             :             else
     498             :             {
     499        3605 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     500             :             }
     501        3700 :             break;
     502             :         }
     503             : 
     504          61 :         case FGFT_DATETIME:
     505             :         case FGFT_DATE:
     506             :         {
     507          61 :             WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     508          61 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     509          61 :                                       UNKNOWN_FIELD_FLAG |
     510          61 :                                       static_cast<int>(psField->IsNullable())));
     511         122 :             if (!OGR_RawField_IsNull(&sDefault) &&
     512          61 :                 !OGR_RawField_IsUnset(&sDefault))
     513             :             {
     514          20 :                 WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     515          20 :                 WriteFloat64(abyBuffer,
     516             :                              FileGDBOGRDateToDoubleDate(
     517             :                                  &sDefault, /* bConvertToUTC = */ true,
     518          20 :                                  psField->IsHighPrecision()));
     519             :             }
     520             :             else
     521             :             {
     522          41 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     523             :             }
     524          61 :             break;
     525             :         }
     526             : 
     527        1741 :         case FGFT_OBJECTID:
     528             :         {
     529        1741 :             WriteUInt8(abyBuffer, 4);  // sizeof(uint32) ?
     530        1741 :             WriteUInt8(abyBuffer, 2);  // magic value
     531        1741 :             break;
     532             :         }
     533             : 
     534         539 :         case FGFT_GEOMETRY:
     535             :         {
     536             :             const auto *geomField =
     537         539 :                 cpl::down_cast<const FileGDBGeomField *>(psField);
     538         539 :             WriteUInt8(abyBuffer, 0);  // unknown role
     539         539 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     540         539 :                                       2 | UNKNOWN_FIELD_FLAG |
     541         539 :                                       static_cast<int>(psField->IsNullable())));
     542         539 :             WriteUTF16String(abyBuffer, geomField->GetWKT().c_str(),
     543             :                              NUMBER_OF_BYTES_ON_UINT16);
     544         539 :             WriteUInt8(
     545             :                 abyBuffer,
     546             :                 static_cast<uint8_t>(
     547             :                     1 |
     548        1078 :                     (((geomField->HasMOriginScaleTolerance() ? 1 : 0)) << 1) |
     549         539 :                     (((geomField->HasZOriginScaleTolerance() ? 1 : 0)) << 2)));
     550         539 :             WriteFloat64(abyBuffer, geomField->GetXOrigin());
     551         539 :             WriteFloat64(abyBuffer, geomField->GetYOrigin());
     552         539 :             WriteFloat64(abyBuffer, geomField->GetXYScale());
     553         539 :             if (geomField->HasMOriginScaleTolerance())
     554             :             {
     555         539 :                 WriteFloat64(abyBuffer, geomField->GetMOrigin());
     556         539 :                 WriteFloat64(abyBuffer, geomField->GetMScale());
     557             :             }
     558         539 :             if (geomField->HasZOriginScaleTolerance())
     559             :             {
     560         539 :                 WriteFloat64(abyBuffer, geomField->GetZOrigin());
     561         539 :                 WriteFloat64(abyBuffer, geomField->GetZScale());
     562             :             }
     563         539 :             WriteFloat64(abyBuffer, geomField->GetXYTolerance());
     564         539 :             if (geomField->HasMOriginScaleTolerance())
     565             :             {
     566         539 :                 WriteFloat64(abyBuffer, geomField->GetMTolerance());
     567             :             }
     568         539 :             if (geomField->HasZOriginScaleTolerance())
     569             :             {
     570         539 :                 WriteFloat64(abyBuffer, geomField->GetZTolerance());
     571             :             }
     572         539 :             nGeomFieldBBoxOffsetOut = static_cast<uint32_t>(abyBuffer.size());
     573         539 :             WriteFloat64(abyBuffer, geomField->GetXMin());
     574         539 :             WriteFloat64(abyBuffer, geomField->GetYMin());
     575         539 :             WriteFloat64(abyBuffer, geomField->GetXMax());
     576         539 :             WriteFloat64(abyBuffer, geomField->GetYMax());
     577         539 :             if (bGeomTypeHasZ)
     578             :             {
     579          48 :                 WriteFloat64(abyBuffer, geomField->GetZMin());
     580          48 :                 WriteFloat64(abyBuffer, geomField->GetZMax());
     581             :             }
     582         539 :             if (bGeomTypeHasM)
     583             :             {
     584          24 :                 WriteFloat64(abyBuffer, geomField->GetMMin());
     585          24 :                 WriteFloat64(abyBuffer, geomField->GetMMax());
     586             :             }
     587         539 :             WriteUInt8(abyBuffer, 0);  // possibly an indicator of existence of
     588             :                                        // spatial index or its type?
     589             :             const auto &adfSpatialIndexGridResolution =
     590         539 :                 geomField->GetSpatialIndexGridResolution();
     591         539 :             WriteUInt32(abyBuffer, static_cast<uint32_t>(
     592         539 :                                        adfSpatialIndexGridResolution.size()));
     593         539 :             nGeomFieldSpatialIndexGridResOffsetOut =
     594         539 :                 static_cast<uint32_t>(abyBuffer.size());
     595        1528 :             for (double dfSize : adfSpatialIndexGridResolution)
     596         989 :                 WriteFloat64(abyBuffer, dfSize);
     597         539 :             break;
     598             :         }
     599             : 
     600         227 :         case FGFT_BINARY:
     601             :         {
     602         227 :             WriteUInt8(abyBuffer, 0);  // unknown role
     603         227 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     604         227 :                                       UNKNOWN_FIELD_FLAG |
     605         227 :                                       static_cast<int>(psField->IsNullable())));
     606         227 :             break;
     607             :         }
     608             : 
     609           0 :         case FGFT_RASTER:
     610             :         {
     611             :             // Not handled for now
     612           0 :             CPLAssert(false);
     613             :             break;
     614             :         }
     615             : 
     616        2479 :         case FGFT_GUID:
     617             :         case FGFT_GLOBALID:
     618             :         {
     619        2479 :             WriteUInt8(abyBuffer, 38);  // size
     620        2479 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     621        2479 :                                       UNKNOWN_FIELD_FLAG |
     622        2479 :                                       static_cast<int>(psField->IsNullable())));
     623        2479 :             break;
     624             :         }
     625             : 
     626         902 :         case FGFT_XML:
     627             :         {
     628         902 :             WriteUInt8(abyBuffer, 0);  // unknown role
     629         902 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     630         902 :                                       UNKNOWN_FIELD_FLAG |
     631         902 :                                       static_cast<int>(psField->IsNullable())));
     632         902 :             break;
     633             :         }
     634             : 
     635           1 :         case FGFT_INT64:
     636             :         {
     637           1 :             WriteUInt8(abyBuffer, 8);  // sizeof(int64)
     638           1 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     639           1 :                                       UNKNOWN_FIELD_FLAG |
     640           1 :                                       static_cast<int>(psField->IsNullable())));
     641           2 :             if (!OGR_RawField_IsNull(&sDefault) &&
     642           1 :                 !OGR_RawField_IsUnset(&sDefault))
     643             :             {
     644           1 :                 WriteUInt8(abyBuffer, 8);  // sizeof(int64)
     645           1 :                 WriteInt64(abyBuffer, sDefault.Integer64);
     646             :             }
     647             :             else
     648             :             {
     649           0 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     650             :             }
     651           1 :             break;
     652             :         }
     653             : 
     654           2 :         case FGFT_TIME:
     655             :         {
     656           2 :             WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     657           2 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     658           2 :                                       UNKNOWN_FIELD_FLAG |
     659           2 :                                       static_cast<int>(psField->IsNullable())));
     660           4 :             if (!OGR_RawField_IsNull(&sDefault) &&
     661           2 :                 !OGR_RawField_IsUnset(&sDefault))
     662             :             {
     663           2 :                 WriteUInt8(abyBuffer, 8);  // sizeof(float64)
     664           2 :                 WriteFloat64(abyBuffer, FileGDBOGRTimeToDoubleTime(&sDefault));
     665             :             }
     666             :             else
     667             :             {
     668           0 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     669             :             }
     670           2 :             break;
     671             :         }
     672             : 
     673           4 :         case FGFT_DATETIME_WITH_OFFSET:
     674             :         {
     675           4 :             WriteUInt8(abyBuffer, 8 + 2);  // sizeof(float64) + sizeof(int16)
     676           4 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(
     677           4 :                                       UNKNOWN_FIELD_FLAG |
     678           4 :                                       static_cast<int>(psField->IsNullable())));
     679           8 :             if (!OGR_RawField_IsNull(&sDefault) &&
     680           4 :                 !OGR_RawField_IsUnset(&sDefault))
     681             :             {
     682           4 :                 WriteUInt8(abyBuffer,
     683             :                            8 + 2);  // sizeof(float64) + sizeof(int16)
     684           4 :                 WriteFloat64(abyBuffer,
     685             :                              FileGDBOGRDateToDoubleDate(
     686             :                                  &sDefault, /* bConvertToUTC = */ false,
     687             :                                  /* bIsHighPrecision = */ true));
     688           4 :                 if (sDefault.Date.TZFlag > 1)
     689             :                 {
     690           2 :                     WriteInt16(abyBuffer,
     691             :                                static_cast<int16_t>(
     692           2 :                                    (sDefault.Date.TZFlag - 100) * 15));
     693             :                 }
     694             :                 else
     695             :                 {
     696           2 :                     WriteInt16(abyBuffer, 0);
     697             :                 }
     698             :             }
     699             :             else
     700             :             {
     701           0 :                 WriteUInt8(abyBuffer, 0);  // size of default value
     702             :             }
     703           4 :             break;
     704             :         }
     705             :     }
     706       13597 : }
     707             : 
     708             : /************************************************************************/
     709             : /*                       WriteFieldDescriptors()                        */
     710             : /************************************************************************/
     711             : 
     712        1967 : bool FileGDBTable::WriteFieldDescriptors(VSILFILE *fpTable)
     713             : {
     714        1967 :     m_bDirtyFieldDescriptors = false;
     715             : 
     716             :     // In-memory field descriptors
     717        3934 :     std::vector<GByte> abyBuffer;
     718             : 
     719        1967 :     WriteUInt32(abyBuffer, 0);  // size of field section, excluding this field.
     720             :                                 // Will be patched later
     721        1967 :     WriteUInt32(abyBuffer, 4);  // version of the file
     722             : 
     723        1967 :     const uint32_t nLayerFlags =
     724        3934 :         static_cast<uint32_t>(m_eTableGeomType) |
     725        1967 :         ((m_bStringsAreUTF8 ? 1 : 0) << 8) |  // string encoding
     726        1967 :         (((m_eTableGeomType != FGTGT_NONE) ? 1 : 0)
     727        1967 :          << 9) |  // "high precision storage"
     728        1967 :         (static_cast<uint32_t>(m_bGeomTypeHasM) << 30U) |
     729        1967 :         (static_cast<uint32_t>(m_bGeomTypeHasZ) << 31U);
     730        1967 :     WriteUInt32(abyBuffer, nLayerFlags);
     731             : 
     732        1967 :     WriteUInt16(abyBuffer, static_cast<uint16_t>(m_apoFields.size()));
     733             : 
     734        1967 :     m_nGeomFieldBBoxSubOffset = 0;
     735       15564 :     for (const auto &poField : m_apoFields)
     736             :     {
     737       13597 :         WriteFieldDescriptor(abyBuffer, poField.get(), m_bGeomTypeHasZ,
     738       13597 :                              m_bGeomTypeHasM, m_bStringsAreUTF8,
     739       13597 :                              m_nGeomFieldBBoxSubOffset,
     740       13597 :                              m_nGeomFieldSpatialIndexGridResSubOffset);
     741             :     }
     742             : 
     743             :     // Just to imitate the behavior of the FileGDB SDK !
     744        1967 :     abyBuffer.push_back(0xDE);
     745        1967 :     abyBuffer.push_back(0xAD);
     746        1967 :     abyBuffer.push_back(0xBE);
     747        1967 :     abyBuffer.push_back(0xEF);
     748             : 
     749             :     // Patch size of field section at beginning of buffer
     750             :     const auto nFieldSectionSize =
     751        1967 :         static_cast<uint32_t>(abyBuffer.size() - sizeof(uint32_t));
     752        1967 :     WriteUInt32(abyBuffer, nFieldSectionSize, 0);
     753             : 
     754        1967 :     bool bUpdateFileSize = false;
     755        1967 :     const auto nOldFieldDescLength = m_nFieldDescLength;
     756        1967 :     if (m_nOffsetFieldDesc + m_nFieldDescLength == m_nFileSize)
     757             :     {
     758             :         // Optimization: if the field descriptor section is already at end of
     759             :         // file, we can rewrite-in-place whatever its new size
     760           0 :         VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
     761           0 :         bUpdateFileSize = true;
     762             :     }
     763        1967 :     else if (abyBuffer.size() > m_nFieldDescLength)
     764             :     {
     765        1947 :         if (m_nOffsetFieldDesc != 0)
     766             :         {
     767          62 :             VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
     768             :             // Cancel unused bytes with NUL characters
     769          62 :             std::vector<GByte> abyNul(m_nFieldDescLength + sizeof(uint32_t));
     770          62 :             CPL_IGNORE_RET_VAL(
     771          62 :                 VSIFWriteL(abyNul.data(), 1, abyNul.size(), fpTable));
     772             :         }
     773        1947 :         VSIFSeekL(fpTable, m_nFileSize, SEEK_SET);
     774        1947 :         m_bDirtyHeader = true;
     775        1947 :         m_nOffsetFieldDesc = m_nFileSize;
     776        1947 :         m_nFileSize += abyBuffer.size();
     777             :     }
     778             :     else
     779             :     {
     780          20 :         VSIFSeekL(fpTable, m_nOffsetFieldDesc, SEEK_SET);
     781             :     }
     782             : 
     783             :     // Write new field descriptor
     784        1967 :     m_nFieldDescLength = nFieldSectionSize;
     785        3934 :     if (VSIFWriteL(abyBuffer.data(), 1, abyBuffer.size(), fpTable) !=
     786        1967 :         abyBuffer.size())
     787           0 :         return false;
     788             : 
     789        1967 :     if (bUpdateFileSize)
     790             :     {
     791           0 :         m_nFileSize = VSIFTellL(fpTable);
     792           0 :         VSIFTruncateL(fpTable, m_nFileSize);
     793           0 :         m_bDirtyHeader = true;
     794             :     }
     795        1967 :     else if (nOldFieldDescLength != 0 &&
     796          82 :              m_nFieldDescLength < nOldFieldDescLength)
     797             :     {
     798             :         // Cancel unused bytes with NUL characters
     799          20 :         std::vector<GByte> abyNul(nOldFieldDescLength - m_nFieldDescLength);
     800          20 :         CPL_IGNORE_RET_VAL(
     801          20 :             VSIFWriteL(abyNul.data(), 1, abyNul.size(), fpTable));
     802             :     }
     803             : 
     804        1967 :     return true;
     805             : }
     806             : 
     807             : /************************************************************************/
     808             : /*                            DeleteField()                             */
     809             : /************************************************************************/
     810             : 
     811          14 : bool FileGDBTable::DeleteField(int iField)
     812             : {
     813          14 :     if (!m_bUpdate)
     814           0 :         return false;
     815             : 
     816          14 :     if (iField < 0 || iField >= static_cast<int>(m_apoFields.size()))
     817             :     {
     818           0 :         return false;
     819             :     }
     820             : 
     821          14 :     if (m_iGeomField == iField)
     822             :     {
     823           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     824             :                  "Geometry field deletion not supported");
     825           0 :         return false;
     826             :     }
     827             : 
     828          14 :     bool bRet = true;
     829          14 :     if (iField != m_iObjectIdField)
     830             :     {
     831          14 :         std::vector<GByte> abyBlank;
     832             : 
     833             :         // Little hack: we present the geometry field as a binary one
     834             :         // to avoid any conversion
     835          14 :         const int iGeomFieldBackup = m_iGeomField;
     836          14 :         if (m_iGeomField >= 0)
     837          12 :             m_apoFields[m_iGeomField]->m_eType = FGFT_BINARY;
     838          14 :         m_iGeomField = -1;
     839             : 
     840          40 :         for (int iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat)
     841             :         {
     842          26 :             iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat);
     843          26 :             if (iCurFeat < 0)
     844           0 :                 break;
     845          52 :             auto asValues = GetAllFieldValues();
     846             : 
     847          26 :             if (m_nRowBlobLength > 0)
     848             :             {
     849          26 :                 if (EncodeFeature(asValues, nullptr, iField))
     850             :                 {
     851          26 :                     VSIFSeekL(m_fpTable,
     852          26 :                               VSIFTellL(m_fpTable) - sizeof(uint32_t) -
     853          26 :                                   m_nRowBlobLength,
     854             :                               SEEK_SET);
     855             : 
     856          26 :                     abyBlank.resize(m_nRowBlobLength - m_abyBuffer.size());
     857             : 
     858          26 :                     if (!WriteUInt32(m_fpTable, static_cast<uint32_t>(
     859          26 :                                                     m_abyBuffer.size())) ||
     860          26 :                         VSIFWriteL(m_abyBuffer.data(), m_abyBuffer.size(), 1,
     861          52 :                                    m_fpTable) != 1 ||
     862          26 :                         (!abyBlank.empty() &&
     863          21 :                          VSIFWriteL(abyBlank.data(), abyBlank.size(), 1,
     864             :                                     m_fpTable) != 1))
     865             :                     {
     866           0 :                         bRet = false;
     867             :                     }
     868             :                 }
     869             :                 else
     870             :                 {
     871           0 :                     bRet = false;
     872             :                 }
     873             :             }
     874             : 
     875          26 :             FreeAllFieldValues(asValues);
     876             :         }
     877             : 
     878          14 :         if (iGeomFieldBackup >= 0)
     879          12 :             m_apoFields[iGeomFieldBackup]->m_eType = FGFT_GEOMETRY;
     880          14 :         m_iGeomField = iGeomFieldBackup;
     881             :     }
     882             : 
     883             :     // Delete linked index if existing
     884          14 :     GetIndexCount();
     885          14 :     if (m_apoFields[iField]->m_poIndex)
     886             :     {
     887           4 :         for (size_t i = 0; i < m_apoIndexes.size(); ++i)
     888             :         {
     889           4 :             if (m_apoIndexes[i].get() == m_apoFields[iField]->m_poIndex)
     890             :             {
     891           1 :                 m_bDirtyGdbIndexesFile = true;
     892             : 
     893           1 :                 if (iField != m_iObjectIdField)
     894             :                 {
     895           1 :                     VSIUnlink(CPLResetExtension(
     896             :                         m_osFilename.c_str(),
     897           2 :                         (m_apoIndexes[i]->GetIndexName() + ".atx").c_str()));
     898             :                 }
     899             : 
     900           1 :                 m_apoIndexes.erase(m_apoIndexes.begin() + i);
     901           1 :                 break;
     902             :             }
     903             :         }
     904             :     }
     905             : 
     906             :     // Renumber objectId and geomField indices
     907          14 :     if (m_iObjectIdField == iField)
     908           0 :         m_iObjectIdField = -1;
     909          14 :     else if (iField < m_iObjectIdField)
     910           2 :         m_iObjectIdField--;
     911             : 
     912          14 :     if (iField < m_iGeomField)
     913           2 :         m_iGeomField--;
     914             : 
     915          14 :     if (m_apoFields[iField]->IsNullable())
     916             :     {
     917          12 :         m_nCountNullableFields--;
     918          12 :         m_nNullableFieldsSizeInBytes =
     919          12 :             BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
     920             :     }
     921             : 
     922          14 :     m_apoFields.erase(m_apoFields.begin() + iField);
     923             : 
     924          14 :     m_bDirtyFieldDescriptors = true;
     925             : 
     926          14 :     return bRet;
     927             : }
     928             : 
     929             : /************************************************************************/
     930             : /*                            AlterField()                              */
     931             : /************************************************************************/
     932             : 
     933           7 : bool FileGDBTable::AlterField(int iField, const std::string &osName,
     934             :                               const std::string &osAlias,
     935             :                               FileGDBFieldType eType, bool bNullable,
     936             :                               int nMaxWidth, const OGRField &sDefault)
     937             : {
     938           7 :     if (!m_bUpdate)
     939           0 :         return false;
     940             : 
     941           7 :     if (iField < 0 || iField >= static_cast<int>(m_apoFields.size()))
     942             :     {
     943           0 :         return false;
     944             :     }
     945             : 
     946           7 :     if (m_iGeomField == iField)
     947             :     {
     948           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     949             :                  "AlterField() not supported on geometry field");
     950           0 :         return false;
     951             :     }
     952             : 
     953           7 :     if (m_apoFields[iField]->GetType() != eType)
     954             :     {
     955           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     956             :                  "AlterField() does not support modifying the field type");
     957           0 :         return false;
     958             :     }
     959             : 
     960           7 :     if (m_apoFields[iField]->IsNullable() != bNullable)
     961             :     {
     962           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     963             :                  "AlterField() does not support modifying the nullable state");
     964           0 :         return false;
     965             :     }
     966             : 
     967           7 :     const bool bRenameField = m_apoFields[iField]->GetName() != osName;
     968           7 :     if (bRenameField && GetFieldIdx(osName) >= 0)
     969             :     {
     970           1 :         CPLError(
     971             :             CE_Failure, CPLE_NotSupported,
     972             :             "AlterField() cannot rename a field to an existing field name");
     973           1 :         return false;
     974             :     }
     975             : 
     976             :     // Update linked index if existing
     977           6 :     GetIndexCount();
     978           6 :     auto poIndex = m_apoFields[iField]->m_poIndex;
     979             : 
     980          12 :     m_apoFields[iField] = std::make_unique<FileGDBField>(
     981           6 :         osName, osAlias, eType, bNullable, nMaxWidth, sDefault);
     982           6 :     m_apoFields[iField]->SetParent(this);
     983           6 :     m_apoFields[iField]->m_poIndex = poIndex;
     984           6 :     if (poIndex && bRenameField)
     985             :     {
     986           1 :         m_bDirtyGdbIndexesFile = true;
     987           1 :         if (STARTS_WITH_CI(poIndex->GetExpression().c_str(), "LOWER("))
     988           0 :             poIndex->m_osExpression = "LOWER(" + osName + ")";
     989             :         else
     990           1 :             poIndex->m_osExpression = osName;
     991             :     }
     992           6 :     m_bDirtyFieldDescriptors = true;
     993             : 
     994           6 :     return true;
     995             : }
     996             : 
     997             : /************************************************************************/
     998             : /*                          AlterGeomField()                            */
     999             : /************************************************************************/
    1000             : 
    1001           4 : bool FileGDBTable::AlterGeomField(const std::string &osName,
    1002             :                                   const std::string &osAlias, bool bNullable,
    1003             :                                   const std::string &osWKT)
    1004             : {
    1005           4 :     if (!m_bUpdate)
    1006           0 :         return false;
    1007           4 :     if (m_iGeomField < 0)
    1008           0 :         return false;
    1009             : 
    1010             :     auto poGeomField =
    1011           4 :         cpl::down_cast<FileGDBGeomField *>(m_apoFields[m_iGeomField].get());
    1012           4 :     if (poGeomField->IsNullable() != bNullable)
    1013             :     {
    1014           0 :         CPLError(
    1015             :             CE_Failure, CPLE_NotSupported,
    1016             :             "AlterGeomField() does not support modifying the nullable state");
    1017           0 :         return false;
    1018             :     }
    1019             : 
    1020           4 :     const bool bRenameField = poGeomField->GetName() != osName;
    1021             : 
    1022           4 :     poGeomField->m_osName = osName;
    1023           4 :     poGeomField->m_osAlias = osAlias;
    1024           4 :     poGeomField->m_bNullable = bNullable;
    1025           4 :     poGeomField->m_osWKT = osWKT;
    1026           4 :     auto poIndex = poGeomField->m_poIndex;
    1027           4 :     if (poIndex && bRenameField)
    1028             :     {
    1029           0 :         poIndex->m_osExpression = osName;
    1030           0 :         m_bDirtyGdbIndexesFile = true;
    1031             :     }
    1032           4 :     m_bDirtyFieldDescriptors = true;
    1033             : 
    1034           4 :     return true;
    1035             : }
    1036             : 
    1037             : } /* namespace OpenFileGDB */

Generated by: LCOV version 1.14