LCOV - code coverage report
Current view: top level - apps - gdalalg_vector_update.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 161 171 94.2 %
Date: 2026-06-23 16:35:19 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          94 : GDALVectorUpdateAlgorithm::GDALVectorUpdateAlgorithm(bool standaloneStep)
      29             :     : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
      30           0 :                                       ConstructorOptions()
      31          94 :                                           .SetStandaloneStep(standaloneStep)
      32          94 :                                           .SetInputDatasetMaxCount(1)
      33          94 :                                           .SetAddInputLayerNameArgument(false)
      34         188 :                                           .SetAddDefaultArguments(false))
      35             : {
      36          94 :     if (standaloneStep)
      37             :     {
      38          58 :         AddProgressArg();
      39          58 :         AddVectorInputArgs(false);
      40             :     }
      41             :     else
      42             :     {
      43          36 :         AddVectorHiddenInputDatasetArg();
      44             :     }
      45             : 
      46             :     {
      47             :         auto &layerArg = AddArg(GDAL_ARG_NAME_INPUT_LAYER, 0,
      48         188 :                                 _("Input layer name"), &m_inputLayerNames)
      49          94 :                              .SetMaxCount(1);
      50          94 :         auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
      51          94 :         if (inputArg)
      52          94 :             SetAutoCompleteFunctionForLayerName(layerArg, *inputArg);
      53             :     }
      54             : 
      55          94 :     AddOutputDatasetArg(&m_outputDataset, GDAL_OF_VECTOR)
      56          94 :         .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
      57          94 :     AddOutputOpenOptionsArg(&m_outputOpenOptions);
      58          94 :     AddOutputLayerNameArg(&m_outputLayerName);
      59             : 
      60          94 :     m_update = true;
      61          94 :     AddUpdateArg(&m_update).SetDefault(true).SetHidden();
      62             : 
      63         188 :     AddArg("mode", 0, _("Set update mode"), &m_mode)
      64          94 :         .SetDefault(m_mode)
      65          94 :         .SetChoices(MODE_MERGE, MODE_UPDATE_ONLY, MODE_APPEND_ONLY);
      66             : 
      67         188 :     AddArg("key", 0, _("Field(s) used as a key to identify features"), &m_key)
      68          94 :         .SetPackedValuesAllowed(false);
      69          94 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                 GDALVectorUpdateAlgorithm::RunStep()                 */
      73             : /************************************************************************/
      74             : 
      75          17 : bool GDALVectorUpdateAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
      76             : {
      77          17 :     auto poSrcDS = m_inputDataset[0].GetDatasetRef();
      78          17 :     CPLAssert(poSrcDS);
      79             : 
      80          17 :     auto poDstDS = m_outputDataset.GetDatasetRef();
      81          17 :     CPLAssert(poDstDS);
      82          17 :     CPLAssert(poDstDS->GetAccess() == GA_Update);
      83             : 
      84          17 :     auto poSrcDriver = poSrcDS->GetDriver();
      85          17 :     auto poDstDriver = poDstDS->GetDriver();
      86          33 :     if (poSrcDS == poDstDS ||
      87          16 :         (poSrcDriver && poDstDriver &&
      88          16 :          !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          15 :     if (m_inputLayerNames.empty() && poSrcDS->GetLayerCount() == 1)
      98             :     {
      99          12 :         const auto poSrcLayer = poSrcDS->GetLayer(0);
     100          12 :         if (poSrcLayer)
     101          12 :             m_inputLayerNames.push_back(poSrcLayer->GetName());
     102             :     }
     103          15 :     if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
     104             :     {
     105          10 :         const auto poDstLayer = poDstDS->GetLayer(0);
     106          10 :         if (poDstLayer)
     107          10 :             m_outputLayerName = poDstLayer->GetName();
     108             :     }
     109             : 
     110          15 :     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          13 :     auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
     128          13 :     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          12 :     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          11 :     auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
     144          11 :     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          20 :     std::vector<int> srcKeyFieldIndices;
     153          20 :     std::vector<OGRFieldType> keyFieldTypes;
     154          10 :     if (m_key.empty())
     155           6 :         m_key.push_back(SpecialFieldNames[SPF_FID]);
     156          20 :     for (const std::string &key : m_key)
     157             :     {
     158          13 :         if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
     159             :         {
     160           6 :             srcKeyFieldIndices.push_back(
     161           6 :                 poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
     162           6 :             keyFieldTypes.push_back(OFTInteger64);
     163           6 :             continue;
     164             :         }
     165             : 
     166             :         const int nSrcIdx =
     167           7 :             poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     168           7 :         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           6 :         srcKeyFieldIndices.push_back(nSrcIdx);
     175             :         const auto poSrcFieldDefn =
     176           6 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
     177           6 :         const auto eType = poSrcFieldDefn->GetType();
     178           6 :         const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
     179             :                                                OFTInteger64, OFTReal};
     180           6 :         if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
     181           6 :                       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           5 :             poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     192           5 :         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           4 :             poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
     200           4 :         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           4 :         keyFieldTypes.push_back(eType);
     209             :     }
     210             : 
     211          13 :     const bool bFIDMatch = m_key.size() == 1 &&
     212           6 :                            EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
     213             :     const GIntBig nFeatureCount =
     214           7 :         ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
     215             : 
     216           7 :     std::string osFilter;
     217           7 :     int nIter = 0;
     218           7 :     bool bRet = true;
     219          21 :     for (const auto &poSrcFeature : *poSrcLayer)
     220             :     {
     221          14 :         ++nIter;
     222          20 :         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          13 :         if (bFIDMatch)
     233             :         {
     234          16 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     235           8 :             poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
     236             :         }
     237             :         else
     238             :         {
     239           5 :             bool bSkip = false;
     240           5 :             osFilter.clear();
     241          21 :             for (size_t iField = 0; iField < srcKeyFieldIndices.size();
     242             :                  ++iField)
     243             :             {
     244          17 :                 const int nSrcFieldIdx = srcKeyFieldIndices[iField];
     245          17 :                 if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
     246             :                 {
     247           1 :                     bSkip = true;
     248           1 :                     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           5 :             if (bSkip)
     282           1 :                 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          12 :         if (poDstFeature)
     308             :         {
     309           6 :             if (m_mode != MODE_APPEND_ONLY)
     310             :             {
     311             :                 auto poDstFeatureOri =
     312           5 :                     std::unique_ptr<OGRFeature>(poDstFeature->Clone());
     313           5 :                 const auto nDstFID = poDstFeature->GetFID();
     314           5 :                 poDstFeature->SetFrom(poSrcFeature.get());
     315             :                 // restore FID unset by SetFrom()
     316           5 :                 poDstFeature->SetFID(nDstFID);
     317          10 :                 if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
     318           5 :                     poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
     319             :                 {
     320           0 :                     bRet = false;
     321           0 :                     break;
     322             :                 }
     323             :             }
     324             :         }
     325           6 :         else if (m_mode != MODE_UPDATE_ONLY)
     326             :         {
     327             :             poDstFeature =
     328           3 :                 std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
     329           3 :             poDstFeature->SetFrom(poSrcFeature.get());
     330           3 :             if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
     331             :             {
     332           0 :                 bRet = false;
     333           0 :                 break;
     334             :             }
     335             :         }
     336             :     }
     337             : 
     338           7 :     poDstLayer->SetAttributeFilter(nullptr);
     339             : 
     340           7 :     return bRet;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                ~GDALVectorUpdateAlgorithmStandalone()                */
     345             : /************************************************************************/
     346             : 
     347             : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
     348             :     default;
     349             : 
     350             : /************************************************************************/
     351             : /*            GDALVectorUpdateAlgorithmStandalone::RunImpl()            */
     352             : /************************************************************************/
     353             : 
     354          16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
     355             :                                                   void *pProgressData)
     356             : {
     357          16 :     GDALPipelineStepRunContext stepCtxt;
     358          16 :     stepCtxt.m_pfnProgress = pfnProgress;
     359          16 :     stepCtxt.m_pProgressData = pProgressData;
     360          32 :     return RunStep(stepCtxt);
     361             : }
     362             : 
     363             : //! @endcond

Generated by: LCOV version 1.14