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