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