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