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