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