Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: WFS Translator
4 : * Purpose: Implements OGRWFSJoinLayer class.
5 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2015, Even Rouault <even dot rouault at spatialys dot com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_wfs.h"
14 : #include "ogrwfsfilter.h"
15 : #include "cpl_md5.h"
16 :
17 : /************************************************************************/
18 : /* OGRWFSJoinLayer() */
19 : /************************************************************************/
20 :
21 28 : OGRWFSJoinLayer::OGRWFSJoinLayer(OGRWFSDataSource *poDSIn,
22 : const swq_select *psSelectInfo,
23 28 : const CPLString &osGlobalFilterIn)
24 : : poDS(poDSIn), osGlobalFilter(osGlobalFilterIn),
25 28 : bDistinct(psSelectInfo->query_mode == SWQM_DISTINCT_LIST),
26 : // If changing that, change in the GML driver too
27 28 : m_osTmpDir(VSIMemGenerateHiddenFilename("_ogr_wfs_"))
28 : {
29 56 : CPLString osName("join_");
30 56 : CPLString osLayerName = psSelectInfo->table_defs[0].table_name;
31 56 : apoLayers.push_back(
32 28 : cpl::down_cast<OGRWFSLayer *>(poDS->GetLayerByName(osLayerName)));
33 28 : osName += osLayerName;
34 56 : for (int i = 0; i < psSelectInfo->join_count; i++)
35 : {
36 28 : osName += "_";
37 : osLayerName =
38 28 : psSelectInfo->table_defs[psSelectInfo->join_defs[i].secondary_table]
39 28 : .table_name;
40 56 : apoLayers.push_back(
41 28 : cpl::down_cast<OGRWFSLayer *>(poDS->GetLayerByName(osLayerName)));
42 28 : osName += osLayerName;
43 : }
44 :
45 28 : osFeatureTypes = "(";
46 84 : for (size_t i = 0; i < apoLayers.size(); i++)
47 : {
48 56 : if (i > 0)
49 28 : osFeatureTypes += ",";
50 56 : osFeatureTypes += apoLayers[i]->GetName();
51 : }
52 28 : osFeatureTypes += ")";
53 :
54 28 : SetDescription(osName);
55 :
56 28 : poFeatureDefn = new OGRFeatureDefn(GetDescription());
57 28 : poFeatureDefn->Reference();
58 28 : poFeatureDefn->SetGeomType(wkbNone);
59 :
60 194 : for (int i = 0; i < psSelectInfo->result_columns(); i++)
61 : {
62 166 : const swq_col_def *def = &psSelectInfo->column_defs[i];
63 166 : int table_index = 0;
64 166 : if (def->table_index >= 0)
65 160 : table_index = def->table_index;
66 : else
67 : {
68 6 : CPLAssert(def->expr->eNodeType == SNT_OPERATION &&
69 : def->expr->nOperation == SWQ_CAST);
70 6 : table_index = def->expr->papoSubExpr[0]->table_index;
71 : }
72 166 : OGRWFSLayer *poLayer = apoLayers[table_index];
73 166 : const char *pszTableAlias =
74 166 : psSelectInfo->table_defs[table_index].table_alias;
75 : const char *pszTablePrefix =
76 166 : pszTableAlias ? pszTableAlias : poLayer->GetShortName();
77 166 : int idx = poLayer->GetLayerDefn()->GetFieldIndex(def->field_name);
78 166 : if (idx >= 0)
79 : {
80 228 : OGRFieldDefn oFieldDefn(poLayer->GetLayerDefn()->GetFieldDefn(idx));
81 114 : const char *pszSrcFieldname = CPLSPrintf(
82 : "%s.%s", poLayer->GetShortName(), oFieldDefn.GetNameRef());
83 : const char *pszFieldname =
84 114 : CPLSPrintf("%s.%s", pszTablePrefix, oFieldDefn.GetNameRef());
85 114 : aoSrcFieldNames.push_back(pszSrcFieldname);
86 114 : oFieldDefn.SetName(def->field_alias ? def->field_alias
87 : : pszFieldname);
88 114 : if (def->expr && def->expr->eNodeType == SNT_OPERATION &&
89 6 : def->expr->nOperation == SWQ_CAST)
90 : {
91 6 : switch (def->field_type)
92 : {
93 2 : case SWQ_INTEGER:
94 2 : oFieldDefn.SetType(OFTInteger);
95 2 : break;
96 2 : case SWQ_INTEGER64:
97 2 : oFieldDefn.SetType(OFTInteger64);
98 2 : break;
99 2 : case SWQ_FLOAT:
100 2 : oFieldDefn.SetType(OFTReal);
101 2 : break;
102 0 : case SWQ_STRING:
103 0 : oFieldDefn.SetType(OFTString);
104 0 : break;
105 0 : case SWQ_BOOLEAN:
106 0 : oFieldDefn.SetType(OFTInteger);
107 0 : oFieldDefn.SetSubType(OFSTBoolean);
108 0 : break;
109 0 : case SWQ_DATE:
110 0 : oFieldDefn.SetType(OFTDate);
111 0 : break;
112 0 : case SWQ_TIME:
113 0 : oFieldDefn.SetType(OFTTime);
114 0 : break;
115 0 : case SWQ_TIMESTAMP:
116 0 : oFieldDefn.SetType(OFTDateTime);
117 0 : break;
118 0 : default:
119 0 : break;
120 : }
121 : }
122 114 : poFeatureDefn->AddFieldDefn(&oFieldDefn);
123 : }
124 : else
125 : {
126 52 : idx = poLayer->GetLayerDefn()->GetGeomFieldIndex(def->field_name);
127 52 : if (idx >= 0)
128 : {
129 : OGRGeomFieldDefn oFieldDefn(
130 104 : poLayer->GetLayerDefn()->GetGeomFieldDefn(idx));
131 52 : const char *pszSrcFieldname = CPLSPrintf(
132 : "%s.%s", poLayer->GetShortName(), oFieldDefn.GetNameRef());
133 52 : const char *pszFieldname = CPLSPrintf("%s.%s", pszTablePrefix,
134 : oFieldDefn.GetNameRef());
135 52 : aoSrcGeomFieldNames.push_back(pszSrcFieldname);
136 52 : oFieldDefn.SetName(def->field_alias ? def->field_alias
137 : : pszFieldname);
138 52 : poFeatureDefn->AddGeomFieldDefn(&oFieldDefn);
139 : }
140 : }
141 : }
142 :
143 30 : for (int i = 0; i < psSelectInfo->order_specs; i++)
144 : {
145 2 : int nFieldIndex = apoLayers[0]->GetLayerDefn()->GetFieldIndex(
146 2 : psSelectInfo->order_defs[i].field_name);
147 2 : if (nFieldIndex < 0)
148 0 : break;
149 :
150 : /* Make sure to have the right case */
151 2 : const char *pszFieldName = apoLayers[0]
152 2 : ->GetLayerDefn()
153 2 : ->GetFieldDefn(nFieldIndex)
154 2 : ->GetNameRef();
155 2 : if (!osSortBy.empty())
156 0 : osSortBy += ",";
157 2 : osSortBy += pszFieldName;
158 2 : if (!psSelectInfo->order_defs[i].ascending_flag)
159 2 : osSortBy += " DESC";
160 : }
161 :
162 : CPLXMLNode *psGlobalSchema =
163 28 : CPLCreateXMLNode(nullptr, CXT_Element, "Schema");
164 84 : for (auto *poLayer : apoLayers)
165 : {
166 : CPLString osTmpFileName =
167 56 : CPLSPrintf("%s/file.xsd", poLayer->GetTmpDir().c_str());
168 56 : CPLPushErrorHandler(CPLQuietErrorHandler);
169 56 : CPLXMLNode *psSchema = CPLParseXMLFile(osTmpFileName);
170 56 : CPLPopErrorHandler();
171 56 : if (psSchema == nullptr)
172 : {
173 0 : CPLDestroyXMLNode(psGlobalSchema);
174 0 : psGlobalSchema = nullptr;
175 0 : break;
176 : }
177 56 : CPLXMLNode *psIter = psSchema->psChild; // Used after for.
178 336 : for (; psIter != nullptr; psIter = psIter->psNext)
179 : {
180 336 : if (psIter->eType == CXT_Element)
181 56 : break;
182 : }
183 56 : CPLAddXMLChild(psGlobalSchema, CPLCloneXMLTree(psIter));
184 56 : CPLDestroyXMLNode(psSchema);
185 : }
186 28 : if (psGlobalSchema)
187 : {
188 56 : CPLString osTmpFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
189 28 : CPLSerializeXMLTreeToFile(psGlobalSchema, osTmpFileName);
190 28 : CPLDestroyXMLNode(psGlobalSchema);
191 : }
192 28 : }
193 :
194 : /************************************************************************/
195 : /* ~OGRWFSJoinLayer() */
196 : /************************************************************************/
197 :
198 56 : OGRWFSJoinLayer::~OGRWFSJoinLayer()
199 : {
200 28 : if (poFeatureDefn != nullptr)
201 28 : poFeatureDefn->Release();
202 28 : if (poBaseDS != nullptr)
203 14 : GDALClose(poBaseDS);
204 :
205 28 : VSIRmdirRecursive(m_osTmpDir.c_str());
206 56 : }
207 :
208 : /************************************************************************/
209 : /* OGRWFSRemoveReferenceToTableAlias() */
210 : /************************************************************************/
211 :
212 122 : static void OGRWFSRemoveReferenceToTableAlias(swq_expr_node *poNode,
213 : const swq_select *psSelectInfo)
214 : {
215 122 : if (poNode->eNodeType == SNT_COLUMN)
216 : {
217 68 : if (poNode->table_name != nullptr)
218 : {
219 102 : for (int i = 0; i < psSelectInfo->table_count; i++)
220 : {
221 102 : if (psSelectInfo->table_defs[i].table_alias != nullptr &&
222 102 : EQUAL(poNode->table_name,
223 : psSelectInfo->table_defs[i].table_alias))
224 : {
225 68 : CPLFree(poNode->table_name);
226 68 : poNode->table_name =
227 68 : CPLStrdup(psSelectInfo->table_defs[i].table_name);
228 68 : break;
229 : }
230 : }
231 : }
232 : }
233 54 : else if (poNode->eNodeType == SNT_OPERATION)
234 : {
235 128 : for (int i = 0; i < poNode->nSubExprCount; i++)
236 84 : OGRWFSRemoveReferenceToTableAlias(poNode->papoSubExpr[i],
237 : psSelectInfo);
238 : }
239 122 : }
240 :
241 : /************************************************************************/
242 : /* Build() */
243 : /************************************************************************/
244 :
245 34 : OGRWFSJoinLayer *OGRWFSJoinLayer::Build(OGRWFSDataSource *poDS,
246 : const swq_select *psSelectInfo)
247 : {
248 68 : CPLString osGlobalFilter;
249 :
250 224 : for (int i = 0; i < psSelectInfo->result_columns(); i++)
251 : {
252 192 : const swq_col_def *def = &psSelectInfo->column_defs[i];
253 192 : if (!(def->col_func == SWQCF_NONE &&
254 192 : (def->expr == nullptr || def->expr->eNodeType == SNT_COLUMN ||
255 8 : (def->expr->eNodeType == SNT_OPERATION &&
256 6 : def->expr->nOperation == SWQ_CAST))))
257 : {
258 2 : CPLError(CE_Failure, CPLE_NotSupported,
259 : "Only column names supported in column selection");
260 2 : return nullptr;
261 : }
262 : }
263 :
264 32 : if (psSelectInfo->join_count > 1 || psSelectInfo->where_expr != nullptr)
265 6 : osGlobalFilter += "<And>";
266 62 : for (int i = 0; i < psSelectInfo->join_count; i++)
267 : {
268 32 : OGRWFSRemoveReferenceToTableAlias(psSelectInfo->join_defs[i].poExpr,
269 : psSelectInfo);
270 32 : int bOutNeedsNullCheck = FALSE;
271 : CPLString osFilter = WFS_TurnSQLFilterToOGCFilter(
272 32 : psSelectInfo->join_defs[i].poExpr, poDS, nullptr, 200,
273 : TRUE, /* bPropertyIsNotEqualToSupported */
274 : FALSE, /* bUseFeatureId */
275 : FALSE, /* bGmlObjectIdNeedsGMLPrefix */
276 32 : "", &bOutNeedsNullCheck);
277 32 : if (osFilter.empty())
278 : {
279 2 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported JOIN clause");
280 2 : return nullptr;
281 : }
282 30 : osGlobalFilter += osFilter;
283 : }
284 30 : if (psSelectInfo->where_expr != nullptr)
285 : {
286 6 : OGRWFSRemoveReferenceToTableAlias(psSelectInfo->where_expr,
287 : psSelectInfo);
288 6 : int bOutNeedsNullCheck = FALSE;
289 : CPLString osFilter = WFS_TurnSQLFilterToOGCFilter(
290 6 : psSelectInfo->where_expr, poDS, nullptr, 200,
291 : TRUE, /* bPropertyIsNotEqualToSupported */
292 : FALSE, /* bUseFeatureId */
293 : FALSE, /* bGmlObjectIdNeedsGMLPrefix */
294 6 : "", &bOutNeedsNullCheck);
295 6 : if (osFilter.empty())
296 : {
297 2 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported WHERE clause");
298 2 : return nullptr;
299 : }
300 4 : osGlobalFilter += osFilter;
301 : }
302 28 : if (psSelectInfo->join_count > 1 || psSelectInfo->where_expr != nullptr)
303 4 : osGlobalFilter += "</And>";
304 28 : CPLDebug("WFS", "osGlobalFilter = %s", osGlobalFilter.c_str());
305 :
306 : OGRWFSJoinLayer *poLayer =
307 28 : new OGRWFSJoinLayer(poDS, psSelectInfo, osGlobalFilter);
308 28 : return poLayer;
309 : }
310 :
311 : /************************************************************************/
312 : /* ResetReading() */
313 : /************************************************************************/
314 :
315 32 : void OGRWFSJoinLayer::ResetReading()
316 : {
317 32 : if (bPagingActive)
318 30 : bReloadNeeded = true;
319 32 : nPagingStartIndex = 0;
320 32 : nFeatureRead = 0;
321 32 : nFeatureCountRequested = 0;
322 32 : if (bReloadNeeded)
323 : {
324 30 : GDALClose(poBaseDS);
325 30 : poBaseDS = nullptr;
326 30 : poBaseLayer = nullptr;
327 30 : bHasFetched = false;
328 30 : bReloadNeeded = false;
329 : }
330 32 : if (poBaseLayer)
331 0 : poBaseLayer->ResetReading();
332 32 : aoSetMD5.clear();
333 32 : }
334 :
335 : /************************************************************************/
336 : /* MakeGetFeatureURL() */
337 : /************************************************************************/
338 :
339 84 : CPLString OGRWFSJoinLayer::MakeGetFeatureURL(int bRequestHits)
340 : {
341 84 : CPLString osURL(poDS->GetBaseURL());
342 84 : osURL = CPLURLAddKVP(osURL, "SERVICE", "WFS");
343 84 : osURL = CPLURLAddKVP(osURL, "VERSION", poDS->GetVersion());
344 84 : osURL = CPLURLAddKVP(osURL, "REQUEST", "GetFeature");
345 84 : osURL = CPLURLAddKVP(osURL, "TYPENAMES", WFS_EscapeURL(osFeatureTypes));
346 :
347 84 : int nRequestMaxFeatures = 0;
348 154 : if (poDS->IsPagingAllowed() && !bRequestHits &&
349 154 : CPLURLGetValue(osURL, "COUNT").empty())
350 : {
351 140 : osURL = CPLURLAddKVP(
352 : osURL, "STARTINDEX",
353 140 : CPLSPrintf("%d", nPagingStartIndex + poDS->GetBaseStartIndex()));
354 70 : nRequestMaxFeatures = poDS->GetPageSize();
355 70 : nFeatureCountRequested = nRequestMaxFeatures;
356 70 : bPagingActive = true;
357 : }
358 :
359 84 : if (nRequestMaxFeatures)
360 : {
361 : osURL =
362 70 : CPLURLAddKVP(osURL, "COUNT", CPLSPrintf("%d", nRequestMaxFeatures));
363 : }
364 :
365 168 : CPLString osFilter;
366 84 : osFilter = "<Filter xmlns=\"http://www.opengis.net/fes/2.0\"";
367 :
368 168 : std::map<CPLString, CPLString> oMapNS;
369 252 : for (auto &poLayer : apoLayers)
370 : {
371 168 : const char *pszNS = poLayer->GetNamespacePrefix();
372 168 : const char *pszNSVal = poLayer->GetNamespaceName();
373 168 : if (pszNS && pszNSVal)
374 8 : oMapNS[pszNS] = pszNSVal;
375 : }
376 84 : std::map<CPLString, CPLString>::iterator oIter = oMapNS.begin();
377 88 : for (; oIter != oMapNS.end(); ++oIter)
378 : {
379 4 : osFilter += " xmlns:";
380 4 : osFilter += oIter->first;
381 4 : osFilter += "=\"";
382 4 : osFilter += oIter->second;
383 4 : osFilter += "\"";
384 : }
385 84 : osFilter += " xmlns:gml=\"http://www.opengis.net/gml/3.2\">";
386 84 : osFilter += osGlobalFilter;
387 84 : osFilter += "</Filter>";
388 :
389 84 : osURL = CPLURLAddKVP(osURL, "FILTER", WFS_EscapeURL(osFilter));
390 :
391 84 : if (bRequestHits)
392 : {
393 14 : osURL = CPLURLAddKVP(osURL, "RESULTTYPE", "hits");
394 : }
395 70 : else if (!osSortBy.empty())
396 : {
397 2 : osURL = CPLURLAddKVP(osURL, "SORTBY", WFS_EscapeURL(osSortBy));
398 : }
399 :
400 168 : return osURL;
401 : }
402 :
403 : /************************************************************************/
404 : /* FetchGetFeature() */
405 : /************************************************************************/
406 :
407 70 : GDALDataset *OGRWFSJoinLayer::FetchGetFeature()
408 : {
409 :
410 140 : CPLString osURL = MakeGetFeatureURL();
411 70 : CPLDebug("WFS", "%s", osURL.c_str());
412 :
413 70 : CPLHTTPResult *psResult = nullptr;
414 :
415 : /* Try streaming when the output format is GML and that we have a .xsd */
416 : /* that we are able to understand */
417 140 : CPLString osXSDFileName = CPLSPrintf("%s/file.xsd", m_osTmpDir.c_str());
418 : VSIStatBufL sBuf;
419 70 : if (CPLTestBool(CPLGetConfigOption("OGR_WFS_USE_STREAMING", "YES")) &&
420 105 : VSIStatL(osXSDFileName, &sBuf) == 0 &&
421 35 : GDALGetDriverByName("GML") != nullptr)
422 : {
423 : const char *pszStreamingName =
424 35 : CPLSPrintf("/vsicurl_streaming/%s", osURL.c_str());
425 70 : if (STARTS_WITH(osURL, "/vsimem/") &&
426 35 : CPLTestBool(CPLGetConfigOption("CPL_CURL_ENABLE_VSIMEM", "FALSE")))
427 : {
428 35 : pszStreamingName = osURL.c_str();
429 : }
430 :
431 35 : const char *const apszAllowedDrivers[] = {"GML", nullptr};
432 35 : const char *apszOpenOptions[2] = {nullptr, nullptr};
433 35 : apszOpenOptions[0] = CPLSPrintf("XSD=%s", osXSDFileName.c_str());
434 : GDALDataset *poGML_DS =
435 35 : GDALDataset::Open(pszStreamingName, GDAL_OF_VECTOR,
436 : apszAllowedDrivers, apszOpenOptions, nullptr);
437 35 : if (poGML_DS)
438 : {
439 : // bStreamingDS = true;
440 31 : return poGML_DS;
441 : }
442 :
443 : /* In case of failure, read directly the content to examine */
444 : /* it, if it is XML error content */
445 : char szBuffer[2048];
446 5 : int nRead = 0;
447 5 : VSILFILE *fp = VSIFOpenL(pszStreamingName, "rb");
448 5 : if (fp)
449 : {
450 4 : nRead = static_cast<int>(
451 4 : VSIFReadL(szBuffer, 1, sizeof(szBuffer) - 1, fp));
452 4 : szBuffer[nRead] = '\0';
453 4 : VSIFCloseL(fp);
454 : }
455 :
456 5 : if (nRead != 0)
457 : {
458 3 : if (strstr(szBuffer, "<ServiceExceptionReport") != nullptr ||
459 2 : strstr(szBuffer, "<ows:ExceptionReport") != nullptr)
460 : {
461 1 : CPLError(CE_Failure, CPLE_AppDefined,
462 : "Error returned by server : %s", szBuffer);
463 1 : return nullptr;
464 : }
465 : }
466 : }
467 :
468 : // bStreamingDS = false;
469 39 : psResult = poDS->HTTPFetch(osURL, nullptr);
470 39 : if (psResult == nullptr)
471 : {
472 4 : return nullptr;
473 : }
474 :
475 35 : VSIMkdir(m_osTmpDir.c_str(), 0);
476 :
477 35 : GByte *pabyData = psResult->pabyData;
478 35 : int nDataLen = psResult->nDataLen;
479 :
480 35 : if (strstr(reinterpret_cast<const char *>(pabyData),
481 34 : "<ServiceExceptionReport") != nullptr ||
482 34 : strstr(reinterpret_cast<const char *>(pabyData),
483 : "<ows:ExceptionReport") != nullptr)
484 : {
485 1 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
486 : pabyData);
487 1 : CPLHTTPDestroyResult(psResult);
488 1 : return nullptr;
489 : }
490 :
491 68 : CPLString osTmpFileName;
492 :
493 34 : osTmpFileName = m_osTmpDir + "/file.gfs";
494 34 : VSIUnlink(osTmpFileName);
495 :
496 34 : osTmpFileName = m_osTmpDir + "/file.gml";
497 :
498 : VSILFILE *fp =
499 34 : VSIFileFromMemBuffer(osTmpFileName, pabyData, nDataLen, TRUE);
500 34 : VSIFCloseL(fp);
501 34 : psResult->pabyData = nullptr;
502 :
503 34 : CPLHTTPDestroyResult(psResult);
504 :
505 : auto l_poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
506 68 : osTmpFileName, GDAL_OF_VECTOR, nullptr, nullptr, nullptr));
507 34 : if (l_poDS == nullptr)
508 : {
509 4 : if (strstr(reinterpret_cast<const char *>(pabyData),
510 4 : "<wfs:FeatureCollection") == nullptr &&
511 4 : strstr(reinterpret_cast<const char *>(pabyData),
512 : "<gml:FeatureCollection") == nullptr)
513 : {
514 4 : if (nDataLen > 1000)
515 0 : pabyData[1000] = 0;
516 4 : CPLError(CE_Failure, CPLE_AppDefined, "Error: cannot parse %s",
517 : pabyData);
518 : }
519 4 : return nullptr;
520 : }
521 :
522 30 : OGRLayer *poLayer = l_poDS->GetLayer(0);
523 30 : if (poLayer == nullptr)
524 : {
525 0 : return nullptr;
526 : }
527 :
528 30 : return l_poDS.release();
529 : }
530 :
531 : /************************************************************************/
532 : /* GetNextFeature() */
533 : /************************************************************************/
534 :
535 76 : OGRFeature *OGRWFSJoinLayer::GetNextFeature()
536 : {
537 : while (true)
538 : {
539 76 : if (bPagingActive &&
540 48 : nFeatureRead == nPagingStartIndex + nFeatureCountRequested)
541 : {
542 42 : bReloadNeeded = true;
543 42 : nPagingStartIndex = nFeatureRead;
544 : }
545 76 : if (bReloadNeeded)
546 : {
547 42 : GDALClose(poBaseDS);
548 42 : poBaseDS = nullptr;
549 42 : poBaseLayer = nullptr;
550 42 : bHasFetched = false;
551 42 : bReloadNeeded = false;
552 : }
553 76 : if (poBaseDS == nullptr && !bHasFetched)
554 : {
555 70 : bHasFetched = true;
556 70 : poBaseDS = FetchGetFeature();
557 70 : if (poBaseDS)
558 : {
559 60 : poBaseLayer = poBaseDS->GetLayer(0);
560 60 : poBaseLayer->ResetReading();
561 : }
562 : }
563 76 : if (!poBaseLayer)
564 74 : return nullptr;
565 :
566 66 : OGRFeature *poSrcFeature = poBaseLayer->GetNextFeature();
567 66 : if (poSrcFeature == nullptr)
568 16 : return nullptr;
569 50 : nFeatureRead++;
570 :
571 50 : OGRFeature *poNewFeature = new OGRFeature(poFeatureDefn);
572 :
573 : struct CPLMD5Context sMD5Context;
574 50 : if (bDistinct)
575 6 : CPLMD5Init(&sMD5Context);
576 :
577 256 : for (int i = 0; i < static_cast<int>(aoSrcFieldNames.size()); i++)
578 : {
579 206 : int iSrcField = poSrcFeature->GetFieldIndex(aoSrcFieldNames[i]);
580 206 : if (iSrcField >= 0 && poSrcFeature->IsFieldSetAndNotNull(iSrcField))
581 : {
582 206 : OGRFieldType eType = poFeatureDefn->GetFieldDefn(i)->GetType();
583 206 : if (eType ==
584 206 : poSrcFeature->GetFieldDefnRef(iSrcField)->GetType())
585 : {
586 200 : poNewFeature->SetField(
587 200 : i, poSrcFeature->GetRawFieldRef(iSrcField));
588 : }
589 6 : else if (eType == OFTString)
590 0 : poNewFeature->SetField(
591 : i, poSrcFeature->GetFieldAsString(iSrcField));
592 6 : else if (eType == OFTInteger)
593 2 : poNewFeature->SetField(
594 : i, poSrcFeature->GetFieldAsInteger(iSrcField));
595 4 : else if (eType == OFTInteger64)
596 2 : poNewFeature->SetField(
597 : i, poSrcFeature->GetFieldAsInteger64(iSrcField));
598 2 : else if (eType == OFTReal)
599 2 : poNewFeature->SetField(
600 : i, poSrcFeature->GetFieldAsDouble(iSrcField));
601 : else
602 0 : poNewFeature->SetField(
603 : i, poSrcFeature->GetFieldAsString(iSrcField));
604 206 : if (bDistinct)
605 : {
606 30 : if (eType == OFTInteger)
607 : {
608 6 : int nVal = poNewFeature->GetFieldAsInteger(i);
609 6 : CPLMD5Update(&sMD5Context, &nVal, sizeof(nVal));
610 : }
611 24 : else if (eType == OFTInteger64)
612 : {
613 6 : GIntBig nVal = poNewFeature->GetFieldAsInteger64(i);
614 6 : CPLMD5Update(&sMD5Context, &nVal, sizeof(nVal));
615 : }
616 18 : else if (eType == OFTReal)
617 : {
618 6 : double dfVal = poNewFeature->GetFieldAsDouble(i);
619 6 : CPLMD5Update(&sMD5Context, &dfVal, sizeof(dfVal));
620 : }
621 : else
622 : {
623 12 : const char *pszStr = poNewFeature->GetFieldAsString(i);
624 12 : CPLMD5Update(&sMD5Context, pszStr, strlen(pszStr));
625 : }
626 : }
627 : }
628 : }
629 142 : for (int i = 0; i < static_cast<int>(aoSrcGeomFieldNames.size()); i++)
630 : {
631 : int iSrcField =
632 92 : poSrcFeature->GetGeomFieldIndex(aoSrcGeomFieldNames[i]);
633 92 : if (iSrcField >= 0)
634 : {
635 92 : OGRGeometry *poGeom = poSrcFeature->StealGeometry(iSrcField);
636 92 : if (poGeom)
637 : {
638 92 : poGeom->assignSpatialReference(
639 92 : poFeatureDefn->GetGeomFieldDefn(i)->GetSpatialRef());
640 :
641 92 : if (bDistinct)
642 : {
643 6 : const size_t nSize = poGeom->WkbSize();
644 : GByte *pabyGeom =
645 6 : static_cast<GByte *>(VSI_MALLOC_VERBOSE(nSize));
646 6 : if (pabyGeom)
647 : {
648 6 : poGeom->exportToWkb(wkbNDR, pabyGeom);
649 6 : CPLMD5Update(&sMD5Context, pabyGeom, nSize);
650 6 : CPLFree(pabyGeom);
651 : }
652 : }
653 :
654 92 : poNewFeature->SetGeomFieldDirectly(i, poGeom);
655 : }
656 : }
657 : }
658 :
659 50 : poNewFeature->SetFID(nFeatureRead);
660 50 : delete poSrcFeature;
661 :
662 50 : if (bDistinct)
663 : {
664 6 : CPLString osDigest = "0123456789abcdef";
665 6 : CPLMD5Final(reinterpret_cast<unsigned char *>(osDigest.data()),
666 : &sMD5Context);
667 6 : if (aoSetMD5.find(osDigest) == aoSetMD5.end())
668 : {
669 4 : aoSetMD5.insert(std::move(osDigest));
670 4 : return poNewFeature;
671 : }
672 : else
673 2 : delete poNewFeature;
674 : }
675 : else
676 44 : return poNewFeature;
677 2 : }
678 : }
679 :
680 : /************************************************************************/
681 : /* ExecuteGetFeatureResultTypeHits() */
682 : /************************************************************************/
683 :
684 14 : GIntBig OGRWFSJoinLayer::ExecuteGetFeatureResultTypeHits()
685 : {
686 14 : char *pabyData = nullptr;
687 28 : CPLString osURL = MakeGetFeatureURL(TRUE);
688 14 : CPLDebug("WFS", "%s", osURL.c_str());
689 :
690 14 : CPLHTTPResult *psResult = poDS->HTTPFetch(osURL, nullptr);
691 14 : if (psResult == nullptr)
692 : {
693 4 : return -1;
694 : }
695 :
696 10 : pabyData = reinterpret_cast<char *>(psResult->pabyData);
697 10 : psResult->pabyData = nullptr;
698 :
699 10 : if (strstr(pabyData, "<ServiceExceptionReport") != nullptr ||
700 8 : strstr(pabyData, "<ows:ExceptionReport") != nullptr)
701 : {
702 2 : CPLError(CE_Failure, CPLE_AppDefined, "Error returned by server : %s",
703 : pabyData);
704 2 : CPLHTTPDestroyResult(psResult);
705 2 : CPLFree(pabyData);
706 2 : return -1;
707 : }
708 :
709 8 : CPLXMLNode *psXML = CPLParseXMLString(pabyData);
710 8 : if (psXML == nullptr)
711 : {
712 2 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid XML content : %s",
713 : pabyData);
714 2 : CPLHTTPDestroyResult(psResult);
715 2 : CPLFree(pabyData);
716 2 : return -1;
717 : }
718 :
719 6 : CPLStripXMLNamespace(psXML, nullptr, TRUE);
720 6 : CPLXMLNode *psRoot = CPLGetXMLNode(psXML, "=FeatureCollection");
721 6 : if (psRoot == nullptr)
722 : {
723 2 : CPLError(CE_Failure, CPLE_AppDefined,
724 : "Cannot find <FeatureCollection>");
725 2 : CPLDestroyXMLNode(psXML);
726 2 : CPLHTTPDestroyResult(psResult);
727 2 : CPLFree(pabyData);
728 2 : return -1;
729 : }
730 :
731 : const char *pszValue =
732 4 : CPLGetXMLValue(psRoot, "numberMatched", nullptr); /* WFS 2.0.0 */
733 4 : if (pszValue == nullptr)
734 : {
735 2 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find numberMatched");
736 2 : CPLDestroyXMLNode(psXML);
737 2 : CPLHTTPDestroyResult(psResult);
738 2 : CPLFree(pabyData);
739 2 : return -1;
740 : }
741 :
742 2 : GIntBig nFeatures = CPLAtoGIntBig(pszValue);
743 :
744 2 : CPLDestroyXMLNode(psXML);
745 2 : CPLHTTPDestroyResult(psResult);
746 2 : CPLFree(pabyData);
747 :
748 2 : return nFeatures;
749 : }
750 :
751 : /************************************************************************/
752 : /* GetFeatureCount() */
753 : /************************************************************************/
754 :
755 16 : GIntBig OGRWFSJoinLayer::GetFeatureCount(int bForce)
756 : {
757 16 : if (!bDistinct)
758 : {
759 14 : const GIntBig nFeatures = ExecuteGetFeatureResultTypeHits();
760 14 : if (nFeatures >= 0)
761 2 : return nFeatures;
762 : }
763 :
764 14 : const GIntBig nFeatures = OGRLayer::GetFeatureCount(bForce);
765 14 : return nFeatures;
766 : }
767 :
768 : /************************************************************************/
769 : /* GetLayerDefn() */
770 : /************************************************************************/
771 :
772 4 : const OGRFeatureDefn *OGRWFSJoinLayer::GetLayerDefn() const
773 : {
774 4 : return poFeatureDefn;
775 : }
776 :
777 : /************************************************************************/
778 : /* TestCapability() */
779 : /************************************************************************/
780 :
781 2 : int OGRWFSJoinLayer::TestCapability(const char *) const
782 : {
783 2 : return FALSE;
784 : }
785 :
786 : /************************************************************************/
787 : /* ISetSpatialFilter() */
788 : /************************************************************************/
789 :
790 4 : OGRErr OGRWFSJoinLayer::ISetSpatialFilter(int, const OGRGeometry *poGeom)
791 : {
792 4 : if (poGeom != nullptr)
793 : {
794 2 : CPLError(CE_Failure, CPLE_NotSupported,
795 : "Setting a spatial filter on a layer resulting from a WFS "
796 : "join is unsupported");
797 2 : return OGRERR_FAILURE;
798 : }
799 2 : return OGRERR_NONE;
800 : }
801 :
802 : /************************************************************************/
803 : /* SetAttributeFilter() */
804 : /************************************************************************/
805 :
806 4 : OGRErr OGRWFSJoinLayer::SetAttributeFilter(const char *pszFilter)
807 : {
808 4 : if (pszFilter != nullptr)
809 : {
810 2 : CPLError(CE_Failure, CPLE_NotSupported,
811 : "Setting an attribute filter on a layer resulting from a WFS "
812 : "join is unsupported");
813 2 : return OGRERR_FAILURE;
814 : }
815 2 : return OGRERR_NONE;
816 : }
|