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

Generated by: LCOV version 1.14