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 100 : GDALVectorSQLAlgorithm::GetConstructorOptions(bool standaloneStep)
34 : {
35 100 : ConstructorOptions opts;
36 100 : opts.SetStandaloneStep(standaloneStep);
37 100 : opts.SetOutputDatasetRequired(false);
38 100 : opts.SetAddInputLayerNameArgument(false);
39 100 : opts.SetAddOutputLayerNameArgument(false);
40 100 : opts.SetInputDatasetAlias("dataset");
41 100 : return opts;
42 : }
43 :
44 : /************************************************************************/
45 : /* GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm() */
46 : /************************************************************************/
47 :
48 100 : GDALVectorSQLAlgorithm::GDALVectorSQLAlgorithm(bool standaloneStep)
49 : : GDALVectorPipelineStepAlgorithm(NAME, DESCRIPTION, HELP_URL,
50 100 : GetConstructorOptions(standaloneStep))
51 : {
52 200 : auto &sqlArg = AddArg("sql", 0, _("SQL statement(s)"), &m_sql)
53 100 : .SetRequired()
54 100 : .SetPackedValuesAllowed(false)
55 100 : .SetReadFromFileAtSyntaxAllowed()
56 200 : .SetMetaVar("<statement>|@<filename>")
57 100 : .SetRemoveSQLCommentsEnabled();
58 100 : if (!standaloneStep)
59 40 : sqlArg.SetPositional();
60 : AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, standaloneStep ? 0 : 'l',
61 100 : _("Output layer name(s)"), &m_outputLayer);
62 100 : AddArg("dialect", 0, _("SQL dialect (e.g. OGRSQL, SQLITE)"), &m_dialect);
63 100 : }
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 12 : explicit GDALVectorSQLAlgorithmDataset(GDALDataset &oSrcDS)
80 12 : : m_oSrcDS(oSrcDS)
81 : {
82 12 : m_oSrcDS.Reference();
83 12 : }
84 :
85 24 : ~GDALVectorSQLAlgorithmDataset() override
86 12 : {
87 23 : for (OGRLayer *poLayer : m_layers)
88 11 : m_oSrcDS.ReleaseResultSet(poLayer);
89 12 : m_oSrcDS.ReleaseRef();
90 24 : }
91 :
92 11 : void AddLayer(OGRLayer *poLayer)
93 : {
94 11 : m_layers.push_back(poLayer);
95 11 : }
96 :
97 107 : int GetLayerCount() const override
98 : {
99 107 : return static_cast<int>(m_layers.size());
100 : }
101 :
102 34 : OGRLayer *GetLayer(int idx) const override
103 : {
104 34 : return idx >= 0 && idx < GetLayerCount() ? m_layers[idx] : nullptr;
105 : }
106 :
107 17 : bool TestCapability(const char *pszCap) const override
108 : {
109 17 : if (EQUAL(pszCap, ODsCCurveGeometries) ||
110 16 : EQUAL(pszCap, ODsCMeasuredGeometries) ||
111 14 : EQUAL(pszCap, ODsCZGeometries))
112 : {
113 5 : return true;
114 : }
115 :
116 12 : 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 19 : bool GDALVectorSQLAlgorithm::RunStep(GDALPipelineStepRunContext &)
250 : {
251 19 : auto poSrcDS = m_inputDataset[0].GetDatasetRef();
252 19 : CPLAssert(poSrcDS);
253 :
254 19 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
255 19 : 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, but no output dataset was specified. "
271 : "The result set will be ignored. You may silence "
272 : "this warning with the 'quiet' argument.",
273 : sql.c_str());
274 : }
275 3 : else if (CPLGetErrorCounter() > nErrorCounter &&
276 1 : CPLGetLastErrorType() == CE_Failure)
277 : {
278 1 : ReportError(CE_Failure, CPLE_AppDefined,
279 : "Execution of the SQL statement '%s' failed.%s",
280 : sql.c_str(),
281 1 : m_update ? ""
282 : : ".\nPerhaps you need to specify the "
283 : "'update' argument?");
284 1 : return false;
285 : }
286 : }
287 2 : return true;
288 : }
289 :
290 16 : CPLAssert(m_outputDataset.GetName().empty());
291 16 : CPLAssert(!m_outputDataset.GetDatasetRef());
292 :
293 16 : if (!m_outputLayer.empty() && m_outputLayer.size() != m_sql.size())
294 : {
295 1 : ReportError(CE_Failure, CPLE_AppDefined,
296 : "There should be as many layer names in --output-layer as "
297 : "in --statement");
298 1 : return false;
299 : }
300 :
301 15 : if (m_sql.size() == 1)
302 : {
303 12 : auto outDS = std::make_unique<GDALVectorSQLAlgorithmDataset>(*poSrcDS);
304 12 : outDS->SetDescription(poSrcDS->GetDescription());
305 :
306 12 : const auto nErrorCounter = CPLGetErrorCounter();
307 24 : OGRLayer *poLayer = poSrcDS->ExecuteSQL(
308 12 : m_sql[0].c_str(), nullptr,
309 14 : m_dialect.empty() ? nullptr : m_dialect.c_str());
310 12 : if (!poLayer)
311 : {
312 1 : if (nErrorCounter == CPLGetErrorCounter())
313 : {
314 1 : ReportError(CE_Failure, CPLE_AppDefined,
315 : "Execution of the SQL statement '%s' did not "
316 : "result in a result layer.",
317 1 : m_sql[0].c_str());
318 : }
319 1 : return false;
320 : }
321 :
322 11 : if (!m_outputLayer.empty())
323 : {
324 1 : const std::string &osLayerName = m_outputLayer[0];
325 1 : poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
326 1 : poLayer->SetDescription(osLayerName.c_str());
327 : }
328 11 : outDS->AddLayer(poLayer);
329 11 : m_outputDataset.Set(std::move(outDS));
330 : }
331 : else
332 : {
333 : // First pass to check all statements are valid and figure out layer
334 : // names
335 3 : std::set<std::string> setOutputLayerNames;
336 3 : std::vector<std::string> aosLayerNames;
337 8 : for (const std::string &sql : m_sql)
338 : {
339 6 : const auto nErrorCounter = CPLGetErrorCounter();
340 6 : auto poLayer = poSrcDS->ExecuteSQL(
341 : sql.c_str(), nullptr,
342 6 : m_dialect.empty() ? nullptr : m_dialect.c_str());
343 6 : if (!poLayer)
344 : {
345 1 : if (nErrorCounter == CPLGetErrorCounter())
346 : {
347 1 : ReportError(CE_Failure, CPLE_AppDefined,
348 : "Execution of the SQL statement '%s' did not "
349 : "result in a result layer.",
350 : sql.c_str());
351 : }
352 1 : return false;
353 : }
354 :
355 10 : std::string osLayerName;
356 :
357 5 : if (!m_outputLayer.empty())
358 : {
359 2 : osLayerName = m_outputLayer[aosLayerNames.size()];
360 : }
361 : else
362 : {
363 3 : osLayerName = poLayer->GetDescription();
364 3 : for (int num = 2;
365 4 : cpl::contains(setOutputLayerNames, osLayerName); ++num)
366 : {
367 1 : osLayerName = poLayer->GetDescription();
368 1 : osLayerName += std::to_string(num);
369 : }
370 : }
371 :
372 5 : if (!osLayerName.empty())
373 : {
374 5 : poLayer->GetLayerDefn()->SetName(osLayerName.c_str());
375 5 : poLayer->SetDescription(osLayerName.c_str());
376 : }
377 5 : setOutputLayerNames.insert(poLayer->GetDescription());
378 5 : aosLayerNames.push_back(poLayer->GetDescription());
379 :
380 5 : poSrcDS->ReleaseResultSet(poLayer);
381 : }
382 :
383 : auto outDS =
384 2 : std::make_unique<GDALVectorSQLAlgorithmDatasetMultiLayer>(*poSrcDS);
385 2 : outDS->SetDescription(poSrcDS->GetDescription());
386 :
387 6 : for (size_t i = 0; i < aosLayerNames.size(); ++i)
388 : {
389 4 : outDS->AddLayer(m_sql[i], m_dialect, aosLayerNames[i]);
390 : }
391 :
392 2 : m_outputDataset.Set(std::move(outDS));
393 : }
394 :
395 13 : return true;
396 : }
397 :
398 : GDALVectorSQLAlgorithmStandalone::~GDALVectorSQLAlgorithmStandalone() = default;
399 :
400 : //! @endcond
|