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-06-03 12:46:18 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 :         m_inputLayerNames.push_back(poSrcDS->GetLayer(0)->GetName());
     100             :     }
     101          15 :     if (m_outputLayerName.empty() && poDstDS->GetLayerCount() == 1)
     102             :     {
     103          10 :         m_outputLayerName = poDstDS->GetLayer(0)->GetName();
     104             :     }
     105             : 
     106          15 :     if (m_inputLayerNames.empty())
     107             :     {
     108           2 :         if (!m_outputLayerName.empty())
     109             :         {
     110           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     111             :                         "Please specify the 'input-layer' argument.");
     112           1 :             return false;
     113             :         }
     114             :         else
     115             :         {
     116           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     117             :                         "Please specify the 'input-layer' and 'output-layer' "
     118             :                         "arguments.");
     119           1 :             return false;
     120             :         }
     121             :     }
     122             : 
     123          13 :     auto poSrcLayer = poSrcDS->GetLayerByName(m_inputLayerNames[0].c_str());
     124          13 :     if (!poSrcLayer)
     125             :     {
     126           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     127             :                     "No layer named '%s' in input dataset.",
     128           1 :                     m_inputLayerNames[0].c_str());
     129           1 :         return false;
     130             :     }
     131             : 
     132          12 :     if (m_outputLayerName.empty())
     133             :     {
     134           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     135             :                     "Please specify the 'output-layer' argument.");
     136           1 :         return false;
     137             :     }
     138             : 
     139          11 :     auto poDstLayer = poDstDS->GetLayerByName(m_outputLayerName.c_str());
     140          11 :     if (!poDstLayer)
     141             :     {
     142           1 :         ReportError(CE_Failure, CPLE_AppDefined,
     143             :                     "No layer named '%s' in output dataset",
     144             :                     m_outputLayerName.c_str());
     145           1 :         return false;
     146             :     }
     147             : 
     148          20 :     std::vector<int> srcKeyFieldIndices;
     149          20 :     std::vector<OGRFieldType> keyFieldTypes;
     150          10 :     if (m_key.empty())
     151           6 :         m_key.push_back(SpecialFieldNames[SPF_FID]);
     152          20 :     for (const std::string &key : m_key)
     153             :     {
     154          13 :         if (EQUAL(key.c_str(), SpecialFieldNames[SPF_FID]))
     155             :         {
     156           6 :             srcKeyFieldIndices.push_back(
     157           6 :                 poSrcLayer->GetLayerDefn()->GetFieldCount() + SPF_FID);
     158           6 :             keyFieldTypes.push_back(OFTInteger64);
     159           6 :             continue;
     160             :         }
     161             : 
     162             :         const int nSrcIdx =
     163           7 :             poSrcLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     164           7 :         if (nSrcIdx < 0)
     165             :         {
     166           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     167             :                         "Cannot find field '%s' in input layer", key.c_str());
     168           3 :             return false;
     169             :         }
     170           6 :         srcKeyFieldIndices.push_back(nSrcIdx);
     171             :         const auto poSrcFieldDefn =
     172           6 :             poSrcLayer->GetLayerDefn()->GetFieldDefn(nSrcIdx);
     173           6 :         const auto eType = poSrcFieldDefn->GetType();
     174           6 :         const OGRFieldType aeAllowedTypes[] = {OFTString, OFTInteger,
     175             :                                                OFTInteger64, OFTReal};
     176           6 :         if (std::find(std::begin(aeAllowedTypes), std::end(aeAllowedTypes),
     177           6 :                       eType) == std::end(aeAllowedTypes))
     178             :         {
     179           1 :             ReportError(CE_Failure, CPLE_NotSupported,
     180             :                         "Type of field '%s' is not one of those supported for "
     181             :                         "a key field: String, Integer, Integer64, Real",
     182             :                         key.c_str());
     183           1 :             return false;
     184             :         }
     185             : 
     186             :         const int nDstIdx =
     187           5 :             poDstLayer->GetLayerDefn()->GetFieldIndex(key.c_str());
     188           5 :         if (nDstIdx < 0)
     189             :         {
     190           1 :             ReportError(CE_Failure, CPLE_AppDefined,
     191             :                         "Cannot find field '%s' in output layer", key.c_str());
     192           1 :             return false;
     193             :         }
     194             :         const auto poDstFieldDefn =
     195           4 :             poDstLayer->GetLayerDefn()->GetFieldDefn(nDstIdx);
     196           4 :         if (poDstFieldDefn->GetType() != eType)
     197             :         {
     198           0 :             ReportError(
     199             :                 CE_Failure, CPLE_NotSupported,
     200             :                 "Type of field '%s' is not the same in input and output layers",
     201             :                 key.c_str());
     202           0 :             return false;
     203             :         }
     204           4 :         keyFieldTypes.push_back(eType);
     205             :     }
     206             : 
     207          13 :     const bool bFIDMatch = m_key.size() == 1 &&
     208           6 :                            EQUAL(m_key[0].c_str(), SpecialFieldNames[SPF_FID]);
     209             :     const GIntBig nFeatureCount =
     210           7 :         ctxt.m_pfnProgress ? poSrcLayer->GetFeatureCount(true) : -1;
     211             : 
     212           7 :     std::string osFilter;
     213           7 :     int nIter = 0;
     214           7 :     bool bRet = true;
     215          21 :     for (const auto &poSrcFeature : *poSrcLayer)
     216             :     {
     217          14 :         ++nIter;
     218          20 :         if (ctxt.m_pfnProgress && nFeatureCount > 0 &&
     219           6 :             !ctxt.m_pfnProgress(static_cast<double>(nIter) / nFeatureCount, "",
     220             :                                 ctxt.m_pProgressData))
     221             :         {
     222           1 :             ReportError(CE_Failure, CPLE_UserInterrupt, "Interrupted by user");
     223           1 :             bRet = false;
     224           1 :             break;
     225             :         }
     226             : 
     227           0 :         std::unique_ptr<OGRFeature> poDstFeature;
     228          13 :         if (bFIDMatch)
     229             :         {
     230          16 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     231           8 :             poDstFeature.reset(poDstLayer->GetFeature(poSrcFeature->GetFID()));
     232             :         }
     233             :         else
     234             :         {
     235           5 :             bool bSkip = false;
     236           5 :             osFilter.clear();
     237          21 :             for (size_t iField = 0; iField < srcKeyFieldIndices.size();
     238             :                  ++iField)
     239             :             {
     240          17 :                 const int nSrcFieldIdx = srcKeyFieldIndices[iField];
     241          17 :                 if (!poSrcFeature->IsFieldSet(nSrcFieldIdx))
     242             :                 {
     243           1 :                     bSkip = true;
     244           1 :                     break;
     245             :                 }
     246          16 :                 if (!osFilter.empty())
     247          12 :                     osFilter += " AND ";
     248          16 :                 osFilter += CPLString(m_key[iField]).SQLQuotedIdentifier();
     249          16 :                 osFilter += " = ";
     250          16 :                 switch (keyFieldTypes[iField])
     251             :                 {
     252           4 :                     case OFTString:
     253             :                     {
     254           4 :                         osFilter += CPLString(poSrcFeature->GetFieldAsString(
     255             :                                                   nSrcFieldIdx))
     256           4 :                                         .SQLQuotedLiteral();
     257           4 :                         break;
     258             :                     }
     259             : 
     260           4 :                     case OFTReal:
     261             :                     {
     262             :                         osFilter += CPLSPrintf(
     263             :                             "%.17g",
     264           4 :                             poSrcFeature->GetFieldAsDouble(nSrcFieldIdx));
     265           4 :                         break;
     266             :                     }
     267             : 
     268           8 :                     default:
     269             :                     {
     270             :                         osFilter += CPLSPrintf(
     271             :                             CPL_FRMT_GIB,
     272           8 :                             poSrcFeature->GetFieldAsInteger64(nSrcFieldIdx));
     273           8 :                         break;
     274             :                     }
     275             :                 }
     276             :             }
     277           5 :             if (bSkip)
     278           1 :                 continue;
     279           4 :             if (poDstLayer->SetAttributeFilter(osFilter.c_str()) != OGRERR_NONE)
     280             :             {
     281           0 :                 bRet = false;
     282           0 :                 break;
     283             :             }
     284           4 :             poDstFeature.reset(poDstLayer->GetNextFeature());
     285           4 :             if (poDstFeature)
     286             :             {
     287             :                 // Check there is only one feature matching the criterion
     288           3 :                 if (std::unique_ptr<OGRFeature>(poDstLayer->GetNextFeature()))
     289             :                 {
     290           1 :                     poDstFeature.reset();
     291             :                 }
     292             :                 else
     293             :                 {
     294           2 :                     CPLDebugOnly("GDAL",
     295             :                                  "Updating output feature " CPL_FRMT_GIB
     296             :                                  " with src input " CPL_FRMT_GIB,
     297             :                                  poDstFeature->GetFID(),
     298             :                                  poSrcFeature->GetFID());
     299             :                 }
     300             :             }
     301             :         }
     302             : 
     303          12 :         if (poDstFeature)
     304             :         {
     305           6 :             if (m_mode != MODE_APPEND_ONLY)
     306             :             {
     307             :                 auto poDstFeatureOri =
     308           5 :                     std::unique_ptr<OGRFeature>(poDstFeature->Clone());
     309           5 :                 const auto nDstFID = poDstFeature->GetFID();
     310           5 :                 poDstFeature->SetFrom(poSrcFeature.get());
     311             :                 // restore FID unset by SetFrom()
     312           5 :                 poDstFeature->SetFID(nDstFID);
     313          10 :                 if (!poDstFeature->Equal(poDstFeatureOri.get()) &&
     314           5 :                     poDstLayer->SetFeature(poDstFeature.get()) != OGRERR_NONE)
     315             :                 {
     316           0 :                     bRet = false;
     317           0 :                     break;
     318             :                 }
     319             :             }
     320             :         }
     321           6 :         else if (m_mode != MODE_UPDATE_ONLY)
     322             :         {
     323             :             poDstFeature =
     324           3 :                 std::make_unique<OGRFeature>(poDstLayer->GetLayerDefn());
     325           3 :             poDstFeature->SetFrom(poSrcFeature.get());
     326           3 :             if (poDstLayer->CreateFeature(poDstFeature.get()) != OGRERR_NONE)
     327             :             {
     328           0 :                 bRet = false;
     329           0 :                 break;
     330             :             }
     331             :         }
     332             :     }
     333             : 
     334           7 :     poDstLayer->SetAttributeFilter(nullptr);
     335             : 
     336           7 :     return bRet;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                ~GDALVectorUpdateAlgorithmStandalone()                */
     341             : /************************************************************************/
     342             : 
     343             : GDALVectorUpdateAlgorithmStandalone::~GDALVectorUpdateAlgorithmStandalone() =
     344             :     default;
     345             : 
     346             : /************************************************************************/
     347             : /*            GDALVectorUpdateAlgorithmStandalone::RunImpl()            */
     348             : /************************************************************************/
     349             : 
     350          16 : bool GDALVectorUpdateAlgorithmStandalone::RunImpl(GDALProgressFunc pfnProgress,
     351             :                                                   void *pProgressData)
     352             : {
     353          16 :     GDALPipelineStepRunContext stepCtxt;
     354          16 :     stepCtxt.m_pfnProgress = pfnProgress;
     355          16 :     stepCtxt.m_pProgressData = pProgressData;
     356          32 :     return RunStep(stepCtxt);
     357             : }
     358             : 
     359             : //! @endcond

Generated by: LCOV version 1.14