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