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