Coverage Report

Created: 2026-02-14 09:00

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