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

Generated by: LCOV version 1.14