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

Generated by: LCOV version 1.14