Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_vector_sql.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  "sql" 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_sql.h"
14
15
#include "gdal_priv.h"
16
#include "ogrsf_frmts.h"
17
#include "ogrlayerpool.h"
18
19
#include <mutex>
20
#include <set>
21
22
//! @cond Doxygen_Suppress
23
24
#ifndef _
25
0
#define _(x) (x)
26
#endif
27
28
/************************************************************************/
29
/*           GDALVectorSQLAlgorithm::GetConstructorOptions()            */
30
/************************************************************************/
31
32
/* static */ GDALVectorSQLAlgorithm::ConstructorOptions
33
GDALVectorSQLAlgorithm::GetConstructorOptions(bool standaloneStep)
34
0
{
35
0
    ConstructorOptions opts;
36
0
    opts.SetStandaloneStep(standaloneStep);
37
0
    opts.SetOutputDatasetRequired(false);
38
0
    opts.SetAddInputLayerNameArgument(false);
39
0
    opts.SetAddOutputLayerNameArgument(false);
40
0
    return opts;
41
0
}
42
43
/************************************************************************/
44
/*           GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm()           */
45
/************************************************************************/
46
47
GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm(bool standaloneStep)
48
0
    : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
49
0
                                      GetConstructorOptions(standaloneStep))
50
0
{
51
0
    auto &sqlArg = AddArg("sql", 0, _("SQL statement(s)"), &m_sql)
52
0
                       .SetRequired()
53
0
                       .SetPackedValuesAllowed(false)
54
0
                       .SetReadFromFileAtSyntaxAllowed()
55
0
                       .SetMetaVar("<statement>|@<filename>")
56
0
                       .SetRemoveSQLCommentsEnabled();
57
0
    if (!standaloneStep)
58
0
        sqlArg.SetPositional();
59
0
    AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, standaloneStep ? 0 : 'l',
60
0
           _("Output layer name(s)"), &m_outputLayer);
61
0
    AddArg("dialect", 0, _("SQL dialect (e.g. OGRSQL, SQLITE)"), &m_dialect);
62
0
}
63
64
/************************************************************************/
65
/*                    GDALVectorSQLAlgorithmDataset                     */
66
/************************************************************************/
67
68
namespace
69
{
70
class GDALVectorSQLAlgorithmDataset final : public GDALDataset
71
{
72
    GDALDataset &m_oSrcDS;
73
    std::vector<OGRLayer *> m_layers{};
74
75
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDataset)
76
77
  public:
78
    explicit GDALVectorSQLAlgorithmDataset(GDALDataset &oSrcDS)
79
0
        : m_oSrcDS(oSrcDS)
80
0
    {
81
0
        m_oSrcDS.Reference();
82
0
    }
83
84
    ~GDALVectorSQLAlgorithmDataset() override
85
0
    {
86
0
        for (OGRLayer *poLayer : m_layers)
87
0
            m_oSrcDS.ReleaseResultSet(poLayer);
88
0
        m_oSrcDS.ReleaseRef();
89
0
    }
90
91
    void AddLayer(OGRLayer *poLayer)
92
0
    {
93
0
        m_layers.push_back(poLayer);
94
0
    }
95
96
    int GetLayerCount() const override
97
0
    {
98
0
        return static_cast<int>(m_layers.size());
99
0
    }
100
101
    OGRLayer *GetLayer(int idx) const override
102
0
    {
103
0
        return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
104
0
    }
105
};
106
}  // namespace
107
108
/************************************************************************/
109
/*               GDALVectorSQLAlgorithmDatasetMultiLayer                */
110
/************************************************************************/
111
112
namespace
113
{
114
115
class ProxiedSQLLayer final : public OGRProxiedLayer
116
{
117
    mutable OGRFeatureDefn *m_poLayerDefn = nullptr;
118
    mutable std::mutex m_oMutex{};
119
120
    CPL_DISALLOW_COPY_ASSIGN(ProxiedSQLLayer)
121
122
  public:
123
    ProxiedSQLLayer(const std::string &osName, OGRLayerPool *poPoolIn,
124
                    OpenLayerFunc pfnOpenLayerIn,
125
                    ReleaseLayerFunc pfnReleaseLayerIn,
126
                    FreeUserDataFunc pfnFreeUserDataIn, void *pUserDataIn)
127
0
        : OGRProxiedLayer(poPoolIn, pfnOpenLayerIn, pfnReleaseLayerIn,
128
0
                          pfnFreeUserDataIn, pUserDataIn)
129
0
    {
130
0
        SetDescription(osName.c_str());
131
0
    }
132
133
    ~ProxiedSQLLayer() override
134
0
    {
135
0
        if (m_poLayerDefn)
136
0
            m_poLayerDefn->Release();
137
0
    }
138
139
    const char *GetName() const override
140
0
    {
141
0
        return GetDescription();
142
0
    }
143
144
    const OGRFeatureDefn *GetLayerDefn() const override
145
0
    {
146
0
        std::lock_guard oLock(m_oMutex);
147
148
0
        if (!m_poLayerDefn)
149
0
        {
150
0
            m_poLayerDefn = OGRProxiedLayer::GetLayerDefn()->Clone();
151
0
            m_poLayerDefn->SetName(GetDescription());
152
0
        }
153
0
        return m_poLayerDefn;
154
0
    }
155
};
156
157
class GDALVectorSQLAlgorithmDatasetMultiLayer final : public GDALDataset
158
{
159
    // We can't safely have 2 SQL layers active simultaneously on the same
160
    // source dataset. So each time we access one, we must close the last
161
    // active one.
162
    OGRLayerPool m_oPool{1};
163
    GDALDataset &m_oSrcDS;
164
    std::vector<std::unique_ptr<ProxiedSQLLayer>> m_layers{};
165
166
    struct UserData
167
    {
168
        GDALDataset &oSrcDS;
169
        std::string osSQL{};
170
        std::string osDialect{};
171
        std::string osLayerName{};
172
173
        UserData(GDALDataset &oSrcDSIn, const std::string &osSQLIn,
174
                 const std::string &osDialectIn,
175
                 const std::string &osLayerNameIn)
176
0
            : oSrcDS(oSrcDSIn), osSQL(osSQLIn), osDialect(osDialectIn),
177
0
              osLayerName(osLayerNameIn)
178
0
        {
179
0
        }
180
        CPL_DISALLOW_COPY_ASSIGN(UserData)
181
    };
182
183
    CPL_DISALLOW_COPY_ASSIGN(GDALVectorSQLAlgorithmDatasetMultiLayer)
184
185
  public:
186
    explicit GDALVectorSQLAlgorithmDatasetMultiLayer(GDALDataset &oSrcDS)
187
0
        : m_oSrcDS(oSrcDS)
188
0
    {
189
0
        m_oSrcDS.Reference();
190
0
    }
191
192
    ~GDALVectorSQLAlgorithmDatasetMultiLayer() override
193
0
    {
194
0
        m_layers.clear();
195
0
        m_oSrcDS.ReleaseRef();
196
0
    }
197
198
    void AddLayer(const std::string &osSQL, const std::string &osDialect,
199
                  const std::string &osLayerName)
200
0
    {
201
0
        const auto OpenLayer = [](void *pUserDataIn)
202
0
        {
203
0
            UserData *pUserData = static_cast<UserData *>(pUserDataIn);
204
0
            return pUserData->oSrcDS.ExecuteSQL(
205
0
                pUserData->osSQL.c_str(), nullptr,
206
0
                pUserData->osDialect.empty() ? nullptr
207
0
                                             : pUserData->osDialect.c_str());
208
0
        };
209
210
0
        const auto CloseLayer = [](OGRLayer *poLayer, void *pUserDataIn)
211
0
        {
212
0
            UserData *pUserData = static_cast<UserData *>(pUserDataIn);
213
0
            pUserData->oSrcDS.ReleaseResultSet(poLayer);
214
0
        };
215
216
0
        const auto DeleteUserData = [](void *pUserDataIn)
217
0
        { delete static_cast<UserData *>(pUserDataIn); };
218
219
0
        auto pUserData = new UserData(m_oSrcDS, osSQL, osDialect, osLayerName);
220
0
        m_layers.emplace_back(std::make_unique<ProxiedSQLLayer>(
221
0
            osLayerName, &m_oPool, OpenLayer, CloseLayer, DeleteUserData,
222
0
            pUserData));
223
0
    }
224
225
    int GetLayerCount() const override
226
0
    {
227
0
        return static_cast<int>(m_layers.size());
228
0
    }
229
230
    OGRLayer *GetLayer(int idx) const override
231
0
    {
232
0
        return idx >= 0 && idx < GetLayerCount() ? m_layers[idx].get()
233
0
                                                 : nullptr;
234
0
    }
235
};
236
}  // namespace
237
238
/************************************************************************/
239
/*                  GDALVectorSQLAlgorithm::RunStep()                   */
240
/************************************************************************/
241
242
bool GDALVectorSQLAlgorithm::RunStep(GDALPipelineStepRunContext &)
243
0
{
244
0
    auto poSrcDS = m_inputDataset[0].GetDatasetRef();
245
0
    CPLAssert(poSrcDS);
246
247
0
    auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
248
0
    if (outputArg && !outputArg->IsExplicitlySet())
249
0
    {
250
        // Mode where we update a dataset.
251
0
        for (const auto &sql : m_sql)
252
0
        {
253
0
            const auto nErrorCounter = CPLGetErrorCounter();
254
0
            OGRLayer *poLayer = poSrcDS->ExecuteSQL(
255
0
                sql.c_str(), nullptr,
256
0
                m_dialect.empty() ? nullptr : m_dialect.c_str());
257
0
            const bool bResultSet = poLayer != nullptr;
258
0
            poSrcDS->ReleaseResultSet(poLayer);
259
0
            if (bResultSet && !m_quiet)
260
0
            {
261
0
                ReportError(CE_Warning, CPLE_AppDefined,
262
0
                            "Execution of the SQL statement '%s' returned a "
263
0
                            "result set. It will be ignored. You may silence "
264
0
                            "this warning with the 'quiet' argument.",
265
0
                            sql.c_str());
266
0
            }
267
0
            else if (CPLGetErrorCounter() > nErrorCounter &&
268
0
                     CPLGetLastErrorType() == CE_Failure)
269
0
            {
270
0
                ReportError(CE_Failure, CPLE_AppDefined,
271
0
                            "Execution of the SQL statement '%s' failed.%s",
272
0
                            sql.c_str(),
273
0
                            m_update ? ""
274
0
                                     : " Perhaps you need to specify the "
275
0
                                       "'update' argument?");
276
0
                return false;
277
0
            }
278
0
        }
279
0
        return true;
280
0
    }
281
282
0
    CPLAssert(m_outputDataset.GetName().empty());
283
0
    CPLAssert(!m_outputDataset.GetDatasetRef());
284
285
0
    if (!m_outputLayer.empty() && m_outputLayer.size() != m_sql.size())
286
0
    {
287
0
        ReportError(CE_Failure, CPLE_AppDefined,
288
0
                    "There should be as many layer names in --output-layer as "
289
0
                    "in --statement");
290
0
        return false;
291
0
    }
292
293
0
    if (m_sql.size() == 1)
294
0
    {
295
0
        auto outDS = std::make_unique<GDALVectorSQLAlgorithmDataset>(*poSrcDS);
296
0
        outDS->SetDescription(poSrcDS->GetDescription());
297
298
0
        const auto nErrorCounter = CPLGetErrorCounter();
299
0
        OGRLayer *poLayer = poSrcDS->ExecuteSQL(
300
0
            m_sql[0].c_str(), nullptr,
301
0
            m_dialect.empty() ? nullptr : m_dialect.c_str());
302
0
        if (!poLayer)
303
0
        {
304
0
            if (nErrorCounter == CPLGetErrorCounter())
305
0
            {
306
0
                ReportError(CE_Failure, CPLE_AppDefined,
307
0
                            "Execution of the SQL statement '%s' did not "
308
0
                            "result in a result layer.",
309
0
                            m_sql[0].c_str());
310
0
            }
311
0
            return false;
312
0
        }
313
314
0
        if (!m_outputLayer.empty())
315
0
        {
316
0
            const std::string &osLayerName = m_outputLayer[0];
317
0
            poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
318
0
            poLayer->SetDescription(osLayerName.c_str());
319
0
        }
320
0
        outDS->AddLayer(poLayer);
321
0
        m_outputDataset.Set(std::move(outDS));
322
0
    }
323
0
    else
324
0
    {
325
        // First pass to check all statements are valid and figure out layer
326
        // names
327
0
        std::set<std::string> setOutputLayerNames;
328
0
        std::vector<std::string> aosLayerNames;
329
0
        for (const std::string &sql : m_sql)
330
0
        {
331
0
            const auto nErrorCounter = CPLGetErrorCounter();
332
0
            auto poLayer = poSrcDS->ExecuteSQL(
333
0
                sql.c_str(), nullptr,
334
0
                m_dialect.empty() ? nullptr : m_dialect.c_str());
335
0
            if (!poLayer)
336
0
            {
337
0
                if (nErrorCounter == CPLGetErrorCounter())
338
0
                {
339
0
                    ReportError(CE_Failure, CPLE_AppDefined,
340
0
                                "Execution of the SQL statement '%s' did not "
341
0
                                "result in a result layer.",
342
0
                                sql.c_str());
343
0
                }
344
0
                return false;
345
0
            }
346
347
0
            std::string osLayerName;
348
349
0
            if (!m_outputLayer.empty())
350
0
            {
351
0
                osLayerName = m_outputLayer[aosLayerNames.size()];
352
0
            }
353
0
            else
354
0
            {
355
0
                osLayerName = poLayer->GetDescription();
356
0
                for (int num = 2;
357
0
                     cpl::contains(setOutputLayerNames, osLayerName); ++num)
358
0
                {
359
0
                    osLayerName = poLayer->GetDescription();
360
0
                    osLayerName += std::to_string(num);
361
0
                }
362
0
            }
363
364
0
            if (!osLayerName.empty())
365
0
            {
366
0
                poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
367
0
                poLayer->SetDescription(osLayerName.c_str());
368
0
            }
369
0
            setOutputLayerNames.insert(poLayer->GetDescription());
370
0
            aosLayerNames.push_back(poLayer->GetDescription());
371
372
0
            poSrcDS->ReleaseResultSet(poLayer);
373
0
        }
374
375
0
        auto outDS =
376
0
            std::make_unique<GDALVectorSQLAlgorithmDatasetMultiLayer>(*poSrcDS);
377
0
        outDS->SetDescription(poSrcDS->GetDescription());
378
379
0
        for (size_t i = 0; i < aosLayerNames.size(); ++i)
380
0
        {
381
0
            outDS->AddLayer(m_sql[i], m_dialect, aosLayerNames[i]);
382
0
        }
383
384
0
        m_outputDataset.Set(std::move(outDS));
385
0
    }
386
387
0
    return true;
388
0
}
389
390
0
GDALVectorSQLAlgorithmStandalone::~GDALVectorSQLAlgorithmStandalone() = default;
391
392
//! @endcond