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