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