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