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