LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_update.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 169 180 93.9 %
Date: 2026-10-02 01:53:29 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  "update" step of "vector pipeline"
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdalalg_vector_update.h"
      14             : 
      15             : #include "ogr_p.h"
      16             : #include "ogrsf_frmts.h"
      17             : 
      18             : //! @cond Doxygen_Suppress
      19             : 
      20             : #ifndef _
      21             : #define _(x) (x)
      22             : #endif
      23             : 
      24             : /************************************************************************/
      25             : /*        GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm()        */
      26             : /************************************************************************/
      27             : 
      28          97 : GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm(bool standaloneStep)
      29             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      30           0 :                                       ConstructorOptions()
      31          97 :                                           .SetStandaloneStep(standaloneStep)
      32          97 :                                           .SetInputDatasetMaxCount(1)
      33          97 :                                           .SetAddInputLayerNameArgument(false)
      34         194 :                                           .SetAddDefaultArguments(false))
      35             : {
      36          97 :     if (standaloneStep)
      37             :     {
      38          61 :         AddProgressArg();
      39          61 :         AddVectorInputArgs(false);
      40             :     }
      41             :     else
      42             :     {
      43          36 :         AddVectorHiddenInputDatasetArg();
      44             :     }
      45             : 
      46             :     {
      47             :         auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 0,
      48         194 :                                 _("Input layer name"), &m_inputLayerNames)
      49          97 :                              .SetMaxCount(1);
      50          97 :         auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
      51          97 :         if (inputArg)
      52          97 :             SetAutoCompleteFunctionForLayerName(layerArg, *inputArg);
      53             :     }
      54             : 
      55          97 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
      56          97 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      57          97 :     AddOutputOpenOptionsArg(&m_outputOpenOptions);
      58          97 :     AddOutputLayerNameArg(&m_outputLayerName);
      59             : 
      60          97 :     m_update = true;
      61          97 :     AddUpdateArg(&m_update).SetDefault(true).SetHidden();
      62             : 
      63         194 :     AddArg("mode", 0, _("Set update mode"), &m_mode)
      64          97 :         .SetDefault(m_mode)
      65          97 :         .SetChoices(MODE_MERGE, MODE_UPDATE_ONLY, MODE_APPEND_ONLY);
      66             : 
      67         194 :     AddArg("key", 0, _("Field(s) used as a key to identify features"), &m_key)
      68          97 :         .SetPackedValuesAllowed(false);
      69          97 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                 GDALVectorUpdateAlgorithm::RunStep()                 */
      73             : /************************************************************************/
      74             : 
      75          19 : bool GDALVectorUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
      76             : {
      77          19 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      78          19 :     CPLAssert(poSrcDS);
      79             : 
      80          19 :     auto poDstDS = m_outputDataset.GetDatasetRef();
      81          19 :     CPLAssert(poDstDS);
      82          19 :     CPLAssert(poDstDS->GetAccess() == GA_Update);
      83             : 
      84          19 :     auto poSrcDriver = poSrcDS->GetDriver();
      85          19 :     auto poDstDriver = poDstDS->GetDriver();
      86          37 :     if (poSrcDS == poDstDS ||
      87          18 :         (poSrcDriver && poDstDriver &&
      88          18 :          !EQUAL(poSrcDriver->GetDescription(), "MEM") &&
      89           1 :          !EQUAL(poDstDriver->GetDescription(), "MEM") &&
      90           1 :          strcmp(poSrcDS->GetDescription(), poDstDS->GetDescription()) == 0))
      91             :     {
      92           2 :         ReportError(CE_Failure, CPLE_NotSupported,
      93             :                     "Input and output datasets must be different");
      94           2 :         return false;
      95             :     }
      96             : 
      97          17 :     if (m_inputLayerNames.empty() && poSrcDS->GetLayerCount() == 1)
      98             :     {
      99          14 :         const auto poSrcLayer = poSrcDS->GetLayer(0);
     100          14 :         if (poSrcLayer)
     101          14 :             m_inputLayerNames.push_back(poSrcLayer->GetName());
     102             :     }
     103          17 :     if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
     104             :     {
     105          12 :         const auto poDstLayer = poDstDS->GetLayer(0);
     106          12 :         if (poDstLayer)
     107          12 :             m_outputLayerName = poDstLayer->GetName();
     108             :     }
     109             : 
     110          17 :     if (m_inputLayerNames.empty())
     111             :     {
     112           2 :         if (!m_outputLayerName.empty())
     113             :         {
     114           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     115             :                         "Please specify the 'input-layer' argument.");
     116           1 :             return false;
     117             :         }
     118             :         else
     119             :         {
     120           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     121             :                         "Please specify the 'input-layer' and 'output-layer' "
     122             :                         "arguments.");
     123           1 :             return false;
     124             :         }
     125             :     }
     126             : 
     127          15 :     auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
     128          15 :     if (!poSrcLayer)
     129             :     {
     130           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     131             :                     "No layer named '%s' in input dataset.",
     132           1 :                     m_inputLayerNames[0].c_str());
     133           1 :         return false;
     134             :     }
     135             : 
     136          14 :     if (m_outputLayerName.empty())
     137             :     {
     138           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     139             :                     "Please specify the 'output-layer' argument.");
     140           1 :         return false;
     141             :     }
     142             : 
     143          13 :     auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
     144          13 :     if (!poDstLayer)
     145             :     {
     146           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     147             :                     "No layer named '%s' in output dataset",
     148             :                     m_outputLayerName.c_str());
     149           1 :         return false;
     150             :     }
     151             : 
     152          24 :     std::vector<int> srcKeyFieldIndices;
     153          24 :     std::vector<OGRFieldType> keyFieldTypes;
     154          12 :     if (m_key.empty())
     155           7 :         m_key.push_back(SpecialFieldNames[SPF_FID]);
     156          24 :     for (const std::string &key : m_key)
     157             :     {
     158          15 :         if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
     159             :         {
     160           7 :             srcKeyFieldIndices.push_back(
     161           7 :                 poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
     162           7 :             keyFieldTypes.push_back(OFTInteger64);
     163           7 :             continue;
     164             :         }
     165             : 
     166             :         const int nSrcIdx =
     167           8 :             poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     168           8 :         if (nSrcIdx < 0)
     169             :         {
     170           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     171             :                         "Cannot find field '%s' in input layer", key.c_str());
     172           3 :             return false;
     173             :         }
     174           7 :         srcKeyFieldIndices.push_back(nSrcIdx);
     175             :         const auto poSrcFieldDefn =
     176           7 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
     177           7 :         const auto eType = poSrcFieldDefn->GetType();
     178           7 :         const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
     179             :                                                OFTInteger64, OFTReal};
     180           7 :         if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
     181           7 :                       eType) == std::end(aeAllowedTypes))
     182             :         {
     183           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     184             :                         "Type of field '%s' is not one of those supported for "
     185             :                         "a key field: String, Integer, Integer64, Real",
     186             :                         key.c_str());
     187           1 :             return false;
     188             :         }
     189             : 
     190             :         const int nDstIdx =
     191           6 :             poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     192           6 :         if (nDstIdx < 0)
     193             :         {
     194           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     195             :                         "Cannot find field '%s' in output layer", key.c_str());
     196           1 :             return false;
     197             :         }
     198             :         const auto poDstFieldDefn =
     199           5 :             poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
     200           5 :         if (poDstFieldDefn->GetType() != eType)
     201             :         {
     202           0 :             ReportError(
     203             :                 CE_Failure, CPLE_NotSupported,
     204             :                 "Type of field '%s' is not the same in input and output layers",
     205             :                 key.c_str());
     206           0 :             return false;
     207             :         }
     208           5 :         keyFieldTypes.push_back(eType);
     209             :     }
     210             : 
     211          17 :     const bool bFIDMatch = m_key.size() == 1 &&
     212           8 :                            EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
     213             :     const GIntBig nFeatureCount =
     214           9 :         ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
     215             : 
     216           9 :     std::string osFilter;
     217           9 :     int nIter = 0;
     218           9 :     bool bRet = true;
     219          25 :     for (const auto &poSrcFeature : *poSrcLayer)
     220             :     {
     221          16 :         ++nIter;
     222          22 :         if (ctxt.m_pfnProgress && nFeatureCount > 0 &&
     223           6 :             !ctxt.m_pfnProgress(static_cast<double>(nIter) / nFeatureCount, "",
     224             :                                 ctxt.m_pProgressData))
     225             :         {
     226           1 :             ReportError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
     227           1 :             bRet = false;
     228           1 :             break;
     229             :         }
     230             : 
     231           0 :         std::unique_ptr<OGRFeature> poDstFeature;
     232          15 :         if (bFIDMatch)
     233             :         {
     234          18 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     235           9 :             poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
     236             :         }
     237             :         else
     238             :         {
     239           6 :             bool bSkip = false;
     240           6 :             osFilter.clear();
     241          22 :             for (size_t iField = 0; iField < srcKeyFieldIndices.size();
     242             :                  ++iField)
     243             :             {
     244          18 :                 const int nSrcFieldIdx = srcKeyFieldIndices[iField];
     245          18 :                 if (!poSrcFeature->IsFieldSetAndNotNull(nSrcFieldIdx))
     246             :                 {
     247           2 :                     bSkip = true;
     248           2 :                     break;
     249             :                 }
     250          16 :                 if (!osFilter.empty())
     251          12 :                     osFilter += " AND ";
     252          16 :                 osFilter += CPLString(m_key[iField]).SQLQuotedIdentifier();
     253          16 :                 osFilter += " = ";
     254          16 :                 switch (keyFieldTypes[iField])
     255             :                 {
     256           4 :                     case OFTString:
     257             :                     {
     258           4 :                         osFilter += CPLString(poSrcFeature->GetFieldAsString(
     259             :                                                   nSrcFieldIdx))
     260           4 :                                         .SQLQuotedLiteral();
     261           4 :                         break;
     262             :                     }
     263             : 
     264           4 :                     case OFTReal:
     265             :                     {
     266             :                         osFilter += CPLSPrintf(
     267             :                             "%.17g",
     268           4 :                             poSrcFeature->GetFieldAsDouble(nSrcFieldIdx));
     269           4 :                         break;
     270             :                     }
     271             : 
     272           8 :                     default:
     273             :                     {
     274             :                         osFilter += CPLSPrintf(
     275             :                             CPL_FRMT_GIB,
     276           8 :                             poSrcFeature->GetFieldAsInteger64(nSrcFieldIdx));
     277           8 :                         break;
     278             :                     }
     279             :                 }
     280             :             }
     281           6 :             if (bSkip)
     282           2 :                 continue;
     283           4 :             if (poDstLayer->SetAttributeFilter(osFilter.c_str()) != OGRERR_NONE)
     284             :             {
     285           0 :                 bRet = false;
     286           0 :                 break;
     287             :             }
     288           4 :             poDstFeature.reset(poDstLayer->GetNextFeature());
     289           4 :             if (poDstFeature)
     290             :             {
     291             :                 // Check there is only one feature matching the criterion
     292           3 :                 if (std::unique_ptr<OGRFeature>(poDstLayer->GetNextFeature()))
     293             :                 {
     294           1 :                     poDstFeature.reset();
     295             :                 }
     296             :                 else
     297             :                 {
     298           2 :                     CPLDebugOnly("GDAL",
     299             :                                  "Updating output feature " CPL_FRMT_GIB
     300             :                                  " with src input " CPL_FRMT_GIB,
     301             :                                  poDstFeature->GetFID(),
     302             :                                  poSrcFeature->GetFID());
     303             :                 }
     304             :             }
     305             :         }
     306             : 
     307          13 :         poDstLayer->ResetReading();
     308             : 
     309          13 :         if (poDstFeature)
     310             :         {
     311           7 :             if (m_mode != MODE_APPEND_ONLY)
     312             :             {
     313             :                 auto poDstFeatureOri =
     314           6 :                     std::unique_ptr<OGRFeature>(poDstFeature->Clone());
     315           6 :                 const auto nDstFID = poDstFeature->GetFID();
     316           6 :                 poDstFeature->SetFrom(poSrcFeature.get());
     317             :                 // restore FID unset by SetFrom()
     318           6 :                 poDstFeature->SetFID(nDstFID);
     319             :                 const int nDstGeomFieldCount =
     320           6 :                     poDstFeature->GetGeomFieldCount();
     321          12 :                 for (int i = 0; i < nDstGeomFieldCount; ++i)
     322             :                 {
     323             :                     const bool bHasSrcGeomField =
     324             :                         nDstGeomFieldCount == 1
     325           6 :                             ? poSrcFeature->GetGeomFieldCount() > 0
     326           0 :                             : poSrcFeature->GetGeomFieldIndex(
     327             :                                   poDstFeature->GetGeomFieldDefnRef(i)
     328           6 :                                       ->GetNameRef()) >= 0;
     329           6 :                     if (!bHasSrcGeomField)
     330           1 :                         poDstFeature->SetGeomField(
     331           1 :                             i, poDstFeatureOri->GetGeomFieldRef(i));
     332             :                 }
     333          11 :                 if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
     334           5 :                     poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
     335             :                 {
     336           0 :                     bRet = false;
     337           0 :                     break;
     338             :                 }
     339             :             }
     340             :         }
     341           6 :         else if (m_mode != MODE_UPDATE_ONLY)
     342             :         {
     343             :             poDstFeature =
     344           3 :                 std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
     345           3 :             poDstFeature->SetFrom(poSrcFeature.get());
     346           3 :             if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
     347             :             {
     348           0 :                 bRet = false;
     349           0 :                 break;
     350             :             }
     351             :         }
     352             :     }
     353             : 
     354           9 :     poDstLayer->SetAttributeFilter(nullptr);
     355             : 
     356           9 :     return bRet;
     357             : }
     358             : 
     359             : /************************************************************************/
     360             : /*                ~GDALVectorUpdateAlgorithmStandalone()                */
     361             : /************************************************************************/
     362             : 
     363             : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
     364             :     default;
     365             : 
     366             : /************************************************************************/
     367             : /*            GDALVectorUpdateAlgorithmStandalone::RunImpl()            */
     368             : /************************************************************************/
     369             : 
     370          18 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
     371             :                                                   void *pProgressData)
     372             : {
     373          18 :     GDALPipelineStepRunContext stepCtxt;
     374          18 :     stepCtxt.m_pfnProgress = pfnProgress;
     375          18 :     stepCtxt.m_pProgressData = pProgressData;
     376          36 :     return RunStep(stepCtxt);
     377             : }
     378             : 
     379             : //! @endcond

Generated by: LCOV version 1.14