LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/wfs - ogrwfsjoinlayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 403 431 93.5 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

          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             : }

Generated by: LCOV version 1.14