LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlutils - ogrwfsfilter.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 409 460 88.9 %
Date: 2025-01-18 12:42:00 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  WFS Translator
       4             :  * Purpose:  Implements OGR SQL into OGC Filter translation.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogrwfsfilter.h"
      14             : #include "ogr_p.h"
      15             : 
      16             : #include <cinttypes>
      17             : 
      18             : typedef struct
      19             : {
      20             :     int nVersion;
      21             :     bool bPropertyIsNotEqualToSupported;
      22             :     int bOutNeedsNullCheck;
      23             :     GDALDataset *poDS;
      24             :     OGRFeatureDefn *poFDefn;
      25             :     int nUniqueGeomGMLId;
      26             :     const OGRSpatialReference *poSRS;
      27             :     const char *pszNSPrefix;
      28             : } ExprDumpFilterOptions;
      29             : 
      30             : /************************************************************************/
      31             : /*                WFS_ExprDumpGmlObjectIdFilter()                       */
      32             : /************************************************************************/
      33             : 
      34          96 : static bool WFS_ExprDumpGmlObjectIdFilter(CPLString &osFilter,
      35             :                                           const swq_expr_node *poExpr,
      36             :                                           int bUseFeatureId,
      37             :                                           int bGmlObjectIdNeedsGMLPrefix,
      38             :                                           int nVersion)
      39             : {
      40          96 :     if (poExpr->eNodeType == SNT_OPERATION && poExpr->nOperation == SWQ_EQ &&
      41          59 :         poExpr->nSubExprCount == 2 &&
      42          59 :         poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
      43          59 :         strcmp(poExpr->papoSubExpr[0]->string_value, "gml_id") == 0 &&
      44          22 :         poExpr->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
      45             :     {
      46          22 :         if (bUseFeatureId)
      47           0 :             osFilter += "<FeatureId fid=\"";
      48          22 :         else if (nVersion >= 200)
      49           0 :             osFilter += "<ResourceId rid=\"";
      50          22 :         else if (!bGmlObjectIdNeedsGMLPrefix)
      51          22 :             osFilter += "<GmlObjectId id=\"";
      52             :         else
      53           0 :             osFilter += "<GmlObjectId gml:id=\"";
      54          22 :         if (poExpr->papoSubExpr[1]->field_type == SWQ_INTEGER ||
      55          22 :             poExpr->papoSubExpr[1]->field_type == SWQ_INTEGER64)
      56             :         {
      57           0 :             osFilter +=
      58           0 :                 CPLSPrintf("%" PRId64, poExpr->papoSubExpr[1]->int_value);
      59             :         }
      60          22 :         else if (poExpr->papoSubExpr[1]->field_type == SWQ_STRING)
      61             :         {
      62          22 :             char *pszXML = CPLEscapeString(poExpr->papoSubExpr[1]->string_value,
      63             :                                            -1, CPLES_XML);
      64          22 :             osFilter += pszXML;
      65          22 :             CPLFree(pszXML);
      66             :         }
      67             :         else
      68             :         {
      69           0 :             return false;
      70             :         }
      71          22 :         osFilter += "\"/>";
      72          22 :         return true;
      73             :     }
      74          74 :     else if (poExpr->eNodeType == SNT_OPERATION &&
      75          74 :              poExpr->nOperation == SWQ_OR && poExpr->nSubExprCount == 2)
      76             :     {
      77          40 :         return WFS_ExprDumpGmlObjectIdFilter(
      78          20 :                    osFilter, poExpr->papoSubExpr[0], bUseFeatureId,
      79          22 :                    bGmlObjectIdNeedsGMLPrefix, nVersion) &&
      80           2 :                WFS_ExprDumpGmlObjectIdFilter(
      81           2 :                    osFilter, poExpr->papoSubExpr[1], bUseFeatureId,
      82          20 :                    bGmlObjectIdNeedsGMLPrefix, nVersion);
      83             :     }
      84             : 
      85          54 :     return false;
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                     WFS_ExprDumpRawLitteral()                        */
      90             : /************************************************************************/
      91             : 
      92          81 : static bool WFS_ExprDumpRawLitteral(CPLString &osFilter,
      93             :                                     const swq_expr_node *poExpr)
      94             : {
      95          81 :     if (poExpr->field_type == SWQ_INTEGER ||
      96          47 :         poExpr->field_type == SWQ_INTEGER64)
      97          34 :         osFilter += CPLSPrintf("%" PRId64, poExpr->int_value);
      98          47 :     else if (poExpr->field_type == SWQ_FLOAT)
      99          34 :         osFilter += CPLSPrintf("%.16g", poExpr->float_value);
     100          13 :     else if (poExpr->field_type == SWQ_STRING)
     101             :     {
     102          13 :         char *pszXML = CPLEscapeString(poExpr->string_value, -1, CPLES_XML);
     103          13 :         osFilter += pszXML;
     104          13 :         CPLFree(pszXML);
     105             :     }
     106           0 :     else if (poExpr->field_type == SWQ_TIMESTAMP)
     107             :     {
     108             :         OGRField sDate;
     109           0 :         if (!OGRParseDate(poExpr->string_value, &sDate, 0))
     110           0 :             return false;
     111           0 :         char *pszDate = OGRGetXMLDateTime(&sDate);
     112           0 :         osFilter += pszDate;
     113           0 :         CPLFree(pszDate);
     114             :     }
     115             :     else
     116             :     {
     117           0 :         return false;
     118             :     }
     119          81 :     return true;
     120             : }
     121             : 
     122             : /************************************************************************/
     123             : /*                       WFS_ExprGetSRSName()                          */
     124             : /************************************************************************/
     125             : 
     126          25 : static const char *WFS_ExprGetSRSName(const swq_expr_node *poExpr,
     127             :                                       int iSubArgIndex,
     128             :                                       ExprDumpFilterOptions *psOptions,
     129             :                                       OGRSpatialReference &oSRS)
     130             : {
     131          25 :     if (poExpr->nSubExprCount == iSubArgIndex + 1 &&
     132          15 :         poExpr->papoSubExpr[iSubArgIndex]->field_type == SWQ_STRING)
     133             :     {
     134          12 :         if (oSRS.SetFromUserInput(
     135           6 :                 poExpr->papoSubExpr[iSubArgIndex]->string_value) == OGRERR_NONE)
     136             :         {
     137           6 :             return poExpr->papoSubExpr[iSubArgIndex]->string_value;
     138             :         }
     139             :     }
     140          19 :     else if (poExpr->nSubExprCount == iSubArgIndex + 1 &&
     141           9 :              poExpr->papoSubExpr[iSubArgIndex]->field_type == SWQ_INTEGER)
     142             :     {
     143          18 :         if (oSRS.importFromEPSGA(static_cast<int>(
     144           9 :                 poExpr->papoSubExpr[iSubArgIndex]->int_value)) == OGRERR_NONE)
     145             :         {
     146          18 :             return CPLSPrintf(
     147             :                 "urn:ogc:def:crs:EPSG::%d",
     148           9 :                 static_cast<int>(poExpr->papoSubExpr[iSubArgIndex]->int_value));
     149             :         }
     150             :     }
     151          10 :     else if (poExpr->nSubExprCount == iSubArgIndex &&
     152          10 :              psOptions->poSRS != nullptr)
     153             :     {
     154          10 :         if (psOptions->poSRS->GetAuthorityName(nullptr) &&
     155          10 :             EQUAL(psOptions->poSRS->GetAuthorityName(nullptr), "EPSG") &&
     156          30 :             psOptions->poSRS->GetAuthorityCode(nullptr) &&
     157          10 :             oSRS.importFromEPSGA(atoi(
     158          10 :                 psOptions->poSRS->GetAuthorityCode(nullptr))) == OGRERR_NONE)
     159             :         {
     160          10 :             return CPLSPrintf("urn:ogc:def:crs:EPSG::%s",
     161          20 :                               psOptions->poSRS->GetAuthorityCode(nullptr));
     162             :         }
     163             :     }
     164           0 :     return nullptr;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                     WFS_ExprDumpAsOGCFilter()                        */
     169             : /************************************************************************/
     170             : 
     171         313 : static bool WFS_ExprDumpAsOGCFilter(CPLString &osFilter,
     172             :                                     const swq_expr_node *poExpr,
     173             :                                     int bExpectBinary,
     174             :                                     ExprDumpFilterOptions *psOptions)
     175             : {
     176         313 :     if (poExpr->eNodeType == SNT_COLUMN)
     177             :     {
     178         123 :         if (bExpectBinary)
     179           0 :             return false;
     180             : 
     181             :         /* Special fields not understood by server */
     182         123 :         if (EQUAL(poExpr->string_value, "gml_id") ||
     183         123 :             EQUAL(poExpr->string_value, "FID") ||
     184         123 :             EQUAL(poExpr->string_value, "OGR_GEOMETRY") ||
     185         117 :             EQUAL(poExpr->string_value, "OGR_GEOM_WKT") ||
     186         117 :             EQUAL(poExpr->string_value, "OGR_GEOM_AREA") ||
     187         117 :             EQUAL(poExpr->string_value, "OGR_STYLE"))
     188             :         {
     189           6 :             CPLDebug("WFS", "Attribute refers to a OGR special field. Cannot "
     190             :                             "use server-side filtering");
     191           6 :             return false;
     192             :         }
     193             : 
     194         117 :         const char *pszFieldname = nullptr;
     195             :         const bool bSameTable =
     196         161 :             psOptions->poFDefn != nullptr &&
     197          44 :             (poExpr->table_name == nullptr ||
     198           0 :              EQUAL(poExpr->table_name, psOptions->poFDefn->GetName()));
     199         117 :         if (bSameTable)
     200             :         {
     201             :             int nIndex;
     202          88 :             if ((nIndex = psOptions->poFDefn->GetFieldIndex(
     203          44 :                      poExpr->string_value)) >= 0)
     204             :             {
     205             :                 pszFieldname =
     206          22 :                     psOptions->poFDefn->GetFieldDefn(nIndex)->GetNameRef();
     207             :             }
     208          44 :             else if ((nIndex = psOptions->poFDefn->GetGeomFieldIndex(
     209          22 :                           poExpr->string_value)) >= 0)
     210             :             {
     211             :                 pszFieldname =
     212          22 :                     psOptions->poFDefn->GetGeomFieldDefn(nIndex)->GetNameRef();
     213             :             }
     214             :         }
     215          73 :         else if (psOptions->poDS != nullptr)
     216             :         {
     217             :             OGRLayer *poLayer =
     218          64 :                 psOptions->poDS->GetLayerByName(poExpr->table_name);
     219          64 :             if (poLayer)
     220             :             {
     221          64 :                 OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
     222             :                 int nIndex;
     223          64 :                 if ((nIndex = poFDefn->GetFieldIndex(poExpr->string_value)) >=
     224             :                     0)
     225             :                 {
     226             :                     pszFieldname =
     227          62 :                         CPLSPrintf("%s/%s", poLayer->GetName(),
     228          62 :                                    poFDefn->GetFieldDefn(nIndex)->GetNameRef());
     229             :                 }
     230           4 :                 else if ((nIndex = poFDefn->GetGeomFieldIndex(
     231           2 :                               poExpr->string_value)) >= 0)
     232             :                 {
     233           4 :                     pszFieldname = CPLSPrintf(
     234           2 :                         "%s/%s", poLayer->GetName(),
     235           2 :                         poFDefn->GetGeomFieldDefn(nIndex)->GetNameRef());
     236             :                 }
     237             :             }
     238             :         }
     239             : 
     240         117 :         if (psOptions->poFDefn == nullptr && psOptions->poDS == nullptr)
     241           9 :             pszFieldname = poExpr->string_value;
     242             : 
     243         117 :         if (pszFieldname == nullptr)
     244             :         {
     245           0 :             if (poExpr->table_name != nullptr)
     246           0 :                 CPLDebug("WFS",
     247             :                          "Field \"%s\".\"%s\" unknown. Cannot use server-side "
     248             :                          "filtering",
     249           0 :                          poExpr->table_name, poExpr->string_value);
     250             :             else
     251           0 :                 CPLDebug(
     252             :                     "WFS",
     253             :                     "Field \"%s\" unknown. Cannot use server-side filtering",
     254           0 :                     poExpr->string_value);
     255           0 :             return false;
     256             :         }
     257             : 
     258         117 :         if (psOptions->nVersion >= 200)
     259             :             osFilter +=
     260          64 :                 CPLSPrintf("<%sValueReference>", psOptions->pszNSPrefix);
     261             :         else
     262          53 :             osFilter += CPLSPrintf("<%sPropertyName>", psOptions->pszNSPrefix);
     263         117 :         char *pszFieldnameXML = CPLEscapeString(pszFieldname, -1, CPLES_XML);
     264         117 :         osFilter += pszFieldnameXML;
     265         117 :         CPLFree(pszFieldnameXML);
     266         117 :         if (psOptions->nVersion >= 200)
     267             :             osFilter +=
     268          64 :                 CPLSPrintf("</%sValueReference>", psOptions->pszNSPrefix);
     269             :         else
     270          53 :             osFilter += CPLSPrintf("</%sPropertyName>", psOptions->pszNSPrefix);
     271             : 
     272         117 :         return true;
     273             :     }
     274             : 
     275         190 :     if (poExpr->eNodeType == SNT_CONSTANT)
     276             :     {
     277          27 :         if (bExpectBinary)
     278           0 :             return false;
     279             : 
     280          27 :         osFilter += CPLSPrintf("<%sLiteral>", psOptions->pszNSPrefix);
     281          27 :         if (!WFS_ExprDumpRawLitteral(osFilter, poExpr))
     282           0 :             return false;
     283          27 :         osFilter += CPLSPrintf("</%sLiteral>", psOptions->pszNSPrefix);
     284             : 
     285          27 :         return true;
     286             :     }
     287             : 
     288         163 :     if (poExpr->eNodeType != SNT_OPERATION)
     289           0 :         return false;  // Should not happen.
     290             : 
     291         163 :     if (poExpr->nOperation == SWQ_NOT)
     292             :     {
     293           6 :         osFilter += CPLSPrintf("<%sNot>", psOptions->pszNSPrefix);
     294           6 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], TRUE,
     295             :                                      psOptions))
     296           4 :             return false;
     297           2 :         osFilter += CPLSPrintf("</%sNot>", psOptions->pszNSPrefix);
     298           2 :         return true;
     299             :     }
     300             : 
     301         157 :     if (poExpr->nOperation == SWQ_LIKE || poExpr->nOperation == SWQ_ILIKE)
     302             :     {
     303           6 :         CPLString osVal;
     304             :         const char *pszMatchCase =
     305           6 :             poExpr->nOperation == SWQ_LIKE &&
     306           3 :                     !CPLTestBool(
     307             :                         CPLGetConfigOption("OGR_SQL_LIKE_AS_ILIKE", "FALSE"))
     308           6 :                 ? "true"
     309           3 :                 : "false";
     310           3 :         if (psOptions->nVersion == 100)
     311             :             osFilter +=
     312             :                 CPLSPrintf("<%sPropertyIsLike wildCard=\"*\" singleChar=\"_\" "
     313             :                            "escape=\"!\" matchCase=\"%s\">",
     314           0 :                            psOptions->pszNSPrefix, pszMatchCase);
     315             :         else
     316             :             osFilter +=
     317             :                 CPLSPrintf("<%sPropertyIsLike wildCard=\"*\" singleChar=\"_\" "
     318             :                            "escapeChar=\"!\" matchCase=\"%s\">",
     319           3 :                            psOptions->pszNSPrefix, pszMatchCase);
     320           3 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE,
     321             :                                      psOptions))
     322           0 :             return false;
     323           3 :         if (poExpr->papoSubExpr[1]->eNodeType != SNT_CONSTANT &&
     324           0 :             poExpr->papoSubExpr[1]->field_type != SWQ_STRING)
     325           0 :             return false;
     326           3 :         osFilter += CPLSPrintf("<%sLiteral>", psOptions->pszNSPrefix);
     327             : 
     328             :         // Escape value according to above special characters.  For URL
     329             :         // compatibility reason, we remap the OGR SQL '%' wildcard into '*'.
     330             :         char ch;
     331          16 :         for (int i = 0; (ch = poExpr->papoSubExpr[1]->string_value[i]) != '\0';
     332             :              i++)
     333             :         {
     334          13 :             if (ch == '%')
     335           6 :                 osVal += "*";
     336           7 :             else if (ch == '!')
     337           0 :                 osVal += "!!";
     338           7 :             else if (ch == '*')
     339           0 :                 osVal += "!*";
     340             :             else
     341             :             {
     342             :                 char ach[2];
     343           7 :                 ach[0] = ch;
     344           7 :                 ach[1] = 0;
     345           7 :                 osVal += ach;
     346             :             }
     347             :         }
     348           3 :         char *pszXML = CPLEscapeString(osVal, -1, CPLES_XML);
     349           3 :         osFilter += pszXML;
     350           3 :         CPLFree(pszXML);
     351           3 :         osFilter += CPLSPrintf("</%sLiteral>", psOptions->pszNSPrefix);
     352           3 :         osFilter += CPLSPrintf("</%sPropertyIsLike>", psOptions->pszNSPrefix);
     353           3 :         return true;
     354             :     }
     355             : 
     356         154 :     if (poExpr->nOperation == SWQ_ISNULL)
     357             :     {
     358           6 :         osFilter += CPLSPrintf("<%sPropertyIsNull>", psOptions->pszNSPrefix);
     359           6 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE,
     360             :                                      psOptions))
     361           4 :             return false;
     362           2 :         osFilter += CPLSPrintf("</%sPropertyIsNull>", psOptions->pszNSPrefix);
     363           2 :         psOptions->bOutNeedsNullCheck = TRUE;
     364           2 :         return true;
     365             :     }
     366             : 
     367         148 :     if (poExpr->nOperation == SWQ_EQ || poExpr->nOperation == SWQ_NE ||
     368          97 :         poExpr->nOperation == SWQ_LE || poExpr->nOperation == SWQ_LT ||
     369          93 :         poExpr->nOperation == SWQ_GE || poExpr->nOperation == SWQ_GT)
     370             :     {
     371          59 :         int nOperation = poExpr->nOperation;
     372          59 :         bool bAddClosingNot = false;
     373          59 :         if (!psOptions->bPropertyIsNotEqualToSupported && nOperation == SWQ_NE)
     374             :         {
     375           0 :             osFilter += CPLSPrintf("<%sNot>", psOptions->pszNSPrefix);
     376           0 :             nOperation = SWQ_EQ;
     377           0 :             bAddClosingNot = true;
     378             :         }
     379             : 
     380          59 :         const char *pszName = nullptr;
     381          59 :         switch (nOperation)
     382             :         {
     383          49 :             case SWQ_EQ:
     384          49 :                 pszName = "PropertyIsEqualTo";
     385          49 :                 break;
     386           2 :             case SWQ_NE:
     387           2 :                 pszName = "PropertyIsNotEqualTo";
     388           2 :                 break;
     389           2 :             case SWQ_LE:
     390           2 :                 pszName = "PropertyIsLessThanOrEqualTo";
     391           2 :                 break;
     392           2 :             case SWQ_LT:
     393           2 :                 pszName = "PropertyIsLessThan";
     394           2 :                 break;
     395           2 :             case SWQ_GE:
     396           2 :                 pszName = "PropertyIsGreaterThanOrEqualTo";
     397           2 :                 break;
     398           2 :             case SWQ_GT:
     399           2 :                 pszName = "PropertyIsGreaterThan";
     400           2 :                 break;
     401           0 :             default:
     402           0 :                 break;
     403             :         }
     404          59 :         osFilter += "<";
     405          59 :         osFilter += psOptions->pszNSPrefix;
     406          59 :         osFilter += pszName;
     407          59 :         osFilter += ">";
     408          59 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], FALSE,
     409             :                                      psOptions))
     410           2 :             return false;
     411          57 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[1], FALSE,
     412             :                                      psOptions))
     413           0 :             return false;
     414          57 :         osFilter += "</";
     415          57 :         osFilter += psOptions->pszNSPrefix;
     416          57 :         osFilter += pszName;
     417          57 :         osFilter += ">";
     418          57 :         if (bAddClosingNot)
     419           0 :             osFilter += CPLSPrintf("</%sNot>", psOptions->pszNSPrefix);
     420          57 :         return true;
     421             :     }
     422             : 
     423          89 :     if (poExpr->nOperation == SWQ_AND || poExpr->nOperation == SWQ_OR)
     424             :     {
     425          39 :         const char *pszName = (poExpr->nOperation == SWQ_AND) ? "And" : "Or";
     426          39 :         osFilter += "<";
     427          39 :         osFilter += psOptions->pszNSPrefix;
     428          39 :         osFilter += pszName;
     429          39 :         osFilter += ">";
     430          39 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[0], TRUE,
     431             :                                      psOptions))
     432           0 :             return false;
     433          39 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr->papoSubExpr[1], TRUE,
     434             :                                      psOptions))
     435           0 :             return false;
     436          39 :         osFilter += "</";
     437          39 :         osFilter += psOptions->pszNSPrefix;
     438          39 :         osFilter += pszName;
     439          39 :         osFilter += ">";
     440          39 :         return true;
     441             :     }
     442             : 
     443          50 :     if (poExpr->nOperation == SWQ_CUSTOM_FUNC &&
     444          50 :         EQUAL(poExpr->string_value, "ST_MakeEnvelope"))
     445             :     {
     446          26 :         OGRSpatialReference oSRS;
     447          13 :         const char *pszSRSName = WFS_ExprGetSRSName(poExpr, 4, psOptions, oSRS);
     448          13 :         bool bAxisSwap = false;
     449             : 
     450          13 :         osFilter += "<gml:Envelope";
     451          13 :         if (pszSRSName)
     452             :         {
     453          13 :             osFilter += " srsName=\"";
     454          13 :             osFilter += pszSRSName;
     455          13 :             osFilter += "\"";
     456          24 :             if (!STARTS_WITH_CI(pszSRSName, "EPSG:") &&
     457          11 :                 (oSRS.EPSGTreatsAsLatLong() ||
     458           2 :                  oSRS.EPSGTreatsAsNorthingEasting()))
     459           9 :                 bAxisSwap = true;
     460             :         }
     461          13 :         osFilter += ">";
     462          13 :         osFilter += "<gml:lowerCorner>";
     463          13 :         if (!WFS_ExprDumpRawLitteral(osFilter,
     464          13 :                                      poExpr->papoSubExpr[bAxisSwap ? 1 : 0]))
     465           0 :             return false;
     466          13 :         osFilter += " ";
     467          13 :         if (!WFS_ExprDumpRawLitteral(osFilter,
     468          13 :                                      poExpr->papoSubExpr[bAxisSwap ? 0 : 1]))
     469           0 :             return false;
     470          13 :         osFilter += "</gml:lowerCorner>";
     471          13 :         osFilter += "<gml:upperCorner>";
     472          13 :         if (!WFS_ExprDumpRawLitteral(osFilter,
     473          13 :                                      poExpr->papoSubExpr[bAxisSwap ? 3 : 2]))
     474           0 :             return false;
     475          13 :         osFilter += " ";
     476          13 :         if (!WFS_ExprDumpRawLitteral(osFilter,
     477          13 :                                      poExpr->papoSubExpr[bAxisSwap ? 2 : 3]))
     478           0 :             return false;
     479          13 :         osFilter += "</gml:upperCorner>";
     480          13 :         osFilter += "</gml:Envelope>";
     481          13 :         return true;
     482             :     }
     483             : 
     484          37 :     if (poExpr->nOperation == SWQ_CUSTOM_FUNC &&
     485          37 :         EQUAL(poExpr->string_value, "ST_GeomFromText"))
     486             :     {
     487          12 :         OGRSpatialReference oSRS;
     488          12 :         const char *pszSRSName = WFS_ExprGetSRSName(poExpr, 1, psOptions, oSRS);
     489          12 :         OGRGeometry *poGeom = nullptr;
     490          12 :         const char *pszWKT = poExpr->papoSubExpr[0]->string_value;
     491          12 :         OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom);
     492          12 :         char **papszOptions = nullptr;
     493          12 :         papszOptions = CSLSetNameValue(papszOptions, "FORMAT", "GML3");
     494          12 :         if (pszSRSName != nullptr)
     495             :         {
     496          12 :             oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     497             : 
     498          12 :             if (STARTS_WITH_CI(pszSRSName, "urn:ogc:def:crs:EPSG::"))
     499             :                 papszOptions =
     500           8 :                     CSLSetNameValue(papszOptions, "GML3_LONGSRS", "YES");
     501             :             else
     502             :                 papszOptions =
     503           4 :                     CSLSetNameValue(papszOptions, "GML3_LONGSRS", "NO");
     504             : 
     505          12 :             poGeom->assignSpatialReference(&oSRS);
     506             :         }
     507             :         papszOptions =
     508          12 :             CSLSetNameValue(papszOptions, "GMLID",
     509          12 :                             CPLSPrintf("id%d", psOptions->nUniqueGeomGMLId++));
     510             :         char *pszGML =
     511          12 :             OGR_G_ExportToGMLEx(OGRGeometry::ToHandle(poGeom), papszOptions);
     512          12 :         osFilter += pszGML;
     513          12 :         CSLDestroy(papszOptions);
     514          12 :         delete poGeom;
     515          12 :         CPLFree(pszGML);
     516          12 :         return true;
     517             :     }
     518             : 
     519          25 :     if (poExpr->nOperation == SWQ_CUSTOM_FUNC)
     520             :     {
     521          25 :         const char *pszName =
     522          50 :             EQUAL(poExpr->string_value, "ST_Equals")       ? "Equals"
     523          50 :             : EQUAL(poExpr->string_value, "ST_Disjoint")   ? "Disjoint"
     524          50 :             : EQUAL(poExpr->string_value, "ST_Touches")    ? "Touches"
     525          50 :             : EQUAL(poExpr->string_value, "ST_Contains")   ? "Contains"
     526          29 :             : EQUAL(poExpr->string_value, "ST_Intersects") ? "Intersects"
     527           6 :             : EQUAL(poExpr->string_value, "ST_Within")     ? "Within"
     528           4 :             : EQUAL(poExpr->string_value, "ST_Crosses")    ? "Crosses"
     529           4 :             : EQUAL(poExpr->string_value, "ST_Overlaps")   ? "Overlaps"
     530           2 :             : EQUAL(poExpr->string_value, "ST_DWithin")    ? "DWithin"
     531           0 :             : EQUAL(poExpr->string_value, "ST_Beyond")     ? "Beyond"
     532             :                                                            : nullptr;
     533          25 :         if (pszName == nullptr)
     534           0 :             return false;
     535          25 :         osFilter += "<";
     536          25 :         osFilter += psOptions->pszNSPrefix;
     537          25 :         osFilter += pszName;
     538          25 :         osFilter += ">";
     539          75 :         for (int i = 0; i < 2; i++)
     540             :         {
     541          50 :             if (i == 1 && poExpr->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
     542          25 :                 poExpr->papoSubExpr[1]->eNodeType == SNT_OPERATION &&
     543          25 :                 poExpr->papoSubExpr[1]->nOperation == SWQ_CUSTOM_FUNC &&
     544          25 :                 (EQUAL(poExpr->papoSubExpr[1]->string_value,
     545          13 :                        "ST_GeomFromText") ||
     546          13 :                  EQUAL(poExpr->papoSubExpr[1]->string_value,
     547             :                        "ST_MakeEnvelope")))
     548             :             {
     549             :                 int bSameTable =
     550          47 :                     psOptions->poFDefn != nullptr &&
     551          22 :                     (poExpr->papoSubExpr[0]->table_name == nullptr ||
     552           0 :                      EQUAL(poExpr->papoSubExpr[0]->table_name,
     553          25 :                            psOptions->poFDefn->GetName()));
     554          25 :                 if (bSameTable)
     555             :                 {
     556          44 :                     const int nIndex = psOptions->poFDefn->GetGeomFieldIndex(
     557          22 :                         poExpr->papoSubExpr[0]->string_value);
     558          22 :                     if (nIndex >= 0)
     559             :                     {
     560          22 :                         psOptions->poSRS =
     561          22 :                             psOptions->poFDefn->GetGeomFieldDefn(nIndex)
     562          22 :                                 ->GetSpatialRef();
     563             :                     }
     564             :                 }
     565           3 :                 else if (psOptions->poDS != nullptr)
     566             :                 {
     567           4 :                     OGRLayer *poLayer = psOptions->poDS->GetLayerByName(
     568           2 :                         poExpr->papoSubExpr[0]->table_name);
     569           2 :                     if (poLayer)
     570             :                     {
     571           2 :                         OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
     572           4 :                         const int nIndex = poFDefn->GetGeomFieldIndex(
     573           2 :                             poExpr->papoSubExpr[0]->string_value);
     574           2 :                         if (nIndex >= 0)
     575             :                         {
     576           2 :                             psOptions->poSRS = poFDefn->GetGeomFieldDefn(nIndex)
     577           2 :                                                    ->GetSpatialRef();
     578             :                         }
     579             :                     }
     580             :                 }
     581             :             }
     582         100 :             const bool bRet = WFS_ExprDumpAsOGCFilter(
     583          50 :                 osFilter, poExpr->papoSubExpr[i], FALSE, psOptions);
     584          50 :             psOptions->poSRS = nullptr;
     585          50 :             if (!bRet)
     586           0 :                 return false;
     587             :         }
     588          25 :         if (poExpr->nSubExprCount > 2)
     589             :         {
     590             :             osFilter +=
     591           2 :                 CPLSPrintf("<%sDistance unit=\"m\">", psOptions->pszNSPrefix);
     592           2 :             if (!WFS_ExprDumpRawLitteral(osFilter, poExpr->papoSubExpr[2]))
     593           0 :                 return false;
     594           2 :             osFilter += CPLSPrintf("</%sDistance>", psOptions->pszNSPrefix);
     595             :         }
     596          25 :         osFilter += "</";
     597          25 :         osFilter += psOptions->pszNSPrefix;
     598          25 :         osFilter += pszName;
     599          25 :         osFilter += ">";
     600          25 :         return true;
     601             :     }
     602             : 
     603           0 :     return false;
     604             : }
     605             : 
     606             : /************************************************************************/
     607             : /*               WFS_TurnSQLFilterToOGCFilter()                         */
     608             : /************************************************************************/
     609             : 
     610             : CPLString
     611          74 : WFS_TurnSQLFilterToOGCFilter(const swq_expr_node *poExpr, GDALDataset *poDS,
     612             :                              OGRFeatureDefn *poFDefn, int nVersion,
     613             :                              int bPropertyIsNotEqualToSupported,
     614             :                              int bUseFeatureId, int bGmlObjectIdNeedsGMLPrefix,
     615             :                              const char *pszNSPrefix, int *pbOutNeedsNullCheck)
     616             : {
     617          74 :     CPLString osFilter;
     618             :     /* If the filter is only made of querying one or several gml_id */
     619             :     /* (with OR operator), we turn this to <GmlObjectId> list */
     620          74 :     if (!WFS_ExprDumpGmlObjectIdFilter(osFilter, poExpr, bUseFeatureId,
     621             :                                        bGmlObjectIdNeedsGMLPrefix, nVersion))
     622             :     {
     623             :         ExprDumpFilterOptions sOptions;
     624          54 :         sOptions.nVersion = nVersion;
     625          54 :         sOptions.bPropertyIsNotEqualToSupported =
     626          54 :             CPL_TO_BOOL(bPropertyIsNotEqualToSupported);
     627          54 :         sOptions.bOutNeedsNullCheck = FALSE;
     628          54 :         sOptions.poDS = poDS;
     629          54 :         sOptions.poFDefn = poFDefn;
     630          54 :         sOptions.nUniqueGeomGMLId = 1;
     631          54 :         sOptions.poSRS = nullptr;
     632          54 :         sOptions.pszNSPrefix = pszNSPrefix;
     633          54 :         osFilter = "";
     634          54 :         if (!WFS_ExprDumpAsOGCFilter(osFilter, poExpr, TRUE, &sOptions))
     635           6 :             osFilter = "";
     636             :         /*else
     637             :             CPLDebug("WFS", "Filter %s", osFilter.c_str());*/
     638          54 :         *pbOutNeedsNullCheck = sOptions.bOutNeedsNullCheck;
     639             :     }
     640             : 
     641          74 :     return osFilter;
     642             : }
     643             : 
     644             : /************************************************************************/
     645             : /*                  OGRWFSSpatialBooleanPredicateChecker()              */
     646             : /************************************************************************/
     647             : 
     648          35 : static swq_field_type OGRWFSSpatialBooleanPredicateChecker(
     649             :     swq_expr_node *op, CPL_UNUSED int bAllowMismatchTypeOnFieldComparison)
     650             : {
     651          35 :     if (op->nSubExprCount != 2)
     652             :     {
     653           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     654             :                  "Wrong number of arguments for %s", op->string_value);
     655           2 :         return SWQ_ERROR;
     656             :     }
     657          97 :     for (int i = 0; i < op->nSubExprCount; i++)
     658             :     {
     659          66 :         if (op->papoSubExpr[i]->field_type != SWQ_GEOMETRY)
     660             :         {
     661           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     662             :                      "Wrong field type for argument %d of %s", i + 1,
     663             :                      op->string_value);
     664           2 :             return SWQ_ERROR;
     665             :         }
     666             :     }
     667          31 :     return SWQ_BOOLEAN;
     668             : }
     669             : 
     670             : /************************************************************************/
     671             : /*                           OGRWFSCheckSRIDArg()                       */
     672             : /************************************************************************/
     673             : 
     674          27 : static bool OGRWFSCheckSRIDArg(swq_expr_node *op, int iSubArgIndex)
     675             : {
     676          27 :     if (op->papoSubExpr[iSubArgIndex]->field_type == SWQ_INTEGER)
     677             :     {
     678          13 :         OGRSpatialReference oSRS;
     679          26 :         if (oSRS.importFromEPSGA(static_cast<int>(
     680          13 :                 op->papoSubExpr[iSubArgIndex]->int_value)) != OGRERR_NONE)
     681             :         {
     682           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     683             :                      "Wrong value for argument %d of %s", iSubArgIndex + 1,
     684             :                      op->string_value);
     685           2 :             return false;
     686             :         }
     687             :     }
     688          14 :     else if (op->papoSubExpr[iSubArgIndex]->field_type == SWQ_STRING)
     689             :     {
     690          12 :         OGRSpatialReference oSRS;
     691          24 :         if (oSRS.SetFromUserInput(
     692          12 :                 op->papoSubExpr[iSubArgIndex]->string_value) != OGRERR_NONE)
     693             :         {
     694           4 :             CPLError(CE_Failure, CPLE_AppDefined,
     695             :                      "Wrong value for argument %d of %s", iSubArgIndex + 1,
     696             :                      op->string_value);
     697           4 :             return false;
     698             :         }
     699             :     }
     700             :     else
     701             :     {
     702           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     703             :                  "Wrong field type for argument %d of %s", iSubArgIndex + 1,
     704             :                  op->string_value);
     705           2 :         return false;
     706             :     }
     707          19 :     return true;
     708             : }
     709             : 
     710             : /************************************************************************/
     711             : /*                   OGRWFSMakeEnvelopeChecker()                        */
     712             : /************************************************************************/
     713             : 
     714             : static swq_field_type
     715          23 : OGRWFSMakeEnvelopeChecker(swq_expr_node *op,
     716             :                           CPL_UNUSED int bAllowMismatchTypeOnFieldComparison)
     717             : {
     718          23 :     if (op->nSubExprCount != 4 && op->nSubExprCount != 5)
     719             :     {
     720           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     721             :                  "Wrong number of arguments for %s", op->string_value);
     722           2 :         return SWQ_ERROR;
     723             :     }
     724         103 :     for (int i = 0; i < 4; i++)
     725             :     {
     726          84 :         if (op->papoSubExpr[i]->field_type != SWQ_INTEGER &&
     727          34 :             op->papoSubExpr[i]->field_type != SWQ_INTEGER64 &&
     728          34 :             op->papoSubExpr[i]->field_type != SWQ_FLOAT)
     729             :         {
     730           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     731             :                      "Wrong field type for argument %d of %s", i + 1,
     732             :                      op->string_value);
     733           2 :             return SWQ_ERROR;
     734             :         }
     735             :     }
     736          19 :     if (op->nSubExprCount == 5)
     737             :     {
     738          13 :         if (!OGRWFSCheckSRIDArg(op, 4))
     739           6 :             return SWQ_ERROR;
     740             :     }
     741          13 :     return SWQ_GEOMETRY;
     742             : }
     743             : 
     744             : /************************************************************************/
     745             : /*                   OGRWFSGeomFromTextChecker()                        */
     746             : /************************************************************************/
     747             : 
     748             : static swq_field_type
     749          28 : OGRWFSGeomFromTextChecker(swq_expr_node *op,
     750             :                           CPL_UNUSED int bAllowMismatchTypeOnFieldComparison)
     751             : {
     752          28 :     if (op->nSubExprCount != 1 && op->nSubExprCount != 2)
     753             :     {
     754           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     755             :                  "Wrong number of arguments for %s", op->string_value);
     756           2 :         return SWQ_ERROR;
     757             :     }
     758          26 :     if (op->papoSubExpr[0]->field_type != SWQ_STRING)
     759             :     {
     760           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     761             :                  "Wrong field type for argument %d of %s", 1, op->string_value);
     762           2 :         return SWQ_ERROR;
     763             :     }
     764          24 :     OGRGeometry *poGeom = nullptr;
     765          24 :     const char *pszWKT = op->papoSubExpr[0]->string_value;
     766          24 :     if (OGRGeometryFactory::createFromWkt(pszWKT, nullptr, &poGeom) !=
     767             :         OGRERR_NONE)
     768             :     {
     769           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     770             :                  "Wrong value for argument %d of %s", 1, op->string_value);
     771           2 :         return SWQ_ERROR;
     772             :     }
     773          22 :     delete poGeom;
     774          22 :     if (op->nSubExprCount == 2)
     775             :     {
     776          14 :         if (!OGRWFSCheckSRIDArg(op, 1))
     777           2 :             return SWQ_ERROR;
     778             :     }
     779          20 :     return SWQ_GEOMETRY;
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                      OGRWFSDWithinBeyondChecker()                    */
     784             : /************************************************************************/
     785             : 
     786             : static swq_field_type
     787           8 : OGRWFSDWithinBeyondChecker(swq_expr_node *op,
     788             :                            CPL_UNUSED int bAllowMismatchTypeOnFieldComparison)
     789             : {
     790           8 :     if (op->nSubExprCount != 3)
     791             :     {
     792           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     793             :                  "Wrong number of arguments for %s", op->string_value);
     794           2 :         return SWQ_ERROR;
     795             :     }
     796          16 :     for (int i = 0; i < 2; i++)
     797             :     {
     798          12 :         if (op->papoSubExpr[i]->field_type != SWQ_GEOMETRY)
     799             :         {
     800           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     801             :                      "Wrong field type for argument %d of %s", i + 1,
     802             :                      op->string_value);
     803           2 :             return SWQ_ERROR;
     804             :         }
     805             :     }
     806           4 :     if (op->papoSubExpr[2]->field_type != SWQ_INTEGER &&
     807           2 :         op->papoSubExpr[2]->field_type != SWQ_INTEGER64 &&
     808           2 :         op->papoSubExpr[2]->field_type != SWQ_FLOAT)
     809             :     {
     810           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     811             :                  "Wrong field type for argument %d of %s", 2 + 1,
     812             :                  op->string_value);
     813           2 :         return SWQ_ERROR;
     814             :     }
     815           2 :     return SWQ_BOOLEAN;
     816             : }
     817             : 
     818             : /************************************************************************/
     819             : /*                   OGRWFSCustomFuncRegistrar                          */
     820             : /************************************************************************/
     821             : 
     822             : class OGRWFSCustomFuncRegistrar : public swq_custom_func_registrar
     823             : {
     824             :   public:
     825           1 :     OGRWFSCustomFuncRegistrar()
     826           1 :     {
     827           1 :     }
     828             : 
     829             :     virtual const swq_operation *GetOperator(const char *) override;
     830             : };
     831             : 
     832             : /************************************************************************/
     833             : /*                  WFSGetCustomFuncRegistrar()                         */
     834             : /************************************************************************/
     835             : 
     836         120 : swq_custom_func_registrar *WFSGetCustomFuncRegistrar()
     837             : {
     838         120 :     static OGRWFSCustomFuncRegistrar obj;
     839         120 :     return &obj;
     840             : }
     841             : 
     842             : /************************************************************************/
     843             : /*                           GetOperator()                              */
     844             : /************************************************************************/
     845             : 
     846             : static const swq_operation OGRWFSSpatialOps[] = {
     847             :     {"ST_Equals", SWQ_CUSTOM_FUNC, nullptr,
     848             :      OGRWFSSpatialBooleanPredicateChecker},
     849             :     {"ST_Disjoint", SWQ_CUSTOM_FUNC, nullptr,
     850             :      OGRWFSSpatialBooleanPredicateChecker},
     851             :     {"ST_Touches", SWQ_CUSTOM_FUNC, nullptr,
     852             :      OGRWFSSpatialBooleanPredicateChecker},
     853             :     {"ST_Contains", SWQ_CUSTOM_FUNC, nullptr,
     854             :      OGRWFSSpatialBooleanPredicateChecker},
     855             :     /*{ "ST_Covers", SWQ_CUSTOM_FUNC, NULL, OGRWFSSpatialBooleanPredicateChecker
     856             :        },*/
     857             :     {"ST_Intersects", SWQ_CUSTOM_FUNC, nullptr,
     858             :      OGRWFSSpatialBooleanPredicateChecker},
     859             :     {"ST_Within", SWQ_CUSTOM_FUNC, nullptr,
     860             :      OGRWFSSpatialBooleanPredicateChecker},
     861             :     /*{ "ST_CoveredBy", SWQ_CUSTOM_FUNC, NULL,
     862             :        OGRWFSSpatialBooleanPredicateChecker },*/
     863             :     {"ST_Crosses", SWQ_CUSTOM_FUNC, nullptr,
     864             :      OGRWFSSpatialBooleanPredicateChecker},
     865             :     {"ST_Overlaps", SWQ_CUSTOM_FUNC, nullptr,
     866             :      OGRWFSSpatialBooleanPredicateChecker},
     867             :     {"ST_DWithin", SWQ_CUSTOM_FUNC, nullptr, OGRWFSDWithinBeyondChecker},
     868             :     {"ST_Beyond", SWQ_CUSTOM_FUNC, nullptr, OGRWFSDWithinBeyondChecker},
     869             :     {"ST_MakeEnvelope", SWQ_CUSTOM_FUNC, nullptr, OGRWFSMakeEnvelopeChecker},
     870             :     {"ST_GeomFromText", SWQ_CUSTOM_FUNC, nullptr, OGRWFSGeomFromTextChecker}};
     871             : 
     872         112 : const swq_operation *OGRWFSCustomFuncRegistrar::GetOperator(const char *pszName)
     873             : {
     874         928 :     for (const auto &op : OGRWFSSpatialOps)
     875             :     {
     876         928 :         if (EQUAL(op.pszName, pszName))
     877         112 :             return &op;
     878             :     }
     879           0 :     return nullptr;
     880             : }

Generated by: LCOV version 1.14