Coverage Report

Created: 2026-04-10 07:04

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