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