Coverage Report

Created: 2026-07-25 06:50

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