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

Generated by: LCOV version 1.14