• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geographika / mapserver / 28017742761

23 Jun 2026 09:51AM UTC coverage: 42.467% (+0.01%) from 42.453%
28017742761

push

github

geographika
Add FID tests

1 of 1 new or added line in 1 file covered. (100.0%)

535 existing lines in 4 files now uncovered.

64718 of 152397 relevant lines covered (42.47%)

27423.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

71.15
/src/mapogr.cpp
1
/**********************************************************************
2
 * $Id$
3
 *
4
 * Project:  MapServer
5
 * Purpose:  OGR Link
6
 * Author:   Daniel Morissette, DM Solutions Group (morissette@dmsolutions.ca)
7
 *           Frank Warmerdam (warmerdam@pobox.com)
8
 *
9
 **********************************************************************
10
 * Copyright (c) 2000-2005, Daniel Morissette, DM Solutions Group Inc
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included in
20
 * all copies of this Software or works derived from this Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 **********************************************************************/
30

31
#include <assert.h>
32
#include "mapserver.h"
33
#include "mapproject.h"
34
#include "mapthread.h"
35
#include "mapows.h"
36

37
#include <algorithm>
38
#include <string>
39
#include <vector>
40

41
#include "gdal.h"
42
#include "cpl_conv.h"
43
#include "cpl_string.h"
44
#include "ogr_srs_api.h"
45

46
#include <memory>
47

48
#define ACQUIRE_OGR_LOCK msAcquireLock(TLOCK_OGR)
49
#define RELEASE_OGR_LOCK msReleaseLock(TLOCK_OGR)
50

51
// GDAL 1.x API
52
#include "ogr_api.h"
53

54
typedef struct ms_ogr_file_info_t {
55
  char *pszFname;
56
  char *pszLayerDef;
57
  int nLayerIndex;
58
  OGRDataSourceH hDS;
59
  OGRLayerH hLayer;
60
  OGRFeatureH hLastFeature;
61

62
  int nTileId;             /* applies on the tiles themselves. */
63
  projectionObj sTileProj; /* applies on the tiles themselves. */
64

65
  struct ms_ogr_file_info_t *poCurTile; /* exists on tile index, -> tiles */
66
  bool rect_is_defined;
67
  rectObj rect; /* set by TranslateMsExpression (possibly) and WhichShapes */
68

69
  int last_record_index_read;
70

71
  const char *dialect; /* NULL, Spatialite or PostgreSQL */
72
  char *pszSelect;
73
  char *pszSpatialFilterTableName;
74
  char *pszSpatialFilterGeometryColumn;
75
  char *pszMainTableName;
76
  char *pszRowId;
77
  int bIsOKForSQLCompose;
78
  bool bHasSpatialIndex; // used only for spatialite for now
79
  char *pszTablePrefix;  // prefix to qualify field names. used only for
80
                         // spatialite & gpkg for now when a join is done for
81
                         // spatial filtering.
82

83
  int bPaging;
84

85
  char *pszWHERE;
86

87
} msOGRFileInfo;
88

89
static int msOGRLayerIsOpen(layerObj *layer);
90
static int msOGRLayerInitItemInfo(layerObj *layer);
91
static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
92
                                  shapeObj *shape);
93
static void msOGRCloseConnection(void *conn_handle);
94

95
/* ==================================================================
96
 * Geometry conversion functions
97
 * ================================================================== */
98

99
/**********************************************************************
100
 *                     ogrPointsAddPoint()
101
 *
102
 * NOTE: This function assumes the line->point array already has been
103
 * allocated large enough for the point to be added, but that numpoints
104
 * does not include this new point.
105
 **********************************************************************/
106
static void ogrPointsAddPoint(lineObj *line, double dX, double dY, double dZ,
15,781✔
107
                              int lineindex, rectObj *bounds) {
108
  /* Keep track of shape bounds */
109
  if (line->numpoints == 0 && lineindex == 0) {
15,781✔
110
    bounds->minx = bounds->maxx = dX;
15,073✔
111
    bounds->miny = bounds->maxy = dY;
15,073✔
112
  } else {
113
    if (dX < bounds->minx)
708✔
114
      bounds->minx = dX;
55✔
115
    if (dX > bounds->maxx)
708✔
116
      bounds->maxx = dX;
21✔
117
    if (dY < bounds->miny)
708✔
118
      bounds->miny = dY;
19✔
119
    if (dY > bounds->maxy)
708✔
120
      bounds->maxy = dY;
21✔
121
  }
122

123
  line->point[line->numpoints].x = dX;
15,781✔
124
  line->point[line->numpoints].y = dY;
15,781✔
125
  line->point[line->numpoints].z = dZ;
15,781✔
126
  line->point[line->numpoints].m = 0.0;
15,781✔
127
  line->numpoints++;
15,781✔
128
}
15,781✔
129

130
/**********************************************************************
131
 *                     ogrGeomPoints()
132
 **********************************************************************/
133
static int ogrGeomPoints(OGRGeometryH hGeom, shapeObj *outshp) {
15,073✔
134
  int i;
135
  int numpoints;
136

137
  if (hGeom == NULL)
15,073✔
138
    return 0;
139

140
  OGRwkbGeometryType eGType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
15,073✔
141

142
  /* -------------------------------------------------------------------- */
143
  /*      Container types result in recursive invocation on each          */
144
  /*      subobject to add a set of points to the current list.           */
145
  /* -------------------------------------------------------------------- */
146
  switch (eGType) {
15,073✔
147
  case wkbGeometryCollection:
148
  case wkbMultiLineString:
149
  case wkbMultiPolygon:
150
  case wkbPolygon: {
151
    /* Treat it as GeometryCollection */
152
    for (int iGeom = 0; iGeom < OGR_G_GetGeometryCount(hGeom); iGeom++) {
×
153
      if (ogrGeomPoints(OGR_G_GetGeometryRef(hGeom, iGeom), outshp) == -1)
×
154
        return -1;
155
    }
156

157
    return 0;
158
  } break;
159

160
  case wkbPoint:
161
  case wkbMultiPoint:
162
  case wkbLineString:
163
  case wkbLinearRing:
164
    /* We will handle these directly */
165
    break;
166

167
  default:
×
168
    /* There shouldn't be any more cases should there? */
169
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported yet.",
×
170
               "ogrGeomPoints()", OGR_G_GetGeometryName(hGeom));
171
    return (-1);
×
172
  }
173

174
  /* ------------------------------------------------------------------
175
   * Count total number of points
176
   * ------------------------------------------------------------------ */
177
  if (eGType == wkbPoint) {
15,073✔
178
    numpoints = 1;
179
  } else if (eGType == wkbLineString || eGType == wkbLinearRing) {
9✔
180
    numpoints = OGR_G_GetPointCount(hGeom);
×
181
  } else if (eGType == wkbMultiPoint) {
182
    numpoints = OGR_G_GetGeometryCount(hGeom);
9✔
183
  } else {
184
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported yet.",
185
               "ogrGeomPoints()", OGR_G_GetGeometryName(hGeom));
186
    return (-1);
187
  }
188

189
  /* ------------------------------------------------------------------
190
   * Do we need to allocate a line object to contain all our points?
191
   * ------------------------------------------------------------------ */
192
  if (outshp->numlines == 0) {
15,073✔
193
    lineObj newline;
194

195
    newline.numpoints = 0;
15,073✔
196
    newline.point = NULL;
15,073✔
197
    msAddLine(outshp, &newline);
15,073✔
198
  }
199

200
  /* ------------------------------------------------------------------
201
   * Extend the point array for the new of points to add from the
202
   * current geometry.
203
   * ------------------------------------------------------------------ */
204
  lineObj *line = outshp->line + outshp->numlines - 1;
15,073✔
205

206
  if (line->point == NULL)
15,073✔
207
    line->point = (pointObj *)malloc(sizeof(pointObj) * numpoints);
×
208
  else
209
    line->point = (pointObj *)realloc(
15,073✔
210
        line->point, sizeof(pointObj) * (numpoints + line->numpoints));
15,073✔
211

212
  if (!line->point) {
15,073✔
213
    msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
×
214
               "ogrGeomPoints()");
215
    return (-1);
×
216
  }
217

218
  /* ------------------------------------------------------------------
219
   * alloc buffer and filter/transform points
220
   * ------------------------------------------------------------------ */
221
  if (eGType == wkbPoint) {
15,073✔
222
    ogrPointsAddPoint(line, OGR_G_GetX(hGeom, 0), OGR_G_GetY(hGeom, 0),
15,064✔
223
                      OGR_G_GetZ(hGeom, 0), outshp->numlines - 1,
15,064✔
224
                      &(outshp->bounds));
225
  } else if (eGType == wkbLineString || eGType == wkbLinearRing) {
9✔
226
    for (i = 0; i < numpoints; i++)
×
227
      ogrPointsAddPoint(line, OGR_G_GetX(hGeom, i), OGR_G_GetY(hGeom, i),
×
228
                        OGR_G_GetZ(hGeom, i), outshp->numlines - 1,
×
229
                        &(outshp->bounds));
230
  } else if (eGType == wkbMultiPoint) {
231
    for (i = 0; i < numpoints; i++) {
726✔
232
      OGRGeometryH hPoint = OGR_G_GetGeometryRef(hGeom, i);
717✔
233
      ogrPointsAddPoint(line, OGR_G_GetX(hPoint, 0), OGR_G_GetY(hPoint, 0),
717✔
234
                        OGR_G_GetZ(hPoint, 0), outshp->numlines - 1,
717✔
235
                        &(outshp->bounds));
236
    }
237
  }
238

239
  outshp->type = MS_SHAPE_POINT;
15,073✔
240

241
  return (0);
15,073✔
242
}
243

244
/**********************************************************************
245
 *                     ogrGeomLine()
246
 *
247
 * Recursively convert any OGRGeometry into a shapeObj.  Each part becomes
248
 * a line in the overall shapeObj.
249
 **********************************************************************/
250
static int ogrGeomLine(OGRGeometryH hGeom, shapeObj *outshp, int bCloseRings) {
4,142✔
251
  if (hGeom == NULL)
4,142✔
252
    return 0;
253

254
  /* ------------------------------------------------------------------
255
   * Use recursive calls for complex geometries
256
   * ------------------------------------------------------------------ */
257
  OGRwkbGeometryType eGType = wkbFlatten(OGR_G_GetGeometryType(hGeom));
4,142✔
258

259
  if (eGType == wkbPolygon || eGType == wkbGeometryCollection ||
4,142✔
260
      eGType == wkbMultiLineString || eGType == wkbMultiPolygon) {
261
    if (eGType == wkbPolygon && outshp->type == MS_SHAPE_NULL)
1,378✔
262
      outshp->type = MS_SHAPE_POLYGON;
1,368✔
263

264
    /* Treat it as GeometryCollection */
265
    for (int iGeom = 0; iGeom < OGR_G_GetGeometryCount(hGeom); iGeom++) {
2,827✔
266
      if (ogrGeomLine(OGR_G_GetGeometryRef(hGeom, iGeom), outshp,
1,430✔
267
                      bCloseRings) == -1)
268
        return -1;
269
    }
270
  }
271
  /* ------------------------------------------------------------------
272
   * OGRPoint and OGRMultiPoint
273
   * ------------------------------------------------------------------ */
274
  else if (eGType == wkbPoint || eGType == wkbMultiPoint) {
275
    /* Hummmm a point when we're drawing lines/polygons... just drop it! */
276
  }
277
  /* ------------------------------------------------------------------
278
   * OGRLinearRing/OGRLineString ... both are of type wkbLineString
279
   * ------------------------------------------------------------------ */
280
  else if (eGType == wkbLineString) {
281
    int j, numpoints;
282
    lineObj line = {0, NULL};
2,741✔
283
    double dX, dY;
284

285
    if ((numpoints = OGR_G_GetPointCount(hGeom)) < 2)
2,741✔
286
      return 0;
×
287

288
    if (outshp->type == MS_SHAPE_NULL)
2,741✔
289
      outshp->type = MS_SHAPE_LINE;
1,339✔
290

291
    line.numpoints = 0;
2,741✔
292
    line.point = (pointObj *)malloc(sizeof(pointObj) * (numpoints + 1));
2,741✔
293
    if (!line.point) {
2,741✔
294
      msSetError(MS_MEMERR, "Unable to allocate temporary point cache.",
×
295
                 "ogrGeomLine");
296
      return (-1);
×
297
    }
298

299
    OGR_G_GetPoints(hGeom, &(line.point[0].x), sizeof(pointObj),
2,741✔
300
                    &(line.point[0].y), sizeof(pointObj), &(line.point[0].z),
2,741✔
301
                    sizeof(pointObj));
302

303
    for (j = 0; j < numpoints; j++) {
287,614✔
304
      dX = line.point[j].x = OGR_G_GetX(hGeom, j);
284,873✔
305
      dY = line.point[j].y = OGR_G_GetY(hGeom, j);
284,873✔
306

307
      /* Keep track of shape bounds */
308
      if (j == 0 && outshp->numlines == 0) {
284,873✔
309
        outshp->bounds.minx = outshp->bounds.maxx = dX;
2,707✔
310
        outshp->bounds.miny = outshp->bounds.maxy = dY;
2,707✔
311
      } else {
312
        if (dX < outshp->bounds.minx)
282,166✔
313
          outshp->bounds.minx = dX;
24,682✔
314
        if (dX > outshp->bounds.maxx)
282,166✔
315
          outshp->bounds.maxx = dX;
67,335✔
316
        if (dY < outshp->bounds.miny)
282,166✔
317
          outshp->bounds.miny = dY;
91,656✔
318
        if (dY > outshp->bounds.maxy)
282,166✔
319
          outshp->bounds.maxy = dY;
25,087✔
320
      }
321
    }
322
    line.numpoints = numpoints;
2,741✔
323

324
    if (bCloseRings && (line.point[line.numpoints - 1].x != line.point[0].x ||
2,741✔
325
                        line.point[line.numpoints - 1].y != line.point[0].y)) {
1,375✔
326
      line.point[line.numpoints].x = line.point[0].x;
38✔
327
      line.point[line.numpoints].y = line.point[0].y;
38✔
328
      line.point[line.numpoints].z = line.point[0].z;
38✔
329
      line.numpoints++;
38✔
330
    }
331

332
    msAddLineDirectly(outshp, &line);
2,741✔
333
  } else {
334
    msSetError(MS_OGRERR, "OGRGeometry type `%s' not supported.",
×
335
               "ogrGeomLine()", OGR_G_GetGeometryName(hGeom));
336
    return (-1);
×
337
  }
338

339
  return (0);
340
}
341

342
/**********************************************************************
343
 *                     ogrGetLinearGeometry()
344
 *
345
 * Fetch geometry from OGR feature. If using GDAL 2.0 or later, the geometry
346
 * might be of curve type, so linearize it.
347
 **********************************************************************/
348
static OGRGeometryH ogrGetLinearGeometry(OGRFeatureH hFeature) {
17,695✔
349
  /* Convert in place and reassign to the feature */
350
  OGRGeometryH hGeom = OGR_F_StealGeometry(hFeature);
17,695✔
351
  if (hGeom != NULL) {
17,695✔
352
    hGeom = OGR_G_ForceTo(hGeom, OGR_GT_GetLinear(OGR_G_GetGeometryType(hGeom)),
17,695✔
353
                          NULL);
354
    OGR_F_SetGeometryDirectly(hFeature, hGeom);
17,695✔
355
  }
356
  return hGeom;
17,695✔
357
}
358

359
/**********************************************************************
360
 *                     ogrConvertGeometry()
361
 *
362
 * Convert OGR geometry into a shape object doing the best possible
363
 * job to match OGR Geometry type and layer type.
364
 *
365
 * If layer type is incompatible with geometry, then shape is returned with
366
 * shape->type = MS_SHAPE_NULL
367
 **********************************************************************/
368
static int ogrConvertGeometry(OGRGeometryH hGeom, shapeObj *outshp,
17,785✔
369
                              enum MS_LAYER_TYPE layertype) {
370
  /* ------------------------------------------------------------------
371
   * Process geometry according to layer type
372
   * ------------------------------------------------------------------ */
373
  int nStatus = MS_SUCCESS;
374

375
  if (hGeom == NULL) {
17,785✔
376
    // Empty geometry... this is not an error... we'll just skip it
377
    return MS_SUCCESS;
378
  }
379

380
  switch (layertype) {
17,785✔
381
    /* ------------------------------------------------------------------
382
     *      POINT layer - Any geometry can be converted to point/multipoint
383
     * ------------------------------------------------------------------ */
384
  case MS_LAYER_POINT:
15,073✔
385
    if (ogrGeomPoints(hGeom, outshp) == -1) {
15,073✔
386
      nStatus = MS_FAILURE; // Error message already produced.
387
    }
388
    break;
389
    /* ------------------------------------------------------------------
390
     *      LINE layer
391
     * ------------------------------------------------------------------ */
392
  case MS_LAYER_LINE:
1,349✔
393
    if (ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
1,349✔
394
      nStatus = MS_FAILURE; // Error message already produced.
395
    }
396
    if (outshp->type != MS_SHAPE_LINE && outshp->type != MS_SHAPE_POLYGON)
1,349✔
397
      outshp->type = MS_SHAPE_NULL; // Incompatible type for this layer
×
398
    break;
399
    /* ------------------------------------------------------------------
400
     *      POLYGON layer
401
     * ------------------------------------------------------------------ */
402
  case MS_LAYER_POLYGON:
1,363✔
403
    if (ogrGeomLine(hGeom, outshp, MS_TRUE) == -1) {
1,363✔
404
      nStatus = MS_FAILURE; // Error message already produced.
405
    }
406
    if (outshp->type != MS_SHAPE_POLYGON)
1,363✔
407
      outshp->type = MS_SHAPE_NULL; // Incompatible type for this layer
7✔
408
    break;
409
    /* ------------------------------------------------------------------
410
     *      Chart or Query layers - return real feature type
411
     * ------------------------------------------------------------------ */
412
  case MS_LAYER_CHART:
×
413
  case MS_LAYER_QUERY:
414
    switch (OGR_G_GetGeometryType(hGeom)) {
×
415
    case wkbPoint:
×
416
    case wkbPoint25D:
417
    case wkbMultiPoint:
418
    case wkbMultiPoint25D:
419
      if (ogrGeomPoints(hGeom, outshp) == -1) {
×
420
        nStatus = MS_FAILURE; // Error message already produced.
421
      }
422
      break;
423
    default:
×
424
      // Handle any non-point types as lines/polygons ... ogrGeomLine()
425
      // will decide the shape type
426
      if (ogrGeomLine(hGeom, outshp, MS_FALSE) == -1) {
×
427
        nStatus = MS_FAILURE; // Error message already produced.
428
      }
429
    }
430
    break;
431

432
  default:
×
433
    msSetError(MS_MISCERR, "Unknown or unsupported layer type.",
×
434
               "msOGRLayerNextShape()");
435
    nStatus = MS_FAILURE;
436
  } /* switch layertype */
437

438
  return nStatus;
439
}
440

441
/**********************************************************************
442
 *                     msOGRGeometryToShape()
443
 *
444
 * Utility function to convert from OGR geometry to a mapserver shape
445
 * object.
446
 **********************************************************************/
447
int msOGRGeometryToShape(OGRGeometryH hGeometry, shapeObj *psShape,
90✔
448
                         OGRwkbGeometryType nType) {
449
  if (hGeometry && psShape && nType > 0) {
90✔
450
    if (nType == wkbPoint || nType == wkbMultiPoint)
451
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_POINT);
30✔
452
    else if (nType == wkbLineString || nType == wkbMultiLineString)
453
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_LINE);
4✔
454
    else if (nType == wkbPolygon || nType == wkbMultiPolygon)
455
      return ogrConvertGeometry(hGeometry, psShape, MS_LAYER_POLYGON);
56✔
456
    else
457
      return MS_FAILURE;
458
  } else
459
    return MS_FAILURE;
460
}
461

462
/* ==================================================================
463
 * Attributes handling functions
464
 * ================================================================== */
465

466
// Special field index codes for handling text string and angle coming from
467
// OGR style strings.
468

469
#define MSOGR_FID_INDEX -99
470

471
#define MSOGR_LABELNUMITEMS 21
472
#define MSOGR_LABELFONTNAMENAME "OGR:LabelFont"
473
#define MSOGR_LABELFONTNAMEINDEX -100
474
#define MSOGR_LABELSIZENAME "OGR:LabelSize"
475
#define MSOGR_LABELSIZEINDEX -101
476
#define MSOGR_LABELTEXTNAME "OGR:LabelText"
477
#define MSOGR_LABELTEXTINDEX -102
478
#define MSOGR_LABELANGLENAME "OGR:LabelAngle"
479
#define MSOGR_LABELANGLEINDEX -103
480
#define MSOGR_LABELFCOLORNAME "OGR:LabelFColor"
481
#define MSOGR_LABELFCOLORINDEX -104
482
#define MSOGR_LABELBCOLORNAME "OGR:LabelBColor"
483
#define MSOGR_LABELBCOLORINDEX -105
484
#define MSOGR_LABELPLACEMENTNAME "OGR:LabelPlacement"
485
#define MSOGR_LABELPLACEMENTINDEX -106
486
#define MSOGR_LABELANCHORNAME "OGR:LabelAnchor"
487
#define MSOGR_LABELANCHORINDEX -107
488
#define MSOGR_LABELDXNAME "OGR:LabelDx"
489
#define MSOGR_LABELDXINDEX -108
490
#define MSOGR_LABELDYNAME "OGR:LabelDy"
491
#define MSOGR_LABELDYINDEX -109
492
#define MSOGR_LABELPERPNAME "OGR:LabelPerp"
493
#define MSOGR_LABELPERPINDEX -110
494
#define MSOGR_LABELBOLDNAME "OGR:LabelBold"
495
#define MSOGR_LABELBOLDINDEX -111
496
#define MSOGR_LABELITALICNAME "OGR:LabelItalic"
497
#define MSOGR_LABELITALICINDEX -112
498
#define MSOGR_LABELUNDERLINENAME "OGR:LabelUnderline"
499
#define MSOGR_LABELUNDERLINEINDEX -113
500
#define MSOGR_LABELPRIORITYNAME "OGR:LabelPriority"
501
#define MSOGR_LABELPRIORITYINDEX -114
502
#define MSOGR_LABELSTRIKEOUTNAME "OGR:LabelStrikeout"
503
#define MSOGR_LABELSTRIKEOUTINDEX -115
504
#define MSOGR_LABELSTRETCHNAME "OGR:LabelStretch"
505
#define MSOGR_LABELSTRETCHINDEX -116
506
#define MSOGR_LABELADJHORNAME "OGR:LabelAdjHor"
507
#define MSOGR_LABELADJHORINDEX -117
508
#define MSOGR_LABELADJVERTNAME "OGR:LabelAdjVert"
509
#define MSOGR_LABELADJVERTINDEX -118
510
#define MSOGR_LABELHCOLORNAME "OGR:LabelHColor"
511
#define MSOGR_LABELHCOLORINDEX -119
512
#define MSOGR_LABELOCOLORNAME "OGR:LabelOColor"
513
#define MSOGR_LABELOCOLORINDEX -120
514
// Special codes for the OGR style parameters
515
#define MSOGR_LABELPARAMNAME "OGR:LabelParam"
516
#define MSOGR_LABELPARAMNAMELEN 14
517
#define MSOGR_LABELPARAMINDEX -500
518
#define MSOGR_BRUSHPARAMNAME "OGR:BrushParam"
519
#define MSOGR_BRUSHPARAMNAMELEN 14
520
#define MSOGR_BRUSHPARAMINDEX -600
521
#define MSOGR_PENPARAMNAME "OGR:PenParam"
522
#define MSOGR_PENPARAMNAMELEN 12
523
#define MSOGR_PENPARAMINDEX -700
524
#define MSOGR_SYMBOLPARAMNAME "OGR:SymbolParam"
525
#define MSOGR_SYMBOLPARAMNAMELEN 15
526
#define MSOGR_SYMBOLPARAMINDEX -800
527

528
/**********************************************************************
529
 *                     msOGRGetValues()
530
 *
531
 * Load selected item (i.e. field) values into a char array
532
 *
533
 * Some special attribute names are used to return some OGRFeature params
534
 * like for instance stuff encoded in the OGRStyleString.
535
 * For now the following pseudo-attribute names are supported:
536
 *  "OGR:TextString"  OGRFeatureStyle's text string if present
537
 *  "OGR:TextAngle"   OGRFeatureStyle's text angle, or 0 if not set
538
 **********************************************************************/
539
static char **msOGRGetValues(layerObj *layer, OGRFeatureH hFeature) {
9,868✔
540
  char **values;
541
  const char *pszValue = NULL;
542
  int i;
543

544
  if (layer->numitems == 0)
9,868✔
545
    return (NULL);
546

547
  if (!layer->iteminfo) // Should not happen... but just in case!
9,868✔
548
    if (msOGRLayerInitItemInfo(layer) != MS_SUCCESS)
×
549
      return NULL;
550

551
  if ((values = (char **)malloc(sizeof(char *) * layer->numitems)) == NULL) {
9,868✔
552
    msSetError(MS_MEMERR, NULL, "msOGRGetValues()");
×
553
    return (NULL);
×
554
  }
555

556
  OGRStyleMgrH hStyleMgr = NULL;
557
  OGRStyleToolH hLabelStyle = NULL;
558
  OGRStyleToolH hPenStyle = NULL;
559
  OGRStyleToolH hBrushStyle = NULL;
560
  OGRStyleToolH hSymbolStyle = NULL;
561

562
  int *itemindexes = (int *)layer->iteminfo;
9,868✔
563

564
  int nYear;
565
  int nMonth;
566
  int nDay;
567
  int nHour;
568
  int nMinute;
569
  int nSecond;
570
  int nTZFlag;
571

572
  for (i = 0; i < layer->numitems; i++) {
67,298✔
573
    if (itemindexes[i] >= 0) {
57,430✔
574
      // Extract regular attributes
575
      const char *pszValue = OGR_F_GetFieldAsString(hFeature, itemindexes[i]);
57,404✔
576
      if (pszValue[0] == 0) {
57,404✔
577
        values[i] = msStrdup("");
5,520✔
578
      } else {
579
        OGRFieldDefnH hFieldDefnRef =
580
            OGR_F_GetFieldDefnRef(hFeature, itemindexes[i]);
51,884✔
581
        switch (OGR_Fld_GetType(hFieldDefnRef)) {
51,884✔
582
        case OFTTime:
4✔
583
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
4✔
584
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
585
          switch (nTZFlag) {
4✔
586
          case 0: // Unknown time zone
4✔
587
          case 1: // Local time zone (not specified)
588
            values[i] =
8✔
589
                msStrdup(CPLSPrintf("%02d:%02d:%02d", nHour, nMinute, nSecond));
4✔
590
            break;
4✔
591
          case 100: // GMT
×
592
            values[i] = msStrdup(
×
593
                CPLSPrintf("%02d:%02d:%02dZ", nHour, nMinute, nSecond));
594
            break;
×
595
          default: // Offset (in quarter-hour units) from GMT
×
596
            const int TZOffset = std::abs(nTZFlag - 100) * 15;
×
597
            const int TZHour = TZOffset / 60;
×
598
            const int TZMinute = TZOffset % 60;
×
599
            const char TZSign = (nTZFlag > 100) ? '+' : '-';
×
600
            values[i] =
×
601
                msStrdup(CPLSPrintf("%02d:%02d:%02d%c%02d:%02d", nHour, nMinute,
×
602
                                    nSecond, TZSign, TZHour, TZMinute));
603
          }
604
          break;
605
        case OFTDate:
12✔
606
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
12✔
607
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
608
          values[i] =
24✔
609
              msStrdup(CPLSPrintf("%04d-%02d-%02d", nYear, nMonth, nDay));
12✔
610
          break;
12✔
611
        case OFTDateTime:
24✔
612
          OGR_F_GetFieldAsDateTime(hFeature, itemindexes[i], &nYear, &nMonth,
24✔
613
                                   &nDay, &nHour, &nMinute, &nSecond, &nTZFlag);
614
          switch (nTZFlag) {
24✔
615
          case 0: // Unknown time zone
8✔
616
          case 1: // Local time zone (not specified)
617
            values[i] =
16✔
618
                msStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d", nYear,
8✔
619
                                    nMonth, nDay, nHour, nMinute, nSecond));
620
            break;
8✔
621
          case 100: // GMT
12✔
622
            values[i] =
24✔
623
                msStrdup(CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ", nYear,
12✔
624
                                    nMonth, nDay, nHour, nMinute, nSecond));
625
            break;
12✔
626
          default: // Offset (in quarter-hour units) from GMT
4✔
627
            const int TZOffset = std::abs(nTZFlag - 100) * 15;
4✔
628
            const int TZHour = TZOffset / 60;
4✔
629
            const int TZMinute = TZOffset % 60;
4✔
630
            const char TZSign = (nTZFlag > 100) ? '+' : '-';
4✔
631
            values[i] = msStrdup(CPLSPrintf(
4✔
632
                "%04d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", nYear, nMonth, nDay,
633
                nHour, nMinute, nSecond, TZSign, TZHour, TZMinute));
634
          }
635
          break;
636
        default:
51,844✔
637
          values[i] = msStrdup(pszValue);
51,844✔
638
          break;
51,844✔
639
        }
640
      }
641
    } else if (itemindexes[i] == MSOGR_FID_INDEX) {
26✔
642
      values[i] =
26✔
643
          msStrdup(CPLSPrintf(CPL_FRMT_GIB, (GIntBig)OGR_F_GetFID(hFeature)));
26✔
644
    } else {
645
      // Handle special OGR attributes coming from StyleString
646
      if (!hStyleMgr) {
×
647
        hStyleMgr = OGR_SM_Create(NULL);
×
648
        OGR_SM_InitFromFeature(hStyleMgr, hFeature);
×
649
        int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
×
650
        for (int i = 0; i < numParts; i++) {
×
651
          OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
×
652
          if (hStylePart) {
×
653
            if (OGR_ST_GetType(hStylePart) == OGRSTCLabel && !hLabelStyle)
×
654
              hLabelStyle = hStylePart;
655
            else if (OGR_ST_GetType(hStylePart) == OGRSTCPen && !hPenStyle)
×
656
              hPenStyle = hStylePart;
657
            else if (OGR_ST_GetType(hStylePart) == OGRSTCBrush && !hBrushStyle)
×
658
              hBrushStyle = hStylePart;
659
            else if (OGR_ST_GetType(hStylePart) == OGRSTCSymbol &&
×
660
                     !hSymbolStyle)
661
              hSymbolStyle = hStylePart;
662
            else {
663
              OGR_ST_Destroy(hStylePart);
×
664
              hStylePart = NULL;
665
            }
666
          }
667
          /* Setting up the size units according to msOGRLayerGetAutoStyle*/
668
          if (hStylePart && layer->map)
×
669
            OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
×
670
                           layer->map->cellsize * layer->map->resolution /
×
671
                               layer->map->defresolution * 72.0 * 39.37);
×
672
        }
673
      }
674
      int bDefault;
675
      if (itemindexes[i] == MSOGR_LABELTEXTINDEX) {
×
676
        if (hLabelStyle == NULL ||
×
677
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelTextString,
×
678
                                            &bDefault)) == NULL))
679
          values[i] = msStrdup("");
×
680
        else
681
          values[i] = msStrdup(pszValue);
×
682

683
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
684
          msDebug(MSOGR_LABELTEXTNAME " = \"%s\"\n", values[i]);
×
685
      } else if (itemindexes[i] == MSOGR_LABELANGLEINDEX) {
686
        if (hLabelStyle == NULL ||
×
687
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAngle,
×
688
                                            &bDefault)) == NULL))
689
          values[i] = msStrdup("0");
×
690
        else
691
          values[i] = msStrdup(pszValue);
×
692

693
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
694
          msDebug(MSOGR_LABELANGLENAME " = \"%s\"\n", values[i]);
×
695
      } else if (itemindexes[i] == MSOGR_LABELSIZEINDEX) {
696
        if (hLabelStyle == NULL ||
×
697
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelSize,
×
698
                                            &bDefault)) == NULL))
699
          values[i] = msStrdup("0");
×
700
        else
701
          values[i] = msStrdup(pszValue);
×
702

703
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
704
          msDebug(MSOGR_LABELSIZENAME " = \"%s\"\n", values[i]);
×
705
      } else if (itemindexes[i] == MSOGR_LABELFCOLORINDEX) {
706
        if (hLabelStyle == NULL ||
×
707
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFColor,
×
708
                                            &bDefault)) == NULL))
709
          values[i] = msStrdup("#000000");
×
710
        else
711
          values[i] = msStrdup(pszValue);
×
712

713
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
714
          msDebug(MSOGR_LABELFCOLORNAME " = \"%s\"\n", values[i]);
×
715
      } else if (itemindexes[i] == MSOGR_LABELFONTNAMEINDEX) {
716
        if (hLabelStyle == NULL ||
×
717
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFontName,
×
718
                                            &bDefault)) == NULL))
719
          values[i] = msStrdup("Arial");
×
720
        else
721
          values[i] = msStrdup(pszValue);
×
722

723
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
724
          msDebug(MSOGR_LABELFONTNAMENAME " =       \"%s\"\n", values[i]);
×
725
      } else if (itemindexes[i] == MSOGR_LABELBCOLORINDEX) {
726
        if (hLabelStyle == NULL ||
×
727
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelBColor,
×
728
                                            &bDefault)) == NULL))
729
          values[i] = msStrdup("#000000");
×
730
        else
731
          values[i] = msStrdup(pszValue);
×
732

733
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
734
          msDebug(MSOGR_LABELBCOLORNAME " = \"%s\"\n", values[i]);
×
735
      } else if (itemindexes[i] == MSOGR_LABELPLACEMENTINDEX) {
736
        if (hLabelStyle == NULL ||
×
737
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPlacement,
×
738
                                            &bDefault)) == NULL))
739
          values[i] = msStrdup("");
×
740
        else
741
          values[i] = msStrdup(pszValue);
×
742

743
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
744
          msDebug(MSOGR_LABELPLACEMENTNAME " = \"%s\"\n", values[i]);
×
745
      } else if (itemindexes[i] == MSOGR_LABELANCHORINDEX) {
746
        if (hLabelStyle == NULL ||
×
747
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAnchor,
×
748
                                            &bDefault)) == NULL))
749
          values[i] = msStrdup("0");
×
750
        else
751
          values[i] = msStrdup(pszValue);
×
752

753
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
754
          msDebug(MSOGR_LABELANCHORNAME " = \"%s\"\n", values[i]);
×
755
      } else if (itemindexes[i] == MSOGR_LABELDXINDEX) {
756
        if (hLabelStyle == NULL ||
×
757
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelDx,
×
758
                                            &bDefault)) == NULL))
759
          values[i] = msStrdup("0");
×
760
        else
761
          values[i] = msStrdup(pszValue);
×
762

763
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
764
          msDebug(MSOGR_LABELDXNAME " = \"%s\"\n", values[i]);
×
765
      } else if (itemindexes[i] == MSOGR_LABELDYINDEX) {
766
        if (hLabelStyle == NULL ||
×
767
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelDy,
×
768
                                            &bDefault)) == NULL))
769
          values[i] = msStrdup("0");
×
770
        else
771
          values[i] = msStrdup(pszValue);
×
772

773
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
774
          msDebug(MSOGR_LABELDYNAME " = \"%s\"\n", values[i]);
×
775
      } else if (itemindexes[i] == MSOGR_LABELPERPINDEX) {
776
        if (hLabelStyle == NULL ||
×
777
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPerp,
×
778
                                            &bDefault)) == NULL))
779
          values[i] = msStrdup("0");
×
780
        else
781
          values[i] = msStrdup(pszValue);
×
782

783
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
784
          msDebug(MSOGR_LABELPERPNAME " = \"%s\"\n", values[i]);
×
785
      } else if (itemindexes[i] == MSOGR_LABELBOLDINDEX) {
786
        if (hLabelStyle == NULL ||
×
787
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelBold,
×
788
                                            &bDefault)) == NULL))
789
          values[i] = msStrdup("0");
×
790
        else
791
          values[i] = msStrdup(pszValue);
×
792

793
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
794
          msDebug(MSOGR_LABELBOLDNAME " = \"%s\"\n", values[i]);
×
795
      } else if (itemindexes[i] == MSOGR_LABELITALICINDEX) {
796
        if (hLabelStyle == NULL ||
×
797
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelItalic,
×
798
                                            &bDefault)) == NULL))
799
          values[i] = msStrdup("0");
×
800
        else
801
          values[i] = msStrdup(pszValue);
×
802

803
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
804
          msDebug(MSOGR_LABELITALICNAME " = \"%s\"\n", values[i]);
×
805
      } else if (itemindexes[i] == MSOGR_LABELUNDERLINEINDEX) {
806
        if (hLabelStyle == NULL ||
×
807
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelUnderline,
×
808
                                            &bDefault)) == NULL))
809
          values[i] = msStrdup("0");
×
810
        else
811
          values[i] = msStrdup(pszValue);
×
812

813
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
814
          msDebug(MSOGR_LABELUNDERLINENAME " = \"%s\"\n", values[i]);
×
815
      } else if (itemindexes[i] == MSOGR_LABELPRIORITYINDEX) {
816
        if (hLabelStyle == NULL ||
×
817
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelPriority,
×
818
                                            &bDefault)) == NULL))
819
          values[i] = msStrdup("0");
×
820
        else
821
          values[i] = msStrdup(pszValue);
×
822

823
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
824
          msDebug(MSOGR_LABELPRIORITYNAME " = \"%s\"\n", values[i]);
×
825
      } else if (itemindexes[i] == MSOGR_LABELSTRIKEOUTINDEX) {
826
        if (hLabelStyle == NULL ||
×
827
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelStrikeout,
×
828
                                            &bDefault)) == NULL))
829
          values[i] = msStrdup("0");
×
830
        else
831
          values[i] = msStrdup(pszValue);
×
832

833
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
834
          msDebug(MSOGR_LABELSTRIKEOUTNAME " = \"%s\"\n", values[i]);
×
835
      } else if (itemindexes[i] == MSOGR_LABELSTRETCHINDEX) {
836
        if (hLabelStyle == NULL ||
×
837
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelStretch,
×
838
                                            &bDefault)) == NULL))
839
          values[i] = msStrdup("0");
×
840
        else
841
          values[i] = msStrdup(pszValue);
×
842

843
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
844
          msDebug(MSOGR_LABELSTRETCHNAME " = \"%s\"\n", values[i]);
×
845
      } else if (itemindexes[i] == MSOGR_LABELADJHORINDEX) {
846
        if (hLabelStyle == NULL ||
×
847
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAdjHor,
×
848
                                            &bDefault)) == NULL))
849
          values[i] = msStrdup("");
×
850
        else
851
          values[i] = msStrdup(pszValue);
×
852

853
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
854
          msDebug(MSOGR_LABELADJHORNAME " = \"%s\"\n", values[i]);
×
855
      } else if (itemindexes[i] == MSOGR_LABELADJVERTINDEX) {
856
        if (hLabelStyle == NULL ||
×
857
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelAdjVert,
×
858
                                            &bDefault)) == NULL))
859
          values[i] = msStrdup("");
×
860
        else
861
          values[i] = msStrdup(pszValue);
×
862

863
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
864
          msDebug(MSOGR_LABELADJVERTNAME " = \"%s\"\n", values[i]);
×
865
      } else if (itemindexes[i] == MSOGR_LABELHCOLORINDEX) {
866
        if (hLabelStyle == NULL ||
×
867
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelHColor,
×
868
                                            &bDefault)) == NULL))
869
          values[i] = msStrdup("");
×
870
        else
871
          values[i] = msStrdup(pszValue);
×
872

873
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
874
          msDebug(MSOGR_LABELHCOLORNAME " = \"%s\"\n", values[i]);
×
875
      } else if (itemindexes[i] == MSOGR_LABELOCOLORINDEX) {
876
        if (hLabelStyle == NULL ||
×
877
            ((pszValue = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelOColor,
×
878
                                            &bDefault)) == NULL))
879
          values[i] = msStrdup("");
×
880
        else
881
          values[i] = msStrdup(pszValue);
×
882

883
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
884
          msDebug(MSOGR_LABELOCOLORNAME " = \"%s\"\n", values[i]);
×
885
      } else if (itemindexes[i] >= MSOGR_LABELPARAMINDEX) {
×
886
        if (hLabelStyle == NULL ||
×
887
            ((pszValue = OGR_ST_GetParamStr(
×
888
                  hLabelStyle, itemindexes[i] - MSOGR_LABELPARAMINDEX,
889
                  &bDefault)) == NULL))
890
          values[i] = msStrdup("");
×
891
        else
892
          values[i] = msStrdup(pszValue);
×
893

894
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
895
          msDebug(MSOGR_LABELPARAMNAME " = \"%s\"\n", values[i]);
×
896
      } else if (itemindexes[i] >= MSOGR_BRUSHPARAMINDEX) {
×
897
        if (hBrushStyle == NULL ||
×
898
            ((pszValue = OGR_ST_GetParamStr(
×
899
                  hBrushStyle, itemindexes[i] - MSOGR_BRUSHPARAMINDEX,
900
                  &bDefault)) == NULL))
901
          values[i] = msStrdup("");
×
902
        else
903
          values[i] = msStrdup(pszValue);
×
904

905
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
906
          msDebug(MSOGR_BRUSHPARAMNAME " = \"%s\"\n", values[i]);
×
907
      } else if (itemindexes[i] >= MSOGR_PENPARAMINDEX) {
×
908
        if (hPenStyle == NULL ||
×
909
            ((pszValue = OGR_ST_GetParamStr(
×
910
                  hPenStyle, itemindexes[i] - MSOGR_PENPARAMINDEX,
911
                  &bDefault)) == NULL))
912
          values[i] = msStrdup("");
×
913
        else
914
          values[i] = msStrdup(pszValue);
×
915

916
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
917
          msDebug(MSOGR_PENPARAMNAME " = \"%s\"\n", values[i]);
×
918
      } else if (itemindexes[i] >= MSOGR_SYMBOLPARAMINDEX) {
×
919
        if (hSymbolStyle == NULL ||
×
920
            ((pszValue = OGR_ST_GetParamStr(
×
921
                  hSymbolStyle, itemindexes[i] - MSOGR_SYMBOLPARAMINDEX,
922
                  &bDefault)) == NULL))
923
          values[i] = msStrdup("");
×
924
        else
925
          values[i] = msStrdup(pszValue);
×
926

927
        if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
928
          msDebug(MSOGR_SYMBOLPARAMNAME " = \"%s\"\n", values[i]);
×
929
      } else {
930
        msFreeCharArray(values, i);
×
931

932
        OGR_SM_Destroy(hStyleMgr);
×
933
        OGR_ST_Destroy(hLabelStyle);
×
934
        OGR_ST_Destroy(hPenStyle);
×
935
        OGR_ST_Destroy(hBrushStyle);
×
936
        OGR_ST_Destroy(hSymbolStyle);
×
937

938
        msSetError(MS_OGRERR, "Invalid field index!?!", "msOGRGetValues()");
×
939
        return (NULL);
×
940
      }
941
    }
942
  }
943

944
  OGR_SM_Destroy(hStyleMgr);
9,868✔
945
  OGR_ST_Destroy(hLabelStyle);
9,868✔
946
  OGR_ST_Destroy(hPenStyle);
9,868✔
947
  OGR_ST_Destroy(hBrushStyle);
9,868✔
948
  OGR_ST_Destroy(hSymbolStyle);
9,868✔
949

950
  return (values);
951
}
952

953
/**********************************************************************
954
 *                     msOGRSpatialRef2ProjectionObj()
955
 *
956
 * Init a MapServer projectionObj using an OGRSpatialRef
957
 * Works only with PROJECTION AUTO
958
 *
959
 * Returns MS_SUCCESS/MS_FAILURE
960
 **********************************************************************/
961
static int msOGRSpatialRef2ProjectionObj(OGRSpatialReferenceH hSRS,
50✔
962
                                         projectionObj *proj, int debug_flag) {
963
  // First flush the "auto" name from the projargs[]...
964
  msFreeProjectionExceptContext(proj);
50✔
965

966
  if (hSRS == NULL || OSRIsLocal(hSRS)) {
50✔
967
    // Dataset had no set projection or is NonEarth (LOCAL_CS)...
968
    // Nothing else to do. Leave proj empty and no reprojection will happen!
969
    return MS_SUCCESS;
×
970
  }
971

972
  // This helps avoiding going through potentially lossy PROJ4 strings
973
  const char *pszAuthName = OSRGetAuthorityName(hSRS, NULL);
50✔
974
  if (pszAuthName && EQUAL(pszAuthName, "EPSG")) {
50✔
975
    const char *pszAuthCode = OSRGetAuthorityCode(hSRS, NULL);
44✔
976
    if (pszAuthCode) {
44✔
977
      char szInitStr[32];
978
      snprintf(szInitStr, sizeof(szInitStr), "init=epsg:%d", atoi(pszAuthCode));
979

980
      if (debug_flag)
44✔
981
        msDebug("AUTO = %s\n", szInitStr);
×
982

983
      return msLoadProjectionString(proj, szInitStr) == 0 ? MS_SUCCESS
44✔
984
                                                          : MS_FAILURE;
44✔
985
    }
986
  }
987

988
  // Export OGR SRS to a PROJ4 string
989
  char *pszProj = NULL;
6✔
990

991
  if (OSRExportToProj4(hSRS, &pszProj) != OGRERR_NONE || pszProj == NULL ||
6✔
992
      strlen(pszProj) == 0) {
6✔
993
    msSetError(MS_OGRERR, "Conversion from OGR SRS to PROJ4 failed.",
×
994
               "msOGRSpatialRef2ProjectionObj()");
995
    CPLFree(pszProj);
×
996
    return (MS_FAILURE);
×
997
  }
998

999
  if (debug_flag)
6✔
1000
    msDebug("AUTO = %s\n", pszProj);
×
1001

1002
  if (msLoadProjectionString(proj, pszProj) != 0)
6✔
1003
    return MS_FAILURE;
1004

1005
  CPLFree(pszProj);
6✔
1006

1007
  return MS_SUCCESS;
1008
}
1009

1010
/**********************************************************************
1011
 *                     msOGCWKT2ProjectionObj()
1012
 *
1013
 * Init a MapServer projectionObj using an OGC WKT definition.
1014
 * Works only with PROJECTION AUTO
1015
 *
1016
 * Returns MS_SUCCESS/MS_FAILURE
1017
 **********************************************************************/
1018

1019
int msOGCWKT2ProjectionObj(const char *pszWKT, projectionObj *proj,
50✔
1020
                           int debug_flag)
1021

1022
{
1023
  OGRSpatialReferenceH hSRS;
1024
  char *pszAltWKT = (char *)pszWKT;
50✔
1025
  OGRErr eErr;
1026
  int ms_result;
1027

1028
  hSRS = OSRNewSpatialReference(NULL);
50✔
1029

1030
  if (!EQUALN(pszWKT, "GEOGCS", 6) && !EQUALN(pszWKT, "PROJCS", 6) &&
50✔
1031
      !EQUALN(pszWKT, "LOCAL_CS", 8))
40✔
1032
    eErr = OSRSetFromUserInput(hSRS, pszWKT);
40✔
1033
  else
1034
    eErr = OSRImportFromWkt(hSRS, &pszAltWKT);
10✔
1035

1036
  if (eErr != OGRERR_NONE) {
50✔
1037
    OSRDestroySpatialReference(hSRS);
1✔
1038
    msSetError(MS_OGRERR, "Ingestion of WKT string '%s' failed.",
1✔
1039
               "msOGCWKT2ProjectionObj()", pszWKT);
1040
    return MS_FAILURE;
1✔
1041
  }
1042

1043
  ms_result = msOGRSpatialRef2ProjectionObj(hSRS, proj, debug_flag);
49✔
1044

1045
  OSRDestroySpatialReference(hSRS);
49✔
1046
  return ms_result;
1047
}
1048

1049
/**********************************************************************
1050
 *                     msOGRFileOpen()
1051
 *
1052
 * Open an OGR connection, and initialize a msOGRFileInfo.
1053
 **********************************************************************/
1054

1055
static int bOGRDriversRegistered = MS_FALSE;
1056

1057
void msOGRInitialize(void)
1,355✔
1058

1059
{
1060
  /* ------------------------------------------------------------------
1061
   * Register OGR Drivers, only once per execution
1062
   * ------------------------------------------------------------------ */
1063
  if (!bOGRDriversRegistered) {
1,355✔
1064
    ACQUIRE_OGR_LOCK;
369✔
1065

1066
    OGRRegisterAll();
369✔
1067
    CPLPushErrorHandler(CPLQuietErrorHandler);
369✔
1068

1069
    /* ------------------------------------------------------------------
1070
     * Pass config option GML_FIELDTYPES=ALWAYS_STRING to OGR so that all
1071
     * GML attributes are returned as strings to MapServer. This is most
1072
     * efficient and prevents problems with autodetection of some attribute
1073
     * types.
1074
     * ------------------------------------------------------------------ */
1075
    CPLSetConfigOption("GML_FIELDTYPES", "ALWAYS_STRING");
369✔
1076

1077
    bOGRDriversRegistered = MS_TRUE;
369✔
1078

1079
    RELEASE_OGR_LOCK;
369✔
1080
  }
1081
}
1,355✔
1082

1083
/* ==================================================================
1084
 * The following functions closely relate to the API called from
1085
 * maplayer.c, but are intended to be used for the tileindex or direct
1086
 * layer access.
1087
 * ================================================================== */
1088

1089
static void msOGRFileOpenSpatialite(layerObj *layer, const char *pszLayerDef,
1090
                                    msOGRFileInfo *psInfo);
1091

1092
/**********************************************************************
1093
 *                     msOGRFileOpen()
1094
 *
1095
 * Open an OGR connection, and initialize a msOGRFileInfo.
1096
 **********************************************************************/
1097

1098
static msOGRFileInfo *msOGRFileOpen(layerObj *layer, const char *connection)
920✔
1099

1100
{
1101
  char *conn_decrypted = NULL;
1102

1103
  msOGRInitialize();
920✔
1104

1105
  /* ------------------------------------------------------------------
1106
   * Make sure any encrypted token in the connection string are decrypted
1107
   * ------------------------------------------------------------------ */
1108
  if (connection) {
920✔
1109
    conn_decrypted = msDecryptStringTokens(layer->map, connection);
920✔
1110
    if (conn_decrypted == NULL)
920✔
1111
      return NULL; /* An error should already have been reported */
1112
  }
1113

1114
  /* ------------------------------------------------------------------
1115
   * Parse connection string into dataset name, and layer name.
1116
   * ------------------------------------------------------------------ */
1117
  char *pszDSName = NULL, *pszLayerDef = NULL;
1118

1119
  if (conn_decrypted == NULL) {
1120
    /* we don't have anything */
1121
  } else if (layer->data != NULL) {
920✔
1122
    pszDSName = CPLStrdup(conn_decrypted);
390✔
1123
    pszLayerDef = CPLStrdup(layer->data);
390✔
1124
  } else {
1125
    char **papszTokens = NULL;
1126

1127
    papszTokens = CSLTokenizeStringComplex(conn_decrypted, ",", TRUE, FALSE);
530✔
1128

1129
    if (CSLCount(papszTokens) > 0)
530✔
1130
      pszDSName = CPLStrdup(papszTokens[0]);
530✔
1131
    if (CSLCount(papszTokens) > 1)
530✔
1132
      pszLayerDef = CPLStrdup(papszTokens[1]);
11✔
1133

1134
    CSLDestroy(papszTokens);
530✔
1135
  }
1136

1137
  /* Get rid of decrypted connection string. We'll use the original (not
1138
   * decrypted) string for debug and error messages in the rest of the code.
1139
   */
1140
  msFree(conn_decrypted);
920✔
1141
  conn_decrypted = NULL;
1142

1143
  if (pszDSName == NULL) {
920✔
1144
    msSetError(MS_OGRERR,
×
1145
               "Error parsing OGR connection information in layer `%s'",
1146
               "msOGRFileOpen()", layer->name ? layer->name : "(null)");
×
1147
    return NULL;
×
1148
  }
1149

1150
  if (pszLayerDef == NULL)
920✔
1151
    pszLayerDef = CPLStrdup("0");
519✔
1152

1153
  /* -------------------------------------------------------------------- */
1154
  /*      Can we get an existing connection for this layer?               */
1155
  /* -------------------------------------------------------------------- */
1156
  OGRDataSourceH hDS;
1157

1158
  hDS = (OGRDataSourceH)msConnPoolRequest(layer);
920✔
1159

1160
  /* -------------------------------------------------------------------- */
1161
  /*      If not, open now, and register this connection with the         */
1162
  /*      pool.                                                           */
1163
  /* -------------------------------------------------------------------- */
1164
  if (hDS == NULL) {
920✔
1165
    char szPath[MS_MAXPATHLEN] = "";
897✔
1166
    const char *pszDSSelectedName = pszDSName;
1167

1168
    if (layer->debug >= MS_DEBUGLEVEL_V) {
897✔
1169
      msDebug("msOGRFileOpen(%s)...\n", connection);
3✔
1170
    }
1171

1172
    CPLErrorReset();
897✔
1173
    if (msTryBuildPath3(szPath, layer->map->mappath, layer->map->shapepath,
897✔
1174
                        pszDSName) != NULL ||
1,125✔
1175
        msTryBuildPath(szPath, layer->map->mappath, pszDSName) != NULL) {
228✔
1176
      /* Use relative path */
1177
      pszDSSelectedName = szPath;
1178
    }
1179

1180
    if (layer->debug >= MS_DEBUGLEVEL_V) {
897✔
1181
      msDebug("GDALOpenEx(%s)\n", pszDSSelectedName);
3✔
1182
    }
1183

1184
    ACQUIRE_OGR_LOCK;
897✔
1185
    char **connectionoptions =
1186
        msGetStringListFromHashTable(&(layer->connectionoptions));
897✔
1187
    hDS = (OGRDataSourceH)GDALOpenEx(pszDSSelectedName, GDAL_OF_VECTOR, NULL,
897✔
1188
                                     (const char *const *)connectionoptions,
1189
                                     NULL);
1190
    CSLDestroy(connectionoptions);
897✔
1191
    RELEASE_OGR_LOCK;
897✔
1192

1193
    if (hDS == NULL) {
897✔
1194
      msSetError(MS_OGRERR,
×
1195
                 "Open failed for OGR connection in layer `%s'.  "
1196
                 "File not found or unsupported format. Check server logs.",
1197
                 "msOGRFileOpen()", layer->name ? layer->name : "(null)");
×
1198
      if (strlen(CPLGetLastErrorMsg()) == 0)
×
1199
        msDebug("Open failed for OGR connection in layer `%s'.\n%s\n",
×
1200
                layer->name ? layer->name : "(null)", CPLGetLastErrorMsg());
×
1201
      CPLFree(pszDSName);
×
1202
      CPLFree(pszLayerDef);
×
1203
      return NULL;
×
1204
    }
1205

1206
    msConnPoolRegister(layer, hDS, msOGRCloseConnection);
897✔
1207
  }
1208

1209
  CPLFree(pszDSName);
920✔
1210
  pszDSName = NULL;
1211

1212
  /* ------------------------------------------------------------------
1213
   * Find the layer selected.
1214
   * ------------------------------------------------------------------ */
1215

1216
  int nLayerIndex = 0;
1217
  OGRLayerH hLayer = NULL;
1218

1219
  int iLayer;
1220

1221
  if (EQUALN(pszLayerDef, "SELECT ", 7)) {
920✔
1222
    ACQUIRE_OGR_LOCK;
21✔
1223
    hLayer = OGR_DS_ExecuteSQL(hDS, pszLayerDef, NULL, NULL);
21✔
1224
    if (hLayer == NULL) {
21✔
1225
      msSetError(MS_OGRERR, "ExecuteSQL() failed. Check server logs.",
×
1226
                 "msOGRFileOpen()");
1227
      if (strlen(CPLGetLastErrorMsg()) == 0)
×
1228
        msDebug("ExecuteSQL(%s) failed.\n%s\n", pszLayerDef,
×
1229
                CPLGetLastErrorMsg());
1230
      RELEASE_OGR_LOCK;
×
1231
      msConnPoolRelease(layer, hDS);
×
1232
      CPLFree(pszLayerDef);
×
1233
      return NULL;
×
1234
    }
1235
    RELEASE_OGR_LOCK;
21✔
1236
    nLayerIndex = -1;
1237
  }
1238

1239
  for (iLayer = 0; hLayer == NULL && iLayer < OGR_DS_GetLayerCount(hDS);
1,483✔
1240
       iLayer++) {
1241
    hLayer = OGR_DS_GetLayer(hDS, iLayer);
932✔
1242
    if (hLayer != NULL && EQUAL(OGR_L_GetName(hLayer), pszLayerDef)) {
932✔
1243
      nLayerIndex = iLayer;
1244
      break;
1245
    } else
1246
      hLayer = NULL;
1247
  }
1248

1249
  if (hLayer == NULL && (atoi(pszLayerDef) > 0 || EQUAL(pszLayerDef, "0"))) {
1,450✔
1250
    nLayerIndex = atoi(pszLayerDef);
1251
    if (nLayerIndex < OGR_DS_GetLayerCount(hDS))
530✔
1252
      hLayer = OGR_DS_GetLayer(hDS, nLayerIndex);
530✔
1253
  }
1254

1255
  if (hLayer == NULL) {
530✔
1256
    msSetError(MS_OGRERR, "GetLayer(%s) failed for OGR connection. Check logs.",
×
1257
               "msOGRFileOpen()", pszLayerDef);
1258
    msDebug("GetLayer(%s) failed for OGR connection `%s'.\n", pszLayerDef,
×
1259
            connection);
1260
    CPLFree(pszLayerDef);
×
1261
    msConnPoolRelease(layer, hDS);
×
1262
    return NULL;
×
1263
  }
1264

1265
  /* ------------------------------------------------------------------
1266
   * OK... open succeeded... alloc and fill msOGRFileInfo inside layer obj
1267
   * ------------------------------------------------------------------ */
1268
  msOGRFileInfo *psInfo = (msOGRFileInfo *)CPLCalloc(1, sizeof(msOGRFileInfo));
920✔
1269

1270
  psInfo->pszFname = CPLStrdup(OGR_DS_GetName(hDS));
920✔
1271
  psInfo->pszLayerDef = pszLayerDef;
920✔
1272
  psInfo->nLayerIndex = nLayerIndex;
920✔
1273
  psInfo->hDS = hDS;
920✔
1274
  psInfo->hLayer = hLayer;
920✔
1275

1276
  psInfo->nTileId = 0;
920✔
1277
  msInitProjection(&(psInfo->sTileProj));
920✔
1278
  msProjectionInheritContextFrom(&(psInfo->sTileProj), &(layer->projection));
920✔
1279
  psInfo->poCurTile = NULL;
920✔
1280
  psInfo->rect_is_defined = false;
920✔
1281
  psInfo->rect.minx = psInfo->rect.maxx = 0;
920✔
1282
  psInfo->rect.miny = psInfo->rect.maxy = 0;
920✔
1283
  psInfo->last_record_index_read = -1;
920✔
1284
  psInfo->dialect = NULL;
920✔
1285

1286
  psInfo->pszSelect = NULL;
920✔
1287
  psInfo->pszSpatialFilterTableName = NULL;
920✔
1288
  psInfo->pszSpatialFilterGeometryColumn = NULL;
920✔
1289
  psInfo->pszMainTableName = NULL;
920✔
1290
  psInfo->pszRowId = NULL;
920✔
1291
  psInfo->bIsOKForSQLCompose = true;
920✔
1292
  psInfo->bPaging = false;
920✔
1293
  psInfo->bHasSpatialIndex = false;
920✔
1294
  psInfo->pszTablePrefix = NULL;
920✔
1295
  psInfo->pszWHERE = NULL;
920✔
1296

1297
  // GDAL 1.x API
1298
  OGRSFDriverH dr = OGR_DS_GetDriver(hDS);
920✔
1299
  const char *name = OGR_Dr_GetName(dr);
920✔
1300
  if (strcmp(name, "SQLite") == 0) {
920✔
1301
    bool have_spatialite = false;
1302

1303
    CPLPushErrorHandler(CPLQuietErrorHandler);
257✔
1304

1305
    // test for Spatialite support in driver
1306
    const char *test_spatialite = "SELECT spatialite_version()";
1307
    OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
257✔
1308
    if (l) {
257✔
1309
      OGR_DS_ReleaseResultSet(hDS, l);
257✔
1310
      have_spatialite = true;
1311
    }
1312

1313
    // test for Spatialite enabled db
1314
    if (have_spatialite) {
1315
      have_spatialite = false;
1316
      const char *test_sql =
1317
          "select 1 from sqlite_master where name = 'geometry_columns' and sql "
1318
          "LIKE '%spatial_index_enabled%'";
1319
      OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_sql, NULL, NULL);
257✔
1320
      if (l) {
257✔
1321
        if (OGR_L_GetFeatureCount(l, TRUE) == 1)
257✔
1322
          have_spatialite = true;
1323
        OGR_DS_ReleaseResultSet(hDS, l);
257✔
1324
      }
1325
    }
1326

1327
    CPLPopErrorHandler();
257✔
1328
    CPLErrorReset();
257✔
1329

1330
    if (have_spatialite)
257✔
1331
      psInfo->dialect = "Spatialite";
245✔
1332
    else {
1333
      if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
12✔
1334
        msDebug(
×
1335
            "msOGRFileOpen: Native SQL not available, no Spatialite support "
1336
            "and/or not a Spatialite enabled db\n");
1337
      }
1338
    }
1339
  } else if (strcmp(name, "PostgreSQL") == 0) {
663✔
1340
    psInfo->dialect = "PostgreSQL";
×
1341
    // todo: PostgreSQL not yet tested
1342

1343
  } else if (strcmp(name, "GPKG") == 0 && nLayerIndex >= 0 &&
726✔
1344
             atoi(GDALVersionInfo("VERSION_NUM")) >= 2000000) {
63✔
1345

1346
    bool has_rtree = false;
1347
    const char *test_rtree =
1348
        CPLSPrintf("SELECT 1 FROM sqlite_master WHERE name = 'rtree_%s_%s'",
63✔
1349
                   OGR_L_GetName(hLayer), OGR_L_GetGeometryColumn(hLayer));
1350
    OGRLayerH l = OGR_DS_ExecuteSQL(hDS, test_rtree, NULL, NULL);
63✔
1351
    if (l) {
63✔
1352
      if (OGR_L_GetFeatureCount(l, TRUE) == 1) {
63✔
1353
        has_rtree = true;
1354
      }
1355
      OGR_DS_ReleaseResultSet(hDS, l);
63✔
1356
    }
1357
    if (has_rtree) {
63✔
1358
      bool have_gpkg_spatialite = false;
1359

1360
      CPLPushErrorHandler(CPLQuietErrorHandler);
63✔
1361

1362
      // test for Spatialite >= 4.3 support in driver
1363
      const char *test_spatialite = "SELECT spatialite_version()";
1364
      l = OGR_DS_ExecuteSQL(hDS, test_spatialite, NULL, NULL);
63✔
1365
      if (l) {
63✔
1366
        OGRFeatureH hFeat = OGR_L_GetNextFeature(l);
63✔
1367
        if (hFeat) {
63✔
1368
          const char *pszVersion = OGR_F_GetFieldAsString(hFeat, 0);
63✔
1369
          have_gpkg_spatialite = atof(pszVersion) >= 4.3;
63✔
1370
          OGR_F_Destroy(hFeat);
63✔
1371
        }
1372
        OGR_DS_ReleaseResultSet(hDS, l);
63✔
1373
      }
1374
      CPLPopErrorHandler();
63✔
1375
      CPLErrorReset();
63✔
1376

1377
      if (have_gpkg_spatialite) {
63✔
1378
        psInfo->pszMainTableName = msStrdup(OGR_L_GetName(hLayer));
63✔
1379
        psInfo->pszTablePrefix = msStrdup(psInfo->pszMainTableName);
63✔
1380
        psInfo->pszSpatialFilterTableName = msStrdup(OGR_L_GetName(hLayer));
63✔
1381
        psInfo->pszSpatialFilterGeometryColumn =
63✔
1382
            msStrdup(OGR_L_GetGeometryColumn(hLayer));
63✔
1383
        psInfo->dialect = "GPKG";
63✔
1384
        psInfo->bPaging = true;
63✔
1385
        psInfo->bHasSpatialIndex = true;
63✔
1386
      } else if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
×
1387
        msDebug("msOGRFileOpen: Spatialite support in GPKG not enabled\n");
×
1388
      }
1389
    } else {
1390
      if (layer->debug >= MS_DEBUGLEVEL_DEBUG) {
×
1391
        msDebug("msOGRFileOpen: RTree index not available\n");
×
1392
      }
1393
    }
1394
  }
1395

1396
  if (psInfo->dialect != NULL && EQUAL(psInfo->dialect, "Spatialite"))
920✔
1397
    msOGRFileOpenSpatialite(layer, pszLayerDef, psInfo);
245✔
1398

1399
  return psInfo;
1400
}
1401

1402
/************************************************************************/
1403
/*                      msOGRFileOpenSpatialite()                       */
1404
/************************************************************************/
1405

1406
static void msOGRFileOpenSpatialite(layerObj *layer, const char *pszLayerDef,
245✔
1407
                                    msOGRFileInfo *psInfo) {
1408
  // In the case of a SQLite DB, check that we can identify the
1409
  // underlying table
1410
  if (psInfo->nLayerIndex == -1) {
245✔
1411
    psInfo->bIsOKForSQLCompose = false;
13✔
1412

1413
    const char *from = strstr(psInfo->pszLayerDef, " from ");
13✔
1414
    if (from == NULL)
13✔
1415
      from = strstr(psInfo->pszLayerDef, " FROM ");
1416
    if (from) {
13✔
1417
      const char *pszBeginningOfTable = from + strlen(" FROM ");
13✔
1418
      const char *pszIter = pszBeginningOfTable;
1419
      while (*pszIter && *pszIter != ' ')
81✔
1420
        pszIter++;
68✔
1421
      if (strchr(pszIter, ',') == NULL && strstr(pszIter, " where ") == NULL &&
13✔
1422
          strstr(pszIter, " WHERE ") == NULL &&
7✔
1423
          strstr(pszIter, " join ") == NULL &&
7✔
1424
          strstr(pszIter, " JOIN ") == NULL &&
7✔
1425
          strstr(pszIter, " order by ") == NULL &&
7✔
1426
          strstr(pszIter, " ORDER BY ") == NULL) {
1427
        psInfo->bIsOKForSQLCompose = true;
7✔
1428
        psInfo->pszMainTableName = msStrdup(pszBeginningOfTable);
7✔
1429
        psInfo->pszMainTableName[pszIter - pszBeginningOfTable] = '\0';
7✔
1430
        psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
7✔
1431
        psInfo->pszSpatialFilterGeometryColumn =
7✔
1432
            msStrdup(OGR_L_GetGeometryColumn(psInfo->hLayer));
7✔
1433

1434
        char *pszRequest = NULL;
1435
        pszRequest = msStringConcatenate(pszRequest,
7✔
1436
                                         "SELECT name FROM sqlite_master WHERE "
1437
                                         "type = 'table' AND name = lower('");
1438
        pszRequest = msStringConcatenate(pszRequest, psInfo->pszMainTableName);
7✔
1439
        pszRequest = msStringConcatenate(pszRequest, "')");
7✔
1440
        OGRLayerH hLayer =
1441
            OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
7✔
1442
        msFree(pszRequest);
7✔
1443

1444
        if (hLayer) {
7✔
1445
          OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
7✔
1446
          psInfo->bIsOKForSQLCompose = (hFeature != NULL);
7✔
1447
          if (hFeature) {
7✔
1448
            msFree(psInfo->pszMainTableName);
4✔
1449
            msFree(psInfo->pszSpatialFilterTableName);
4✔
1450
            psInfo->pszMainTableName =
4✔
1451
                msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
4✔
1452
            psInfo->pszSpatialFilterTableName =
4✔
1453
                msStrdup(psInfo->pszMainTableName);
4✔
1454
            OGR_F_Destroy(hFeature);
4✔
1455
          }
1456
          OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
7✔
1457
        }
1458
        if (psInfo->bIsOKForSQLCompose) {
7✔
1459
          psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
4✔
1460
        } else {
1461
          // Test if it is a spatial view
1462
          pszRequest = msStringConcatenate(
3✔
1463
              NULL, "SELECT f_table_name, f_geometry_column, view_rowid FROM "
1464
                    "views_geometry_columns WHERE view_name = lower('");
1465
          pszRequest =
1466
              msStringConcatenate(pszRequest, psInfo->pszMainTableName);
3✔
1467
          pszRequest = msStringConcatenate(pszRequest, "')");
3✔
1468
          CPLPushErrorHandler(CPLQuietErrorHandler);
3✔
1469
          OGRLayerH hLayer =
1470
              OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
3✔
1471
          CPLPopErrorHandler();
3✔
1472
          msFree(pszRequest);
3✔
1473

1474
          if (hLayer) {
3✔
1475
            OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
3✔
1476
            psInfo->bIsOKForSQLCompose = (hFeature != NULL);
3✔
1477
            if (hFeature) {
3✔
1478
              psInfo->pszSelect = msStrdup(psInfo->pszLayerDef);
3✔
1479
              msFree(psInfo->pszSpatialFilterTableName);
3✔
1480
              psInfo->pszSpatialFilterTableName =
3✔
1481
                  msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
3✔
1482
              CPLFree(psInfo->pszSpatialFilterGeometryColumn);
3✔
1483
              psInfo->pszSpatialFilterGeometryColumn =
3✔
1484
                  msStrdup(OGR_F_GetFieldAsString(hFeature, 1));
3✔
1485
              psInfo->pszRowId = msStrdup(OGR_F_GetFieldAsString(hFeature, 2));
3✔
1486
              OGR_F_Destroy(hFeature);
3✔
1487
            }
1488
            OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
3✔
1489
          }
1490
        }
1491
      }
1492
    }
1493
  } else {
1494
    psInfo->bIsOKForSQLCompose = false;
232✔
1495

1496
    char *pszRequest = NULL;
1497
    pszRequest = msStringConcatenate(
232✔
1498
        pszRequest,
1499
        "SELECT * FROM sqlite_master WHERE type = 'table' AND name = lower('");
1500
    pszRequest = msStringConcatenate(
232✔
1501
        pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1502
    pszRequest = msStringConcatenate(pszRequest, "')");
232✔
1503
    OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
232✔
1504
    msFree(pszRequest);
232✔
1505

1506
    if (hLayer) {
232✔
1507
      OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
232✔
1508
      psInfo->bIsOKForSQLCompose = (hFeature != NULL);
232✔
1509
      if (hFeature)
232✔
1510
        OGR_F_Destroy(hFeature);
229✔
1511
      OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
232✔
1512
    }
1513
    if (psInfo->bIsOKForSQLCompose) {
232✔
1514
      psInfo->pszMainTableName =
229✔
1515
          msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
229✔
1516
      psInfo->pszSpatialFilterTableName = msStrdup(psInfo->pszMainTableName);
229✔
1517
      psInfo->pszSpatialFilterGeometryColumn =
229✔
1518
          msStrdup(OGR_L_GetGeometryColumn(psInfo->hLayer));
229✔
1519
    } else {
1520
      // Test if it is a spatial view
1521
      pszRequest = msStringConcatenate(
3✔
1522
          NULL, "SELECT f_table_name, f_geometry_column, view_rowid FROM "
1523
                "views_geometry_columns WHERE view_name = lower('");
1524
      pszRequest = msStringConcatenate(
3✔
1525
          pszRequest, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
1526
      pszRequest = msStringConcatenate(pszRequest, "')");
3✔
1527
      CPLPushErrorHandler(CPLQuietErrorHandler);
3✔
1528
      OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
3✔
1529
      CPLPopErrorHandler();
3✔
1530
      msFree(pszRequest);
3✔
1531

1532
      if (hLayer) {
3✔
1533
        OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
3✔
1534
        psInfo->bIsOKForSQLCompose = (hFeature != NULL);
3✔
1535
        if (hFeature) {
3✔
1536
          psInfo->pszMainTableName =
3✔
1537
              msStrdup(OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
3✔
1538
          psInfo->pszSpatialFilterTableName =
3✔
1539
              msStrdup(OGR_F_GetFieldAsString(hFeature, 0));
3✔
1540
          psInfo->pszSpatialFilterGeometryColumn =
3✔
1541
              msStrdup(OGR_F_GetFieldAsString(hFeature, 1));
3✔
1542
          psInfo->pszRowId = msStrdup(OGR_F_GetFieldAsString(hFeature, 2));
3✔
1543
          OGR_F_Destroy(hFeature);
3✔
1544
        }
1545
        OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
3✔
1546
      }
1547
    }
1548
  }
1549

1550
  // in the case we cannot handle the native string, go back to the client
1551
  // side evaluation by unsetting it.
1552
  if (!psInfo->bIsOKForSQLCompose && psInfo->dialect != NULL) {
245✔
1553
    if (layer->debug >= MS_DEBUGLEVEL_VVV) {
6✔
1554
      msDebug("msOGRFileOpen(): Falling back to MapServer only evaluation\n");
×
1555
    }
1556
    psInfo->dialect = NULL;
6✔
1557
  }
1558

1559
  // Check if spatial index has been disabled (testing purposes)
1560
  if (msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX") != NULL &&
248✔
1561
      !CSLTestBoolean(msLayerGetProcessingKey(layer, "USE_SPATIAL_INDEX"))) {
3✔
1562
    if (layer->debug >= MS_DEBUGLEVEL_VVV) {
3✔
1563
      msDebug("msOGRFileOpen(): Layer %s has spatial index disabled by "
×
1564
              "processing option\n",
1565
              pszLayerDef);
1566
    }
1567
  }
1568
  // Test if spatial index is available
1569
  else if (psInfo->dialect != NULL) {
242✔
1570
    char *pszRequest = NULL;
1571
    pszRequest = msStringConcatenate(
236✔
1572
        pszRequest,
1573
        "SELECT * FROM sqlite_master WHERE type = 'table' AND name = 'idx_");
1574
    pszRequest =
1575
        msStringConcatenate(pszRequest, psInfo->pszSpatialFilterTableName);
236✔
1576
    pszRequest = msStringConcatenate(pszRequest, "_");
236✔
1577
    pszRequest = msStringConcatenate(pszRequest,
236✔
1578
                                     OGR_L_GetGeometryColumn(psInfo->hLayer));
1579
    pszRequest = msStringConcatenate(pszRequest, "'");
236✔
1580

1581
    psInfo->bHasSpatialIndex = false;
236✔
1582
    // msDebug("msOGRFileOpen(): %s", pszRequest);
1583

1584
    OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
236✔
1585
    if (hLayer) {
236✔
1586
      OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
236✔
1587
      if (hFeature) {
236✔
1588
        psInfo->bHasSpatialIndex = true;
236✔
1589
        OGR_F_Destroy(hFeature);
236✔
1590
      }
1591
      OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
236✔
1592
    }
1593
    msFree(pszRequest);
236✔
1594
    pszRequest = NULL;
1595

1596
    if (!psInfo->bHasSpatialIndex) {
236✔
1597
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
×
1598
        msDebug("msOGRFileOpen(): Layer %s has no spatial index table\n",
×
1599
                pszLayerDef);
1600
      }
1601
    } else {
1602
      pszRequest = msStringConcatenate(
236✔
1603
          pszRequest,
1604
          "SELECT * FROM geometry_columns WHERE f_table_name = lower('");
1605
      pszRequest =
1606
          msStringConcatenate(pszRequest, psInfo->pszSpatialFilterTableName);
236✔
1607
      pszRequest =
1608
          msStringConcatenate(pszRequest, "') AND f_geometry_column = lower('");
236✔
1609
      pszRequest = msStringConcatenate(pszRequest,
236✔
1610
                                       psInfo->pszSpatialFilterGeometryColumn);
236✔
1611
      pszRequest =
1612
          msStringConcatenate(pszRequest, "') AND spatial_index_enabled = 1");
236✔
1613

1614
      psInfo->bHasSpatialIndex = false;
236✔
1615

1616
      OGRLayerH hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, pszRequest, NULL, NULL);
236✔
1617
      if (hLayer) {
236✔
1618
        OGRFeatureH hFeature = OGR_L_GetNextFeature(hLayer);
236✔
1619
        if (hFeature) {
236✔
1620
          psInfo->bHasSpatialIndex = true;
236✔
1621
          OGR_F_Destroy(hFeature);
236✔
1622
        }
1623
        OGR_DS_ReleaseResultSet(psInfo->hDS, hLayer);
236✔
1624
      }
1625
      msFree(pszRequest);
236✔
1626
      pszRequest = NULL;
1627

1628
      if (!psInfo->bHasSpatialIndex) {
236✔
1629
        if (layer->debug >= MS_DEBUGLEVEL_VVV) {
×
1630
          msDebug("msOGRFileOpen(): Layer %s has spatial index disabled\n",
×
1631
                  pszLayerDef);
1632
        }
1633
      } else {
1634
        if (layer->debug >= MS_DEBUGLEVEL_VVV) {
236✔
1635
          msDebug("msOGRFileOpen(): Layer %s has spatial index enabled\n",
×
1636
                  pszLayerDef);
1637
        }
1638

1639
        psInfo->pszTablePrefix = msStrdup(psInfo->pszMainTableName);
236✔
1640
      }
1641
    }
1642
  }
1643

1644
  psInfo->bPaging = (psInfo->dialect != NULL);
245✔
1645
}
245✔
1646

1647
/************************************************************************/
1648
/*                        msOGRCloseConnection()                        */
1649
/*                                                                      */
1650
/*      Callback for thread pool to actually release an OGR             */
1651
/*      connection.                                                     */
1652
/************************************************************************/
1653

1654
static void msOGRCloseConnection(void *conn_handle)
880✔
1655

1656
{
1657
  OGRDataSourceH hDS = (OGRDataSourceH)conn_handle;
1658

1659
  ACQUIRE_OGR_LOCK;
880✔
1660
  OGR_DS_Destroy(hDS);
880✔
1661
  RELEASE_OGR_LOCK;
880✔
1662
}
880✔
1663

1664
/**********************************************************************
1665
 *                     msOGRFileClose()
1666
 **********************************************************************/
1667
static int msOGRFileClose(layerObj *layer, msOGRFileInfo *psInfo) {
920✔
1668
  if (!psInfo)
920✔
1669
    return MS_SUCCESS;
1670

1671
  if (layer->debug)
920✔
1672
    msDebug("msOGRFileClose(%s,%d).\n", psInfo->pszFname, psInfo->nLayerIndex);
5✔
1673

1674
  CPLFree(psInfo->pszFname);
920✔
1675
  CPLFree(psInfo->pszLayerDef);
920✔
1676

1677
  ACQUIRE_OGR_LOCK;
920✔
1678
  if (psInfo->hLastFeature)
920✔
1679
    OGR_F_Destroy(psInfo->hLastFeature);
308✔
1680

1681
  /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
1682
  if (psInfo->nLayerIndex == -1)
920✔
1683
    OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
81✔
1684

1685
  // Release (potentially close) the datasource connection.
1686
  // Make sure we aren't holding the lock when the callback may need it.
1687
  RELEASE_OGR_LOCK;
920✔
1688
  msConnPoolRelease(layer, psInfo->hDS);
920✔
1689

1690
  // Free current tile if there is one.
1691
  if (psInfo->poCurTile != NULL)
920✔
1692
    msOGRFileClose(layer, psInfo->poCurTile);
5✔
1693

1694
  msFreeProjection(&(psInfo->sTileProj));
920✔
1695
  msFree(psInfo->pszSelect);
920✔
1696
  msFree(psInfo->pszSpatialFilterTableName);
920✔
1697
  msFree(psInfo->pszSpatialFilterGeometryColumn);
920✔
1698
  msFree(psInfo->pszMainTableName);
920✔
1699
  msFree(psInfo->pszRowId);
920✔
1700
  msFree(psInfo->pszTablePrefix);
920✔
1701
  msFree(psInfo->pszWHERE);
920✔
1702

1703
  CPLFree(psInfo);
920✔
1704

1705
  return MS_SUCCESS;
920✔
1706
}
1707

1708
/************************************************************************/
1709
/*                           msOGREscapeSQLParam                        */
1710
/************************************************************************/
1711
static char *msOGREscapeSQLParam(layerObj *layer, const char *pszString) {
49✔
1712
  char *pszEscapedStr = NULL;
1713
  if (layer && pszString) {
49✔
1714
    char *pszEscapedOGRStr =
1715
        CPLEscapeString(pszString, strlen(pszString), CPLES_SQL);
49✔
1716
    pszEscapedStr = msStrdup(pszEscapedOGRStr);
49✔
1717
    CPLFree(pszEscapedOGRStr);
49✔
1718
  }
1719
  return pszEscapedStr;
49✔
1720
}
1721

1722
// http://www.sqlite.org/lang_expr.html
1723
// http://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html
1724

1725
static char *msOGRGetQuotedItem(layerObj *layer, const char *pszItem) {
508✔
1726
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
508✔
1727
  char *ret = NULL;
1728
  char *escapedItem = msLayerEscapePropertyName(layer, pszItem);
508✔
1729
  if (psInfo->pszTablePrefix) {
508✔
1730
    char *escapedTable =
1731
        msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
406✔
1732
    ret = msStringConcatenate(ret, "\"");
406✔
1733
    ret = msStringConcatenate(ret, escapedTable);
406✔
1734
    ret = msStringConcatenate(ret, "\".\"");
406✔
1735
    ret = msStringConcatenate(ret, escapedItem);
406✔
1736
    ret = msStringConcatenate(ret, "\"");
406✔
1737
    msFree(escapedTable);
406✔
1738
  } else {
1739
    ret = msStringConcatenate(ret, "\"");
102✔
1740
    ret = msStringConcatenate(ret, escapedItem);
102✔
1741
    ret = msStringConcatenate(ret, "\"");
102✔
1742
  }
1743
  msFree(escapedItem);
508✔
1744
  return ret;
508✔
1745
}
1746

1747
static char *msOGRGetToken(layerObj *layer, tokenListNodeObjPtr *node) {
309✔
1748
  msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
309✔
1749
  tokenListNodeObjPtr n = *node;
309✔
1750
  if (!n)
309✔
1751
    return NULL;
1752
  char *out = NULL;
1753
  size_t nOutSize;
1754

1755
  switch (n->token) {
309✔
1756
  case MS_TOKEN_LOGICAL_AND:
7✔
1757
    out = msStrdup(" AND ");
7✔
1758
    break;
7✔
1759
  case MS_TOKEN_LOGICAL_OR:
5✔
1760
    out = msStrdup(" OR ");
5✔
1761
    break;
5✔
1762
  case MS_TOKEN_LOGICAL_NOT:
1✔
1763
    out = msStrdup(" NOT ");
1✔
1764
    break;
1✔
1765
  case MS_TOKEN_LITERAL_NUMBER:
18✔
1766
    nOutSize = 32;
1767
    out = (char *)msSmallMalloc(nOutSize);
18✔
1768
    snprintf(out, nOutSize, "%.18g", n->tokenval.dblval);
18✔
1769
    break;
1770
  case MS_TOKEN_LITERAL_STRING: {
17✔
1771
    char *stresc = msOGREscapeSQLParam(layer, n->tokenval.strval);
17✔
1772
    nOutSize = strlen(stresc) + 3;
17✔
1773
    out = (char *)msSmallMalloc(nOutSize);
17✔
1774
    snprintf(out, nOutSize, "'%s'", stresc);
1775
    msFree(stresc);
17✔
1776
    break;
17✔
1777
  }
1778
  case MS_TOKEN_LITERAL_TIME:
×
1779
    // seems to require METADATA gml_types => auto
1780
    nOutSize = 80;
1781
    out = (char *)msSmallMalloc(nOutSize);
×
1782
#if 0
1783
        // FIXME? or perhaps just remove me. tm_zone is not supported on Windows, and not used anywhere else in the code base
1784
        if (n->tokenval.tmval.tm_zone)
1785
            snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d%s'",
1786
                     n->tokenval.tmval.tm_year+1900, n->tokenval.tmval.tm_mon+1, n->tokenval.tmval.tm_mday,
1787
                     n->tokenval.tmval.tm_hour, n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec,
1788
                     n->tokenval.tmval.tm_zone);
1789
        else
1790
#endif
1791
    snprintf(out, nOutSize, "'%d-%02d-%02dT%02d:%02d:%02d'",
×
1792
             n->tokenval.tmval.tm_year + 1900, n->tokenval.tmval.tm_mon + 1,
×
1793
             n->tokenval.tmval.tm_mday, n->tokenval.tmval.tm_hour,
1794
             n->tokenval.tmval.tm_min, n->tokenval.tmval.tm_sec);
1795
    break;
1796
  case MS_TOKEN_LITERAL_SHAPE: {
17✔
1797
    // assumed to be in right srs after FLTGetSpatialComparisonCommonExpression
1798
    char *wkt = msShapeToWKT(n->tokenval.shpval);
17✔
1799
    char *stresc = msOGRGetQuotedItem(
17✔
1800
        layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
1801
    nOutSize = strlen(wkt) + strlen(stresc) + 35;
17✔
1802
    out = (char *)msSmallMalloc(nOutSize);
17✔
1803
    snprintf(out, nOutSize, "ST_GeomFromText('%s',ST_SRID(%s))", wkt, stresc);
1804
    msFree(wkt);
17✔
1805
    msFree(stresc);
17✔
1806
    break;
17✔
1807
  }
1808
  case MS_TOKEN_LITERAL_BOOLEAN:
17✔
1809
    out = msStrdup(n->tokenval.dblval == 0 ? "FALSE" : "TRUE");
34✔
1810
    break;
17✔
1811
  case MS_TOKEN_COMPARISON_EQ:
35✔
1812
    if (n->next != NULL && n->next->token == MS_TOKEN_LITERAL_STRING &&
35✔
1813
        strcmp(n->next->tokenval.strval, "_MAPSERVER_NULL_") == 0) {
10✔
1814
      out = msStrdup(" IS NULL");
2✔
1815
      n = n->next;
2✔
1816
      break;
2✔
1817
    }
1818

1819
    out = msStrdup(" = ");
33✔
1820
    break;
33✔
1821
  case MS_TOKEN_COMPARISON_NE:
3✔
1822
    out = msStrdup(" != ");
3✔
1823
    break;
3✔
1824
  case MS_TOKEN_COMPARISON_GT:
2✔
1825
    out = msStrdup(" > ");
2✔
1826
    break;
2✔
1827
  case MS_TOKEN_COMPARISON_LT:
2✔
1828
    out = msStrdup(" < ");
2✔
1829
    break;
2✔
1830
  case MS_TOKEN_COMPARISON_LE:
4✔
1831
    out = msStrdup(" <= ");
4✔
1832
    break;
4✔
1833
  case MS_TOKEN_COMPARISON_GE:
4✔
1834
    out = msStrdup(" >= ");
4✔
1835
    break;
4✔
1836
  case MS_TOKEN_COMPARISON_IEQ:
×
1837
    out = msStrdup(" = ");
×
1838
    break;
×
1839
  case MS_TOKEN_COMPARISON_IN:
×
1840
    out = msStrdup(" IN ");
×
1841
    break;
×
1842
  // the origin may be mapfile (complex regexes, layer->map.query.filter.string
1843
  // == NULL, regex may have //) or OGC Filter (simple patterns only,
1844
  // layer->map.query.filter.string != NULL)
1845
  case MS_TOKEN_COMPARISON_RE:
6✔
1846
  case MS_TOKEN_COMPARISON_IRE: {
1847
    int case_sensitive = n->token == MS_TOKEN_COMPARISON_RE;
1848
    // in PostgreSQL and OGR: LIKE (case sensitive) and ILIKE (case insensitive)
1849
    // in SQLite: LIKE (case insensitive) and GLOB (case sensitive)
1850
    const char *op = case_sensitive ? "LIKE" : "ILIKE";
6✔
1851
    char wild_any = '%';
1852
    char wild_one = '_';
1853

1854
    if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG")) {
6✔
1855
      if (case_sensitive) {
6✔
1856
        op = "GLOB";
1857
        wild_any = '*';
1858
        wild_one = '?';
1859
      } else {
1860
        op = "LIKE";
1861
      }
1862
    }
1863

1864
    n = n->next;
6✔
1865
    if (n->token != MS_TOKEN_LITERAL_STRING)
6✔
1866
      return NULL;
1867

1868
    char *regex = msStrdup(n->tokenval.strval);
6✔
1869
    int complex_regex = *n->tokenval.strval ==
6✔
1870
                        '/'; // could be non-complex but that is soo corner case
1871

1872
    // PostgreSQL has POSIX regexes, SQLite does not by default, OGR does not
1873
    if (complex_regex) {
6✔
1874
      if (!EQUAL(info->dialect, "PostgreSQL")) {
×
1875
        msFree(regex);
×
1876
        return NULL;
×
1877
      }
1878
      // remove //
1879
      regex++;
×
1880
      regex[strlen(regex) - 1] = '\0';
×
1881
      if (case_sensitive)
×
1882
        op = "~";
1883
      else
1884
        op = "~*";
1885
    }
1886

1887
    const size_t regex_len = strlen(regex);
6✔
1888
    char *re = (char *)msSmallMalloc(2 * regex_len + 3);
6✔
1889
    size_t i = 0, j = 0;
1890
    re[j++] = '\'';
6✔
1891
    while (i < regex_len) {
55✔
1892
      char c = regex[i];
55✔
1893
      char c_next = regex[i + 1];
55✔
1894

1895
      if (c == '.' && c_next == '*') {
55✔
1896
        i++;
1897
        c = wild_any;
1898
      } else if (c == '.')
49✔
1899
        c = wild_one;
1900
      else if (i == 0 && c == '^') {
48✔
1901
        i++;
1902
        continue;
6✔
1903
      } else if (c == '$' && c_next == 0) {
42✔
1904
        break;
1905
      } else if (c == '\'') {
36✔
1906
        re[j++] = '\'';
1✔
1907
      } else if (c == '\\') {
35✔
1908
        if (c_next == 0) {
3✔
1909
          break;
1910
        }
1911
        i++;
1912
        c = c_next;
1913
      }
1914

1915
      re[j++] = c;
43✔
1916
      i++;
43✔
1917
    }
1918
    re[j++] = '\'';
6✔
1919
    re[j] = '\0';
6✔
1920

1921
    nOutSize = 1 + strlen(op) + 1 + strlen(re) + 1;
6✔
1922
    out = (char *)msSmallMalloc(nOutSize);
6✔
1923
    snprintf(out, nOutSize, " %s %s", op, re);
1924
    msFree(re);
6✔
1925
    msFree(regex);
6✔
1926
    break;
6✔
1927
  }
1928
  case MS_TOKEN_COMPARISON_INTERSECTS:
8✔
1929
    out = msStrdup("ST_Intersects");
8✔
1930
    break;
8✔
1931
  case MS_TOKEN_COMPARISON_DISJOINT:
1✔
1932
    out = msStrdup("ST_Disjoint");
1✔
1933
    break;
1✔
1934
  case MS_TOKEN_COMPARISON_TOUCHES:
1✔
1935
    out = msStrdup("ST_Touches");
1✔
1936
    break;
1✔
1937
  case MS_TOKEN_COMPARISON_OVERLAPS:
1✔
1938
    out = msStrdup("ST_Overlaps");
1✔
1939
    break;
1✔
1940
  case MS_TOKEN_COMPARISON_CROSSES:
1✔
1941
    out = msStrdup("ST_Crosses");
1✔
1942
    break;
1✔
1943
  case MS_TOKEN_COMPARISON_WITHIN:
1✔
1944
    out = msStrdup("ST_Within");
1✔
1945
    break;
1✔
1946
  case MS_TOKEN_COMPARISON_DWITHIN:
1✔
1947
    out = msStrdup("ST_Distance");
1✔
1948
    break;
1✔
1949
  case MS_TOKEN_COMPARISON_BEYOND:
1✔
1950
    out = msStrdup("ST_Distance");
1✔
1951
    break;
1✔
1952
  case MS_TOKEN_COMPARISON_CONTAINS:
1✔
1953
    out = msStrdup("ST_Contains");
1✔
1954
    break;
1✔
1955
  case MS_TOKEN_COMPARISON_EQUALS:
1✔
1956
    out = msStrdup("ST_Equals");
1✔
1957
    break;
1✔
1958
  case MS_TOKEN_FUNCTION_LENGTH:
×
1959
    out = msStrdup("ST_Length");
×
1960
    break;
×
1961
  case MS_TOKEN_FUNCTION_AREA:
×
1962
    out = msStrdup("ST_Area");
×
1963
    break;
×
1964
  case MS_TOKEN_BINDING_DOUBLE: {
16✔
1965
    char *stresc = msOGRGetQuotedItem(layer, n->tokenval.bindval.item);
16✔
1966
    nOutSize = strlen(stresc) + +30;
16✔
1967
    out = (char *)msSmallMalloc(nOutSize);
16✔
1968

1969
    bool bIsNumeric = msLayerPropertyIsNumeric(layer, n->tokenval.bindval.item);
16✔
1970
    // Do not cast if the variable is of the appropriate type as it can
1971
    // prevent using database indexes, such as for SQlite
1972
    if (bIsNumeric) {
16✔
1973
      snprintf(out, nOutSize, "%s", stresc);
1974
    } else {
1975
      const char *SQLtype = "float(16)";
1976
      if (EQUAL(info->dialect, "Spatialite") || EQUAL(info->dialect, "GPKG"))
6✔
1977
        SQLtype = "REAL";
1978
      else if (EQUAL(info->dialect, "PostgreSQL"))
×
1979
        SQLtype = "double precision";
1980
      snprintf(out, nOutSize, "CAST(%s AS %s)", stresc, SQLtype);
1981
    }
1982
    msFree(stresc);
16✔
1983
    break;
16✔
1984
  }
1985
  case MS_TOKEN_BINDING_INTEGER: {
×
1986
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
×
1987
    nOutSize = strlen(stresc) + 20;
×
1988
    out = (char *)msSmallMalloc(nOutSize);
×
1989

1990
    bool bIsNumeric = msLayerPropertyIsNumeric(layer, n->tokenval.bindval.item);
×
1991
    // Do not cast if the variable is of the appropriate type as it can
1992
    // prevent using database indexes, such as for SQlite
1993
    if (bIsNumeric) {
×
1994
      snprintf(out, nOutSize, "\"%s\"", stresc);
1995
    } else {
1996
      snprintf(out, nOutSize, "CAST(\"%s\" AS integer)", stresc);
1997
    }
1998
    msFree(stresc);
×
1999
    break;
×
2000
  }
2001
  case MS_TOKEN_BINDING_STRING: {
25✔
2002
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
25✔
2003
    nOutSize = strlen(stresc) + 30;
25✔
2004
    out = (char *)msSmallMalloc(nOutSize);
25✔
2005

2006
    bool bIsCharacter =
2007
        msLayerPropertyIsCharacter(layer, n->tokenval.bindval.item);
25✔
2008
    // Do not cast if the variable is of the appropriate type as it can
2009
    // prevent using database indexes, such as for SQlite
2010
    if (bIsCharacter) {
25✔
2011
      snprintf(out, nOutSize, "\"%s\"", stresc);
2012
    } else {
2013
      snprintf(out, nOutSize, "CAST(\"%s\" AS text)", stresc);
2014
    }
2015
    msFree(stresc);
25✔
2016
    break;
25✔
2017
  }
2018
  case MS_TOKEN_BINDING_TIME: {
×
2019
    // won't get here unless col is parsed as time and they are not
2020
    char *stresc = msLayerEscapePropertyName(layer, n->tokenval.bindval.item);
×
2021
    nOutSize = strlen(stresc) + 10;
×
2022
    out = (char *)msSmallMalloc(nOutSize);
×
2023
    snprintf(out, nOutSize, "\"%s\"", stresc);
2024
    msFree(stresc);
×
2025
    break;
×
2026
  }
2027
  case MS_TOKEN_BINDING_SHAPE: {
17✔
2028
    char *stresc = msLayerEscapePropertyName(
17✔
2029
        layer, OGR_L_GetGeometryColumn(info->hLayer)); // which geom field??
2030
    nOutSize = strlen(stresc) + 10;
17✔
2031
    out = (char *)msSmallMalloc(nOutSize);
17✔
2032
    snprintf(out, nOutSize, "%s", stresc);
2033
    msFree(stresc);
17✔
2034
    break;
17✔
2035
  }
2036

2037
    // unhandled below until default
2038

2039
  case MS_TOKEN_FUNCTION_TOSTRING:
2040
  case MS_TOKEN_FUNCTION_COMMIFY:
2041
  case MS_TOKEN_FUNCTION_ROUND:
2042
  case MS_TOKEN_FUNCTION_FROMTEXT:
2043
  case MS_TOKEN_FUNCTION_BUFFER:
2044
  case MS_TOKEN_FUNCTION_DIFFERENCE:
2045
  case MS_TOKEN_FUNCTION_SIMPLIFY:
2046
  case MS_TOKEN_FUNCTION_SIMPLIFYPT:
2047
  case MS_TOKEN_FUNCTION_GENERALIZE:
2048
  case MS_TOKEN_FUNCTION_SMOOTHSIA:
2049
  case MS_TOKEN_FUNCTION_JAVASCRIPT:
2050
  case MS_TOKEN_FUNCTION_UPPER:
2051
  case MS_TOKEN_FUNCTION_LOWER:
2052
  case MS_TOKEN_FUNCTION_INITCAP:
2053
  case MS_TOKEN_FUNCTION_FIRSTCAP:
2054
  case MS_TOKEN_BINDING_MAP_CELLSIZE:
2055
  case MS_TOKEN_BINDING_DATA_CELLSIZE:
2056
  case MS_PARSE_TYPE_BOOLEAN:
2057
  case MS_PARSE_TYPE_STRING:
2058
  case MS_PARSE_TYPE_SHAPE:
2059
    break;
2060

2061
  default:
96✔
2062
    if (n->token < 128) {
96✔
2063
      char c = n->token;
96✔
2064
      const size_t nSize = 2;
2065
      out = (char *)msSmallMalloc(nSize);
96✔
2066
      snprintf(out, nSize, "%c", c);
96✔
2067
    }
2068
    break;
2069
  }
2070

2071
  n = n->next;
309✔
2072
  *node = n;
309✔
2073
  return out;
309✔
2074
}
2075

2076
/*
2077
 * msOGRLayerBuildSQLOrderBy()
2078
 *
2079
 * Returns the content of a SQL ORDER BY clause from the sortBy member of
2080
 * the layer. The string does not contain the "ORDER BY" keywords itself.
2081
 */
2082
static char *msOGRLayerBuildSQLOrderBy(layerObj *layer, msOGRFileInfo *psInfo) {
7✔
2083
  char *strOrderBy = NULL;
2084
  if (layer->sortBy.nProperties > 0) {
7✔
2085
    int i;
2086
    for (i = 0; i < layer->sortBy.nProperties; i++) {
19✔
2087
      if (i > 0)
12✔
2088
        strOrderBy = msStringConcatenate(strOrderBy, ", ");
5✔
2089
      char *escapedItem =
2090
          msLayerEscapePropertyName(layer, layer->sortBy.properties[i].item);
12✔
2091
      if (psInfo->pszTablePrefix) {
12✔
2092
        char *escapedTable =
2093
            msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
1✔
2094
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
1✔
2095
        strOrderBy = msStringConcatenate(strOrderBy, escapedTable);
1✔
2096
        strOrderBy = msStringConcatenate(strOrderBy, "\".\"");
1✔
2097
        strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
1✔
2098
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
1✔
2099
        msFree(escapedTable);
1✔
2100
      } else {
2101
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
11✔
2102
        strOrderBy = msStringConcatenate(strOrderBy, escapedItem);
11✔
2103
        strOrderBy = msStringConcatenate(strOrderBy, "\"");
11✔
2104
      }
2105
      msFree(escapedItem);
12✔
2106
      if (layer->sortBy.properties[i].sortOrder == SORT_DESC)
12✔
2107
        strOrderBy = msStringConcatenate(strOrderBy, " DESC");
6✔
2108
    }
2109
  }
2110
  return strOrderBy;
7✔
2111
}
2112

2113
/**********************************************************************
2114
 *                     msOGRFileWhichShapes()
2115
 *
2116
 * Init OGR layer structs ready for calls to msOGRFileNextShape().
2117
 *
2118
 * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
2119
 * layer's FILTER overlaps the selected region.
2120
 **********************************************************************/
2121
static int msOGRFileWhichShapes(layerObj *layer, rectObj rect,
320✔
2122
                                msOGRFileInfo *psInfo) {
2123
  // rect is from BBOX parameter in query (In lieu of a FEATUREID or FILTER) or
2124
  // mapfile somehow
2125
  if (psInfo == NULL || psInfo->hLayer == NULL) {
320✔
2126
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2127
               "msOGRFileWhichShapes()");
2128
    return (MS_FAILURE);
×
2129
  }
2130

2131
  char *select = (psInfo->pszSelect) ? msStrdup(psInfo->pszSelect) : NULL;
320✔
2132
  const rectObj rectInvalid = MS_INIT_INVALID_RECT;
320✔
2133
  bool bIsValidRect = memcmp(&rect, &rectInvalid, sizeof(rect)) != 0;
320✔
2134

2135
  // we'll go strictly two possible ways:
2136
  // 1) GetLayer + SetFilter
2137
  // 2) ExecuteSQL (psInfo->hLayer is an SQL result OR sortBy was requested OR
2138
  // have native_string and start from the second
2139

2140
  if (psInfo->bIsOKForSQLCompose &&
320✔
2141
      (psInfo->nLayerIndex == -1 || layer->sortBy.nProperties > 0 ||
318✔
2142
       layer->filter.native_string ||
300✔
2143
       (psInfo->bPaging && layer->maxfeatures > 0))) {
257✔
2144

2145
    const bool bHasGeometry = OGR_L_GetGeomType(psInfo->hLayer) != wkbNone;
71✔
2146

2147
    if (psInfo->nLayerIndex == -1 && select == NULL) {
71✔
2148
      select = msStrdup(psInfo->pszLayerDef);
8✔
2149
      /* If nLayerIndex == -1 then the layer is an SQL result ... free it */
2150
      OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
8✔
2151
      psInfo->hLayer = NULL;
8✔
2152
    } else if (select == NULL) {
60✔
2153
      const char *pszGeometryColumn;
2154
      int i;
2155
      select = msStringConcatenate(select, "SELECT ");
60✔
2156
      for (i = 0; i < layer->numitems; i++) {
481✔
2157
        if (i > 0)
421✔
2158
          select = msStringConcatenate(select, ", ");
361✔
2159
        char *escaped = msOGRGetQuotedItem(layer, layer->items[i]);
421✔
2160
        select = msStringConcatenate(select, escaped);
421✔
2161
        msFree(escaped);
421✔
2162
        if (psInfo->pszTablePrefix) {
421✔
2163
          select = msStringConcatenate(select, " AS \"");
319✔
2164
          escaped = msLayerEscapePropertyName(layer, layer->items[i]);
319✔
2165
          select = msStringConcatenate(select, escaped);
319✔
2166
          msFree(escaped);
319✔
2167
          select = msStringConcatenate(select, "\"");
319✔
2168
        }
2169
      }
2170
      if (layer->numitems > 0)
60✔
2171
        select = msStringConcatenate(select, ", ");
60✔
2172
      pszGeometryColumn = OGR_L_GetGeometryColumn(psInfo->hLayer);
60✔
2173
      if (pszGeometryColumn != NULL && pszGeometryColumn[0] != '\0') {
60✔
2174
        char *escaped = msOGRGetQuotedItem(layer, pszGeometryColumn);
54✔
2175
        select = msStringConcatenate(select, escaped);
54✔
2176
        msFree(escaped);
54✔
2177
        if (psInfo->pszTablePrefix) {
54✔
2178
          select = msStringConcatenate(select, " AS \"");
54✔
2179
          escaped = msLayerEscapePropertyName(layer, pszGeometryColumn);
54✔
2180
          select = msStringConcatenate(select, escaped);
54✔
2181
          msFree(escaped);
54✔
2182
          select = msStringConcatenate(select, "\"");
54✔
2183
        }
2184
      } else {
2185
        /* Add ", *" so that we still have an hope to get the geometry */
2186
        if (psInfo->pszTablePrefix) {
6✔
2187
          select = msStringConcatenate(select, "\"");
×
2188
          char *escaped =
2189
              msLayerEscapePropertyName(layer, psInfo->pszTablePrefix);
×
2190
          select = msStringConcatenate(select, escaped);
×
2191
          msFree(escaped);
×
2192
          select = msStringConcatenate(select, "\".");
×
2193
        }
2194
        select = msStringConcatenate(select, "*");
6✔
2195
      }
2196
      select = msStringConcatenate(select, " FROM ");
60✔
2197
      if (psInfo->nLayerIndex == -1) {
60✔
2198
        select = msStringConcatenate(select, "(");
×
2199
        select = msStringConcatenate(select, psInfo->pszLayerDef);
×
2200
        select = msStringConcatenate(select, ") MSSUBSELECT");
×
2201
      } else {
2202
        select = msStringConcatenate(select, "\"");
60✔
2203
        char *escaped = msLayerEscapePropertyName(
60✔
2204
            layer, OGR_FD_GetName(OGR_L_GetLayerDefn(psInfo->hLayer)));
2205
        select = msStringConcatenate(select, escaped);
60✔
2206
        msFree(escaped);
60✔
2207
        select = msStringConcatenate(select, "\"");
60✔
2208
      }
2209
    }
2210

2211
    char *filter = NULL;
2212
    if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
71✔
2213
      filter = msStringConcatenate(filter, "(");
×
2214
      filter = msStringConcatenate(
×
2215
          filter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2216
      filter = msStringConcatenate(filter, ")");
×
2217
    }
2218

2219
    /* ------------------------------------------------------------------
2220
     * Set Spatial filter... this may result in no features being returned
2221
     * if layer does not overlap current view.
2222
     *
2223
     * __TODO__ We should return MS_DONE if no shape overlaps the selected
2224
     * region and matches the layer's FILTER expression, but there is currently
2225
     * no _efficient_ way to do that with OGR.
2226
     * ------------------------------------------------------------------ */
2227
    if (psInfo->rect_is_defined) {
71✔
2228
      rect.minx = std::max(psInfo->rect.minx, rect.minx);
12✔
2229
      rect.miny = std::max(psInfo->rect.miny, rect.miny);
12✔
2230
      rect.maxx = std::min(psInfo->rect.maxx, rect.maxx);
12✔
2231
      rect.maxy = std::min(psInfo->rect.maxy, rect.maxy);
12✔
2232
      bIsValidRect = true;
2233
    }
2234
    psInfo->rect = rect;
71✔
2235

2236
    bool bSpatialiteOrGPKGAddOrderByFID = false;
2237

2238
    const char *sql = layer->filter.native_string;
71✔
2239
    if (psInfo->dialect && sql && *sql != '\0' &&
71✔
2240
        (EQUAL(psInfo->dialect, "Spatialite") ||
46✔
2241
         EQUAL(psInfo->dialect, "GPKG") ||
6✔
2242
         EQUAL(psInfo->dialect, "PostgreSQL"))) {
×
2243
      if (filter)
46✔
2244
        filter = msStringConcatenate(filter, " AND ");
×
2245
      filter = msStringConcatenate(filter, "(");
46✔
2246
      filter = msStringConcatenate(filter, sql);
46✔
2247
      filter = msStringConcatenate(filter, ")");
46✔
2248
    } else if (psInfo->pszWHERE) {
25✔
2249
      if (filter)
×
2250
        filter = msStringConcatenate(filter, " AND ");
×
2251
      filter = msStringConcatenate(filter, "(");
×
2252
      filter = msStringConcatenate(filter, psInfo->pszWHERE);
×
2253
      filter = msStringConcatenate(filter, ")");
×
2254
    }
2255

2256
    // use spatial index
2257
    if (psInfo->dialect && bIsValidRect) {
71✔
2258
      if (EQUAL(psInfo->dialect, "PostgreSQL")) {
53✔
2259
        if (filter)
×
2260
          filter = msStringConcatenate(filter, " AND");
×
2261
        const char *col =
2262
            OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
×
2263
        filter = msStringConcatenate(filter, " (\"");
×
2264
        char *escaped = msLayerEscapePropertyName(layer, col);
×
2265
        filter = msStringConcatenate(filter, escaped);
×
2266
        msFree(escaped);
×
2267
        filter = msStringConcatenate(filter, "\" && ST_MakeEnvelope(");
×
2268
        char *points = (char *)msSmallMalloc(30 * 2 * 5);
×
2269
        snprintf(points, 30 * 4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny,
×
2270
                 rect.maxx, rect.maxy);
2271
        filter = msStringConcatenate(filter, points);
×
2272
        msFree(points);
×
2273
        filter = msStringConcatenate(filter, "))");
×
2274
      } else if (psInfo->dialect &&
53✔
2275
                 (EQUAL(psInfo->dialect, "Spatialite") ||
53✔
2276
                  EQUAL(psInfo->dialect, "GPKG")) &&
12✔
2277
                 psInfo->pszMainTableName != NULL) {
53✔
2278
        if ((EQUAL(psInfo->dialect, "Spatialite") &&
53✔
2279
             psInfo->bHasSpatialIndex) ||
41✔
2280
            EQUAL(psInfo->dialect, "GPKG")) {
12✔
2281
          if (filter)
53✔
2282
            filter = msStringConcatenate(filter, " AND ");
44✔
2283
          char *pszEscapedMainTableName =
2284
              msLayerEscapePropertyName(layer, psInfo->pszMainTableName);
53✔
2285
          filter = msStringConcatenate(filter, "\"");
53✔
2286
          filter = msStringConcatenate(filter, pszEscapedMainTableName);
53✔
2287
          msFree(pszEscapedMainTableName);
53✔
2288
          filter = msStringConcatenate(filter, "\".");
53✔
2289
          if (psInfo->pszRowId) {
53✔
2290
            char *pszEscapedRowId =
2291
                msLayerEscapePropertyName(layer, psInfo->pszRowId);
2✔
2292
            filter = msStringConcatenate(filter, "\"");
2✔
2293
            filter = msStringConcatenate(filter, pszEscapedRowId);
2✔
2294
            filter = msStringConcatenate(filter, "\"");
2✔
2295
            msFree(pszEscapedRowId);
2✔
2296
          } else
2297
            filter = msStringConcatenate(filter, "ROWID");
51✔
2298

2299
          filter = msStringConcatenate(filter, " IN ");
53✔
2300
          filter = msStringConcatenate(filter, "(");
53✔
2301
          filter = msStringConcatenate(filter, "SELECT ");
53✔
2302

2303
          if (EQUAL(psInfo->dialect, "Spatialite"))
53✔
2304
            filter = msStringConcatenate(filter, "ms_spat_idx.pkid");
41✔
2305
          else
2306
            filter = msStringConcatenate(filter, "ms_spat_idx.id");
12✔
2307

2308
          filter = msStringConcatenate(filter, " FROM ");
53✔
2309

2310
          char szSpatialIndexName[256];
2311
          snprintf(szSpatialIndexName, sizeof(szSpatialIndexName), "%s_%s_%s",
53✔
2312
                   EQUAL(psInfo->dialect, "Spatialite") ? "idx" : "rtree",
53✔
2313
                   psInfo->pszSpatialFilterTableName,
2314
                   psInfo->pszSpatialFilterGeometryColumn);
2315
          char *pszEscapedSpatialIndexName =
2316
              msLayerEscapePropertyName(layer, szSpatialIndexName);
53✔
2317

2318
          filter = msStringConcatenate(filter, "\"");
53✔
2319
          filter = msStringConcatenate(filter, pszEscapedSpatialIndexName);
53✔
2320
          msFree(pszEscapedSpatialIndexName);
53✔
2321

2322
          filter = msStringConcatenate(filter, "\" ms_spat_idx WHERE ");
53✔
2323

2324
          char szCond[256];
2325
          if (EQUAL(psInfo->dialect, "Spatialite")) {
53✔
2326
            snprintf(
41✔
2327
                szCond, sizeof(szCond),
2328
                "ms_spat_idx.xmin <= %.15g AND ms_spat_idx.xmax >= %.15g AND "
2329
                "ms_spat_idx.ymin <= %.15g AND ms_spat_idx.ymax >= %.15g",
2330
                rect.maxx, rect.minx, rect.maxy, rect.miny);
2331
          } else {
2332
            snprintf(
12✔
2333
                szCond, sizeof(szCond),
2334
                "ms_spat_idx.minx <= %.15g AND ms_spat_idx.maxx >= %.15g AND "
2335
                "ms_spat_idx.miny <= %.15g AND ms_spat_idx.maxy >= %.15g",
2336
                rect.maxx, rect.minx, rect.maxy, rect.miny);
2337
          }
2338
          filter = msStringConcatenate(filter, szCond);
53✔
2339

2340
          filter = msStringConcatenate(filter, ")");
53✔
2341

2342
          bSpatialiteOrGPKGAddOrderByFID = true;
2343
        }
2344

2345
        const bool isGPKG = EQUAL(psInfo->dialect, "GPKG");
53✔
2346
        if (filter)
53✔
2347
          filter = msStringConcatenate(filter, " AND");
53✔
2348
        const char *col =
2349
            OGR_L_GetGeometryColumn(psInfo->hLayer); // which geom field??
53✔
2350
        filter = msStringConcatenate(filter, " Intersects(");
53✔
2351
        if (isGPKG) {
53✔
2352
          // Casting GeoPackage geometries to spatialie ones is done
2353
          // automatically normally, since GDAL enables the
2354
          // "amphibious" mode, but without it
2355
          // explicitly specified, spatialite 4.3.0a does an
2356
          // out-of-bounds access.
2357
          filter = msStringConcatenate(filter, "GeomFromGPB(");
12✔
2358
        }
2359
        filter = msStringConcatenate(filter, "\"");
53✔
2360
        char *escaped = msLayerEscapePropertyName(layer, col);
53✔
2361
        filter = msStringConcatenate(filter, escaped);
53✔
2362
        msFree(escaped);
53✔
2363
        filter = msStringConcatenate(filter, "\"");
53✔
2364
        if (isGPKG)
53✔
2365
          filter = msStringConcatenate(filter, ")");
12✔
2366
        char *points = (char *)msSmallMalloc(30 * 2 * 5);
53✔
2367
        if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
53✔
2368
          filter = msStringConcatenate(filter, ",  ST_GeomFromText(");
1✔
2369
          snprintf(points, 30 * 4, "'POINT(%lf %lf)'", rect.minx, rect.miny);
2370
        } else {
2371
          filter = msStringConcatenate(filter, ", BuildMbr(");
52✔
2372
          snprintf(points, 30 * 4, "%lf,%lf,%lf,%lf", rect.minx, rect.miny,
52✔
2373
                   rect.maxx, rect.maxy);
2374
        }
2375
        filter = msStringConcatenate(filter, points);
53✔
2376
        msFree(points);
53✔
2377
        filter = msStringConcatenate(filter, "))");
53✔
2378
      }
2379
    }
2380

2381
    /* get sortBy */
2382
    char *sort = NULL;
2383
    if (layer->sortBy.nProperties > 0) {
71✔
2384

2385
      char *strOrderBy = msOGRLayerBuildSQLOrderBy(layer, psInfo);
7✔
2386
      if (strOrderBy) {
7✔
2387
        if (psInfo->nLayerIndex == -1) {
7✔
2388
          if (strcasestr(psInfo->pszLayerDef, " ORDER BY ") == NULL)
×
2389
            sort = msStringConcatenate(sort, " ORDER BY ");
×
2390
          else
2391
            sort = msStringConcatenate(sort, ", ");
×
2392
        } else {
2393
          sort = msStringConcatenate(sort, " ORDER BY ");
7✔
2394
        }
2395
        sort = msStringConcatenate(sort, strOrderBy);
7✔
2396
        msFree(strOrderBy);
7✔
2397
      }
2398
    }
2399

2400
    if (bSpatialiteOrGPKGAddOrderByFID) {
71✔
2401
      if (sort == NULL)
53✔
2402
        sort = msStringConcatenate(NULL, " ORDER BY ");
52✔
2403
      else
2404
        sort = msStringConcatenate(sort, ", ");
1✔
2405
      char *pszEscapedMainTableName =
2406
          msLayerEscapePropertyName(layer, psInfo->pszMainTableName);
53✔
2407
      sort = msStringConcatenate(sort, "\"");
53✔
2408
      sort = msStringConcatenate(sort, pszEscapedMainTableName);
53✔
2409
      sort = msStringConcatenate(sort, "\".");
53✔
2410
      msFree(pszEscapedMainTableName);
53✔
2411
      if (psInfo->pszRowId) {
53✔
2412
        char *pszEscapedRowId =
2413
            msLayerEscapePropertyName(layer, psInfo->pszRowId);
2✔
2414
        sort = msStringConcatenate(sort, "\"");
2✔
2415
        sort = msStringConcatenate(sort, pszEscapedRowId);
2✔
2416
        sort = msStringConcatenate(sort, "\"");
2✔
2417
        msFree(pszEscapedRowId);
2✔
2418
      } else
2419
        sort = msStringConcatenate(sort, "ROWID");
51✔
2420
    }
2421

2422
    // compose SQL
2423
    if (filter) {
71✔
2424
      select = msStringConcatenate(select, " WHERE ");
55✔
2425
      select = msStringConcatenate(select, filter);
55✔
2426
      msFree(filter);
55✔
2427
    }
2428
    if (sort) {
71✔
2429
      select = msStringConcatenate(select, " ");
59✔
2430
      select = msStringConcatenate(select, sort);
59✔
2431
      msFree(sort);
59✔
2432
    }
2433

2434
    if (psInfo->bPaging && layer->maxfeatures >= 0) {
71✔
2435
      char szLimit[50];
2436
      snprintf(szLimit, sizeof(szLimit), " LIMIT %d", layer->maxfeatures);
2437
      select = msStringConcatenate(select, szLimit);
11✔
2438
    }
2439

2440
    if (psInfo->bPaging && layer->startindex > 0) {
71✔
2441
      char szOffset[50];
2442
      snprintf(szOffset, sizeof(szOffset), " OFFSET %d", layer->startindex - 1);
5✔
2443
      select = msStringConcatenate(select, szOffset);
5✔
2444
    }
2445

2446
    if (layer->debug)
71✔
2447
      msDebug("msOGRFileWhichShapes: SQL = %s.\n", select);
×
2448

2449
    ACQUIRE_OGR_LOCK;
71✔
2450
    if (psInfo->nLayerIndex == -1 && psInfo->hLayer != NULL) {
71✔
2451
      OGR_DS_ReleaseResultSet(psInfo->hDS, psInfo->hLayer);
3✔
2452
    }
2453

2454
    OGRGeometryH hGeom = NULL;
2455
    if (psInfo->dialect == NULL && bHasGeometry && bIsValidRect) {
71✔
2456
      if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
14✔
2457
        hGeom = OGR_G_CreateGeometry(wkbPoint);
×
2458
        OGR_G_SetPoint_2D(hGeom, 0, rect.minx, rect.miny);
×
2459
      } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
14✔
2460
        hGeom = OGR_G_CreateGeometry(wkbLineString);
×
2461
        OGR_G_AddPoint_2D(hGeom, rect.minx, rect.miny);
×
2462
        OGR_G_AddPoint_2D(hGeom, rect.maxx, rect.maxy);
×
2463
      } else {
2464
        hGeom = OGR_G_CreateGeometry(wkbPolygon);
14✔
2465
        OGRGeometryH hRing = OGR_G_CreateGeometry(wkbLinearRing);
14✔
2466

2467
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
14✔
2468
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.miny);
14✔
2469
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.maxy);
14✔
2470
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.maxy);
14✔
2471
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
14✔
2472
        OGR_G_AddGeometryDirectly(hGeom, hRing);
14✔
2473
      }
2474

2475
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
14✔
2476
        msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g "
×
2477
                "%.15g %.15g\n",
2478
                rect.minx, rect.miny, rect.maxx, rect.maxy);
2479
      }
2480
    }
2481

2482
    psInfo->hLayer = OGR_DS_ExecuteSQL(psInfo->hDS, select, hGeom, NULL);
71✔
2483
    psInfo->nLayerIndex = -1;
71✔
2484
    if (hGeom != NULL)
71✔
2485
      OGR_G_DestroyGeometry(hGeom);
14✔
2486

2487
    if (psInfo->hLayer == NULL) {
71✔
2488
      RELEASE_OGR_LOCK;
×
2489
      msSetError(MS_OGRERR, "ExecuteSQL() failed. Check logs.",
×
2490
                 "msOGRFileWhichShapes()");
2491
      msDebug("ExecuteSQL(%s) failed.\n%s\n", select, CPLGetLastErrorMsg());
×
2492
      msFree(select);
×
2493
      return MS_FAILURE;
×
2494
    }
2495
  } else {
2496

2497
    // case of 1) GetLayer + SetFilter
2498

2499
    char *pszOGRFilter = NULL;
2500
    if (msLayerGetProcessingKey(layer, "NATIVE_FILTER") != NULL) {
249✔
2501
      pszOGRFilter = msStringConcatenate(pszOGRFilter, "(");
3✔
2502
      pszOGRFilter = msStringConcatenate(
3✔
2503
          pszOGRFilter, msLayerGetProcessingKey(layer, "NATIVE_FILTER"));
2504
      pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
3✔
2505
    }
2506

2507
    if (psInfo->pszWHERE) {
249✔
2508
      if (pszOGRFilter) {
42✔
2509
        pszOGRFilter = msStringConcatenate(pszOGRFilter, " AND (");
×
2510
        pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
×
2511
        pszOGRFilter = msStringConcatenate(pszOGRFilter, ")");
×
2512
      } else {
2513
        pszOGRFilter = msStringConcatenate(pszOGRFilter, psInfo->pszWHERE);
42✔
2514
      }
2515
    }
2516

2517
    ACQUIRE_OGR_LOCK;
249✔
2518

2519
    if (OGR_L_GetGeomType(psInfo->hLayer) != wkbNone && bIsValidRect) {
249✔
2520
      if (rect.minx == rect.maxx && rect.miny == rect.maxy) {
239✔
2521
        OGRGeometryH hSpatialFilterPoint = OGR_G_CreateGeometry(wkbPoint);
7✔
2522

2523
        OGR_G_SetPoint_2D(hSpatialFilterPoint, 0, rect.minx, rect.miny);
7✔
2524
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterPoint);
7✔
2525
        OGR_G_DestroyGeometry(hSpatialFilterPoint);
7✔
2526
      } else if (rect.minx == rect.maxx || rect.miny == rect.maxy) {
232✔
2527
        OGRGeometryH hSpatialFilterLine = OGR_G_CreateGeometry(wkbLineString);
×
2528

2529
        OGR_G_AddPoint_2D(hSpatialFilterLine, rect.minx, rect.miny);
×
2530
        OGR_G_AddPoint_2D(hSpatialFilterLine, rect.maxx, rect.maxy);
×
2531
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterLine);
×
2532
        OGR_G_DestroyGeometry(hSpatialFilterLine);
×
2533
      } else {
2534
        OGRGeometryH hSpatialFilterPolygon = OGR_G_CreateGeometry(wkbPolygon);
232✔
2535
        OGRGeometryH hRing = OGR_G_CreateGeometry(wkbLinearRing);
232✔
2536

2537
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
232✔
2538
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.miny);
232✔
2539
        OGR_G_AddPoint_2D(hRing, rect.maxx, rect.maxy);
232✔
2540
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.maxy);
232✔
2541
        OGR_G_AddPoint_2D(hRing, rect.minx, rect.miny);
232✔
2542
        OGR_G_AddGeometryDirectly(hSpatialFilterPolygon, hRing);
232✔
2543
        OGR_L_SetSpatialFilter(psInfo->hLayer, hSpatialFilterPolygon);
232✔
2544
        OGR_G_DestroyGeometry(hSpatialFilterPolygon);
232✔
2545
      }
2546

2547
      if (layer->debug >= MS_DEBUGLEVEL_VVV) {
239✔
2548
        msDebug("msOGRFileWhichShapes: Setting spatial filter to %.15g %.15g "
2✔
2549
                "%.15g %.15g\n",
2550
                rect.minx, rect.miny, rect.maxx, rect.maxy);
2551
      }
2552
    }
2553

2554
    psInfo->rect = rect;
249✔
2555

2556
    /* ------------------------------------------------------------------
2557
     * Apply an attribute filter if we have one prefixed with a WHERE
2558
     * keyword in the filter string.  Otherwise, ensure the attribute
2559
     * filter is clear.
2560
     * ------------------------------------------------------------------ */
2561
    if (pszOGRFilter != NULL) {
249✔
2562

2563
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
45✔
2564
        msDebug("msOGRFileWhichShapes: Setting attribute filter to %s\n",
×
2565
                pszOGRFilter);
2566

2567
      CPLErrorReset();
45✔
2568
      if (OGR_L_SetAttributeFilter(psInfo->hLayer, pszOGRFilter) !=
45✔
2569
          OGRERR_NONE) {
2570
        msSetError(
×
2571
            MS_OGRERR, "SetAttributeFilter() failed on layer %s. Check logs.",
2572
            "msOGRFileWhichShapes()", layer->name ? layer->name : "(null)");
×
2573
        msDebug("SetAttributeFilter(%s) failed on layer %s.\n%s\n",
×
2574
                pszOGRFilter, layer->name ? layer->name : "(null)",
×
2575
                CPLGetLastErrorMsg());
2576
        RELEASE_OGR_LOCK;
×
2577
        msFree(pszOGRFilter);
×
2578
        msFree(select);
×
2579
        return MS_FAILURE;
×
2580
      }
2581
      msFree(pszOGRFilter);
45✔
2582
    } else
2583
      OGR_L_SetAttributeFilter(psInfo->hLayer, NULL);
204✔
2584
  }
2585

2586
  msFree(select);
320✔
2587

2588
  /* ------------------------------------------------------------------
2589
   * Reset current feature pointer
2590
   * ------------------------------------------------------------------ */
2591
  OGR_L_ResetReading(psInfo->hLayer);
320✔
2592
  psInfo->last_record_index_read = -1;
320✔
2593

2594
  RELEASE_OGR_LOCK;
320✔
2595

2596
  return MS_SUCCESS;
2597
}
2598

2599
/**********************************************************************
2600
 *                     msOGRPassThroughFieldDefinitions()
2601
 *
2602
 * Pass the field definitions through to the layer metadata in the
2603
 * "gml_[item]_{type,width,precision}" set of metadata items for
2604
 * defining fields.
2605
 **********************************************************************/
2606

2607
static void msOGRPassThroughFieldDefinitions(layerObj *layer,
367✔
2608
                                             msOGRFileInfo *psInfo)
2609

2610
{
2611
  OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn(psInfo->hLayer);
367✔
2612
  int numitems, i;
2613

2614
  numitems = OGR_FD_GetFieldCount(hDefn);
367✔
2615

2616
  for (i = 0; i < numitems; i++) {
2,821✔
2617
    OGRFieldDefnH hField = OGR_FD_GetFieldDefn(hDefn, i);
2,454✔
2618
    OGRFieldSubType eFieldSubType = OGR_Fld_GetSubType(hField);
2,454✔
2619

2620
    char gml_width[32], gml_precision[32];
2621
    const char *gml_type = NULL;
2622
    const char *item = OGR_Fld_GetNameRef(hField);
2,454✔
2623

2624
    gml_width[0] = '\0';
2,454✔
2625
    gml_precision[0] = '\0';
2,454✔
2626

2627
    switch (OGR_Fld_GetType(hField)) {
2,454✔
2628
    case OFTInteger:
1,223✔
2629
      if (eFieldSubType == OFSTBoolean) {
1,223✔
2630
        gml_type = "Boolean";
2631
      } else {
2632
        gml_type = "Integer";
2633
        if (OGR_Fld_GetWidth(hField) > 0)
1,212✔
2634
          snprintf(gml_width, sizeof(gml_width), "%d",
160✔
2635
                   OGR_Fld_GetWidth(hField));
2636
      }
2637
      break;
2638

2639
    case OFTInteger64:
123✔
2640
      gml_type = "Long";
2641
      if (OGR_Fld_GetWidth(hField) > 0)
123✔
2642
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
80✔
2643
      break;
2644

2645
    case OFTReal:
151✔
2646
      gml_type = "Real";
2647
      if (OGR_Fld_GetWidth(hField) > 0)
151✔
2648
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
80✔
2649
      if (OGR_Fld_GetPrecision(hField) > 0)
151✔
2650
        snprintf(gml_precision, sizeof(gml_precision), "%d",
80✔
2651
                 OGR_Fld_GetPrecision(hField));
2652
      break;
2653

2654
    case OFTString:
906✔
2655
      gml_type = "Character";
2656
      if (OGR_Fld_GetWidth(hField) > 0)
906✔
2657
        snprintf(gml_width, sizeof(gml_width), "%d", OGR_Fld_GetWidth(hField));
526✔
2658
      break;
2659

2660
    case OFTDate:
2661
      gml_type = "Date";
2662
      break;
2663
    case OFTTime:
6✔
2664
      gml_type = "Time";
2665
      break;
6✔
2666
    case OFTDateTime:
28✔
2667
      gml_type = "DateTime";
2668
      break;
28✔
2669

2670
    default:
2671
      gml_type = "Character";
2672
      break;
2673
    }
2674

2675
    msUpdateGMLFieldMetadata(layer, item, gml_type, gml_width, gml_precision,
2,454✔
2676
                             0);
2677
  }
2678

2679
  // FID columns in OGR are 64-bit integers so set type to Long
2680
  const char *exposeFID = msLayerGetProcessingKey(layer, "OGR_EXPOSE_FID");
367✔
2681
  if (exposeFID != NULL && CSLTestBoolean(exposeFID)) {
367✔
2682
    const char *fidColumn = OGR_L_GetFIDColumn(psInfo->hLayer);
11✔
2683
    if (fidColumn != NULL && fidColumn[0] != '\0')
11✔
2684
      msUpdateGMLFieldMetadata(layer, fidColumn, "Long", "", "", 0);
11✔
2685
  }
2686

2687
  /* Should we try to address style items, or other special items? */
2688
}
367✔
2689

2690
/**********************************************************************
2691
 *                     msOGRFileGetItems()
2692
 *
2693
 * Returns a list of field names in a NULL terminated list of strings.
2694
 **********************************************************************/
2695
static char **msOGRFileGetItems(layerObj *layer, msOGRFileInfo *psInfo) {
693✔
2696
  OGRFeatureDefnH hDefn;
2697
  int i, numitems, totalnumitems;
2698
  int numStyleItems = MSOGR_LABELNUMITEMS;
2699
  char **items;
2700
  const char *getShapeStyleItems, *value;
2701

2702
  if ((hDefn = OGR_L_GetLayerDefn(psInfo->hLayer)) == NULL) {
693✔
UNCOV
2703
    msSetError(MS_OGRERR,
×
2704
               "OGR Connection for layer `%s' contains no field definition.",
2705
               "msOGRFileGetItems()", layer->name ? layer->name : "(null)");
×
UNCOV
2706
    return NULL;
×
2707
  }
2708

2709
  const char *fidColumn = OGR_L_GetFIDColumn(psInfo->hLayer);
693✔
2710
  const char *exposeFID = msLayerGetProcessingKey(layer, "OGR_EXPOSE_FID");
693✔
2711
  const bool hasFID = (fidColumn != NULL && fidColumn[0] != '\0') &&
693✔
2712
                      (exposeFID != NULL && CSLTestBoolean(exposeFID));
11✔
2713

2714
  totalnumitems = numitems = OGR_FD_GetFieldCount(hDefn);
693✔
2715

2716
  if (hasFID) {
693✔
2717
    totalnumitems++;
11✔
2718
  }
2719

2720
  getShapeStyleItems = msLayerGetProcessingKey(layer, "GETSHAPE_STYLE_ITEMS");
693✔
2721
  if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all"))
693✔
2722
    totalnumitems += numStyleItems;
×
2723

2724
  if ((items = (char **)malloc(sizeof(char *) * (totalnumitems + 1))) == NULL) {
693✔
2725
    msSetError(MS_MEMERR, NULL, "msOGRFileGetItems()");
×
2726
    return NULL;
×
2727
  }
2728

2729
  for (i = 0; i < numitems; i++) {
6,902✔
2730
    OGRFieldDefnH hField = OGR_FD_GetFieldDefn(hDefn, i);
6,209✔
2731
    items[i] = msStrdup(OGR_Fld_GetNameRef(hField));
6,209✔
2732
  }
2733

2734
  if (hasFID) {
693✔
2735
    items[i++] = msStrdup(fidColumn);
11✔
2736
  }
2737

2738
  if (getShapeStyleItems && EQUAL(getShapeStyleItems, "all")) {
693✔
2739
    assert(numStyleItems == 21);
UNCOV
2740
    items[i++] = msStrdup(MSOGR_LABELFONTNAMENAME);
×
UNCOV
2741
    items[i++] = msStrdup(MSOGR_LABELSIZENAME);
×
UNCOV
2742
    items[i++] = msStrdup(MSOGR_LABELTEXTNAME);
×
UNCOV
2743
    items[i++] = msStrdup(MSOGR_LABELANGLENAME);
×
UNCOV
2744
    items[i++] = msStrdup(MSOGR_LABELFCOLORNAME);
×
UNCOV
2745
    items[i++] = msStrdup(MSOGR_LABELBCOLORNAME);
×
UNCOV
2746
    items[i++] = msStrdup(MSOGR_LABELPLACEMENTNAME);
×
UNCOV
2747
    items[i++] = msStrdup(MSOGR_LABELANCHORNAME);
×
UNCOV
2748
    items[i++] = msStrdup(MSOGR_LABELDXNAME);
×
UNCOV
2749
    items[i++] = msStrdup(MSOGR_LABELDYNAME);
×
UNCOV
2750
    items[i++] = msStrdup(MSOGR_LABELPERPNAME);
×
UNCOV
2751
    items[i++] = msStrdup(MSOGR_LABELBOLDNAME);
×
UNCOV
2752
    items[i++] = msStrdup(MSOGR_LABELITALICNAME);
×
UNCOV
2753
    items[i++] = msStrdup(MSOGR_LABELUNDERLINENAME);
×
UNCOV
2754
    items[i++] = msStrdup(MSOGR_LABELPRIORITYNAME);
×
UNCOV
2755
    items[i++] = msStrdup(MSOGR_LABELSTRIKEOUTNAME);
×
UNCOV
2756
    items[i++] = msStrdup(MSOGR_LABELSTRETCHNAME);
×
UNCOV
2757
    items[i++] = msStrdup(MSOGR_LABELADJHORNAME);
×
UNCOV
2758
    items[i++] = msStrdup(MSOGR_LABELADJVERTNAME);
×
UNCOV
2759
    items[i++] = msStrdup(MSOGR_LABELHCOLORNAME);
×
UNCOV
2760
    items[i++] = msStrdup(MSOGR_LABELOCOLORNAME);
×
2761
  }
2762
  items[i++] = NULL;
693✔
2763

2764
  /* -------------------------------------------------------------------- */
2765
  /*      consider populating the field definitions in metadata.          */
2766
  /* -------------------------------------------------------------------- */
2767
  if ((value = msOWSLookupMetadata(&(layer->metadata), "G", "types")) != NULL &&
693✔
2768
      strcasecmp(value, "auto") == 0)
367✔
2769
    msOGRPassThroughFieldDefinitions(layer, psInfo);
367✔
2770

2771
  return items;
2772
}
2773

2774
/**********************************************************************
2775
 *                     msOGRFileNextShape()
2776
 *
2777
 * Returns shape sequentially from OGR data source.
2778
 * msOGRLayerWhichShape() must have been called first.
2779
 *
2780
 * Returns MS_SUCCESS/MS_FAILURE
2781
 **********************************************************************/
2782
static int msOGRFileNextShape(layerObj *layer, shapeObj *shape,
16,643✔
2783
                              msOGRFileInfo *psInfo) {
2784
  OGRFeatureH hFeature = NULL;
2785

2786
  if (psInfo == NULL || psInfo->hLayer == NULL) {
16,643✔
UNCOV
2787
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2788
               "msOGRFileNextShape()");
2789
    return (MS_FAILURE);
×
2790
  }
2791

2792
  /* ------------------------------------------------------------------
2793
   * Read until we find a feature that matches attribute filter and
2794
   * whose geometry is compatible with current layer type.
2795
   * ------------------------------------------------------------------ */
2796
  msFreeShape(shape);
16,643✔
2797
  shape->type = MS_SHAPE_NULL;
16,643✔
2798

2799
  ACQUIRE_OGR_LOCK;
16,643✔
2800
  while (shape->type == MS_SHAPE_NULL) {
16,649✔
2801
    if (hFeature)
16,649✔
2802
      OGR_F_Destroy(hFeature);
6✔
2803

2804
    if ((hFeature = OGR_L_GetNextFeature(psInfo->hLayer)) == NULL) {
16,649✔
2805
      psInfo->last_record_index_read = -1;
288✔
2806
      if (CPLGetLastErrorType() == CE_Failure) {
288✔
2807
        msSetError(MS_OGRERR, "OGR GetNextFeature() error'd. Check logs.",
×
2808
                   "msOGRFileNextShape()");
2809
        msDebug("msOGRFileNextShape(): %s\n", CPLGetLastErrorMsg());
×
UNCOV
2810
        RELEASE_OGR_LOCK;
×
UNCOV
2811
        return MS_FAILURE;
×
2812
      } else {
2813
        RELEASE_OGR_LOCK;
288✔
2814
        if (layer->debug >= MS_DEBUGLEVEL_VV)
288✔
2815
          msDebug("msOGRFileNextShape: Returning MS_DONE (no more shapes)\n");
2✔
2816
        return MS_DONE; // No more features to read
288✔
2817
      }
2818
    }
2819

2820
    psInfo->last_record_index_read++;
16,361✔
2821

2822
    if (layer->numitems > 0) {
16,361✔
2823
      if (shape->values)
8,534✔
UNCOV
2824
        msFreeCharArray(shape->values, shape->numvalues);
×
2825
      shape->values = msOGRGetValues(layer, hFeature);
8,534✔
2826
      shape->numvalues = layer->numitems;
8,534✔
2827
      if (!shape->values) {
8,534✔
UNCOV
2828
        OGR_F_Destroy(hFeature);
×
2829
        RELEASE_OGR_LOCK;
×
UNCOV
2830
        return (MS_FAILURE);
×
2831
      }
2832
    }
2833

2834
    // Feature matched filter expression... process geometry
2835
    // shape->type will be set if geom is compatible with layer type
2836
    if (ogrConvertGeometry(ogrGetLinearGeometry(hFeature), shape,
16,361✔
2837
                           layer->type) == MS_SUCCESS) {
2838
      if (shape->type != MS_SHAPE_NULL)
16,361✔
2839
        break; // Shape is ready to be returned!
2840

2841
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
6✔
UNCOV
2842
        msDebug("msOGRFileNextShape: Rejecting feature (shapeid = " CPL_FRMT_GIB
×
2843
                ", tileid=%d) of incompatible type for this layer (feature "
2844
                "wkbType %d, layer type %d)\n",
2845
                (GIntBig)OGR_F_GetFID(hFeature), psInfo->nTileId,
UNCOV
2846
                OGR_F_GetGeometryRef(hFeature) == NULL
×
UNCOV
2847
                    ? wkbFlatten(wkbUnknown)
×
UNCOV
2848
                    : wkbFlatten(OGR_G_GetGeometryType(
×
2849
                          OGR_F_GetGeometryRef(hFeature))),
UNCOV
2850
                layer->type);
×
2851

2852
    } else {
UNCOV
2853
      msFreeShape(shape);
×
UNCOV
2854
      OGR_F_Destroy(hFeature);
×
UNCOV
2855
      RELEASE_OGR_LOCK;
×
UNCOV
2856
      return MS_FAILURE; // Error message already produced.
×
2857
    }
2858

2859
    // Feature rejected... free shape to clear attributes values.
2860
    msFreeShape(shape);
6✔
2861
    shape->type = MS_SHAPE_NULL;
6✔
2862
  }
2863

2864
  shape->index = (int)OGR_F_GetFID(
16,355✔
2865
      hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
2866
  shape->resultindex = psInfo->last_record_index_read;
16,355✔
2867
  shape->tileindex = psInfo->nTileId;
16,355✔
2868

2869
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
16,355✔
2870
    msDebug("msOGRFileNextShape: Returning shape=%ld, tile=%d\n", shape->index,
18✔
2871
            shape->tileindex);
2872

2873
  // Keep ref. to last feature read in case we need style info.
2874
  if (psInfo->hLastFeature)
16,355✔
2875
    OGR_F_Destroy(psInfo->hLastFeature);
16,052✔
2876
  psInfo->hLastFeature = hFeature;
16,355✔
2877

2878
  RELEASE_OGR_LOCK;
16,355✔
2879

2880
  return MS_SUCCESS;
16,355✔
2881
}
2882

2883
/**********************************************************************
2884
 *                     msOGRFileGetShape()
2885
 *
2886
 * Returns shape from OGR data source by id.
2887
 *
2888
 * Returns MS_SUCCESS/MS_FAILURE
2889
 **********************************************************************/
2890
static int msOGRFileGetShape(layerObj *layer, shapeObj *shape, long record,
1,334✔
2891
                             msOGRFileInfo *psInfo, int record_is_fid) {
2892
  OGRFeatureH hFeature;
2893

2894
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,334✔
UNCOV
2895
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
2896
               "msOGRFileNextShape()");
UNCOV
2897
    return (MS_FAILURE);
×
2898
  }
2899

2900
  /* -------------------------------------------------------------------- */
2901
  /*      Clear previously loaded shape.                                  */
2902
  /* -------------------------------------------------------------------- */
2903
  msFreeShape(shape);
1,334✔
2904
  shape->type = MS_SHAPE_NULL;
1,334✔
2905

2906
  /* -------------------------------------------------------------------- */
2907
  /*      Support reading feature by fid.                                 */
2908
  /* -------------------------------------------------------------------- */
2909
  if (record_is_fid) {
1,334✔
2910
    ACQUIRE_OGR_LOCK;
2✔
2911
    if ((hFeature = OGR_L_GetFeature(psInfo->hLayer, record)) == NULL) {
2✔
UNCOV
2912
      RELEASE_OGR_LOCK;
×
UNCOV
2913
      return MS_FAILURE;
×
2914
    }
2915
  }
2916

2917
  /* -------------------------------------------------------------------- */
2918
  /*      Support reading shape by offset within the current              */
2919
  /*      resultset.                                                      */
2920
  /* -------------------------------------------------------------------- */
2921
  else {
2922
    ACQUIRE_OGR_LOCK;
1,332✔
2923
    if (record <= psInfo->last_record_index_read ||
1,332✔
2924
        psInfo->last_record_index_read == -1) {
2925
      OGR_L_ResetReading(psInfo->hLayer);
213✔
2926
      psInfo->last_record_index_read = -1;
213✔
2927
    }
2928

2929
    hFeature = NULL;
2930
    while (psInfo->last_record_index_read < record) {
3,229✔
2931
      if (hFeature != NULL) {
1,897✔
2932
        OGR_F_Destroy(hFeature);
565✔
2933
        hFeature = NULL;
2934
      }
2935
      if ((hFeature = OGR_L_GetNextFeature(psInfo->hLayer)) == NULL) {
1,897✔
2936
        RELEASE_OGR_LOCK;
×
UNCOV
2937
        return MS_FAILURE;
×
2938
      }
2939
      psInfo->last_record_index_read++;
1,897✔
2940
    }
2941
  }
2942

2943
  /* ------------------------------------------------------------------
2944
   * Handle shape geometry...
2945
   * ------------------------------------------------------------------ */
2946
  // shape->type will be set if geom is compatible with layer type
2947
  if (ogrConvertGeometry(ogrGetLinearGeometry(hFeature), shape, layer->type) !=
1,334✔
2948
      MS_SUCCESS) {
UNCOV
2949
    RELEASE_OGR_LOCK;
×
UNCOV
2950
    return MS_FAILURE; // Error message already produced.
×
2951
  }
2952

2953
  if (shape->type == MS_SHAPE_NULL) {
1,334✔
UNCOV
2954
    msSetError(MS_OGRERR, "Requested feature is incompatible with layer type",
×
2955
               "msOGRLayerGetShape()");
UNCOV
2956
    RELEASE_OGR_LOCK;
×
UNCOV
2957
    return MS_FAILURE;
×
2958
  }
2959

2960
  /* ------------------------------------------------------------------
2961
   * Process shape attributes
2962
   * ------------------------------------------------------------------ */
2963
  if (layer->numitems > 0) {
1,334✔
2964
    shape->values = msOGRGetValues(layer, hFeature);
1,334✔
2965
    shape->numvalues = layer->numitems;
1,334✔
2966
    if (!shape->values) {
1,334✔
UNCOV
2967
      RELEASE_OGR_LOCK;
×
UNCOV
2968
      return (MS_FAILURE);
×
2969
    }
2970
  }
2971

2972
  if (record_is_fid) {
1,334✔
2973
    shape->index = record;
2✔
2974
    shape->resultindex = -1;
2✔
2975
  } else {
2976
    shape->index = (int)OGR_F_GetFID(
1,332✔
2977
        hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
2978
    shape->resultindex = record;
1,332✔
2979
  }
2980

2981
  shape->tileindex = psInfo->nTileId;
1,334✔
2982

2983
  // Keep ref. to last feature read in case we need style info.
2984
  if (psInfo->hLastFeature)
1,334✔
2985
    OGR_F_Destroy(psInfo->hLastFeature);
1,329✔
2986
  psInfo->hLastFeature = hFeature;
1,334✔
2987

2988
  RELEASE_OGR_LOCK;
1,334✔
2989

2990
  return MS_SUCCESS;
1,334✔
2991
}
2992

2993
/************************************************************************/
2994
/*                         msOGRFileReadTile()                          */
2995
/*                                                                      */
2996
/*      Advance to the next tile (or if targetTile is not -1 advance    */
2997
/*      to that tile), causing the tile to become the poCurTile in      */
2998
/*      the tileindexes psInfo structure.  Returns MS_DONE if there     */
2999
/*      are no more available tiles.                                    */
3000
/*                                                                      */
3001
/*      Newly loaded tiles are automatically "WhichShaped" based on     */
3002
/*      the current rectangle.                                          */
3003
/************************************************************************/
3004

3005
int msOGRFileReadTile(layerObj *layer, msOGRFileInfo *psInfo,
13✔
3006
                      int targetTile = -1)
3007

3008
{
3009
  int nFeatureId;
3010

3011
  /* -------------------------------------------------------------------- */
3012
  /*      Close old tile if one is open.                                  */
3013
  /* -------------------------------------------------------------------- */
3014
  if (psInfo->poCurTile != NULL) {
13✔
3015
    msOGRFileClose(layer, psInfo->poCurTile);
6✔
3016
    psInfo->poCurTile = NULL;
6✔
3017
  }
3018

3019
  /* -------------------------------------------------------------------- */
3020
  /*      If -2 is passed, then seek reset reading of the tileindex.      */
3021
  /*      We want to start from the beginning even if this file is        */
3022
  /*      shared between layers or renders.                               */
3023
  /* -------------------------------------------------------------------- */
3024
  ACQUIRE_OGR_LOCK;
13✔
3025
  if (targetTile == -2) {
13✔
UNCOV
3026
    OGR_L_ResetReading(psInfo->hLayer);
×
3027
  }
3028

3029
  /* -------------------------------------------------------------------- */
3030
  /*      Get the name (connection string really) of the next tile.       */
3031
  /* -------------------------------------------------------------------- */
3032
  OGRFeatureH hFeature;
3033
  char *connection = NULL;
3034
  msOGRFileInfo *psTileInfo = NULL;
3035
  int status;
3036

3037
#ifndef IGNORE_MISSING_DATA
3038
NextFile:
13✔
3039
#endif
3040

3041
  if (targetTile < 0)
13✔
3042
    hFeature = OGR_L_GetNextFeature(psInfo->hLayer);
10✔
3043

3044
  else
3045
    hFeature = OGR_L_GetFeature(psInfo->hLayer, targetTile);
3✔
3046

3047
  if (hFeature == NULL) {
13✔
3048
    RELEASE_OGR_LOCK;
2✔
3049
    if (targetTile == -1)
2✔
3050
      return MS_DONE;
3051
    else
3052
      return MS_FAILURE;
3053
  }
3054

3055
  connection = msStrdup(OGR_F_GetFieldAsString(hFeature, layer->tileitemindex));
11✔
3056

3057
  char *pszSRS = NULL;
3058
  if (layer->tilesrs != NULL) {
11✔
3059
    int idx = OGR_F_GetFieldIndex(hFeature, layer->tilesrs);
6✔
3060
    if (idx >= 0) {
6✔
3061
      pszSRS = msStrdup(OGR_F_GetFieldAsString(hFeature, idx));
6✔
3062
    }
3063
  }
3064

3065
  nFeatureId = (int)OGR_F_GetFID(
11✔
3066
      hFeature); // FIXME? GetFID() is a 64bit integer in GDAL 2.0
3067

3068
  OGR_F_Destroy(hFeature);
11✔
3069

3070
  RELEASE_OGR_LOCK;
11✔
3071

3072
  /* -------------------------------------------------------------------- */
3073
  /*      Open the new tile file.                                         */
3074
  /* -------------------------------------------------------------------- */
3075
  psTileInfo = msOGRFileOpen(layer, connection);
11✔
3076

3077
  free(connection);
11✔
3078

3079
#ifndef IGNORE_MISSING_DATA
3080
  if (psTileInfo == NULL && targetTile == -1) {
11✔
UNCOV
3081
    msFree(pszSRS);
×
UNCOV
3082
    goto NextFile;
×
3083
  }
3084
#endif
3085

3086
  if (psTileInfo == NULL) {
11✔
UNCOV
3087
    msFree(pszSRS);
×
UNCOV
3088
    return MS_FAILURE;
×
3089
  }
3090

3091
  if (pszSRS != NULL) {
11✔
3092
    if (msOGCWKT2ProjectionObj(pszSRS, &(psInfo->sTileProj), layer->debug) !=
6✔
3093
        MS_SUCCESS) {
UNCOV
3094
      msFree(pszSRS);
×
UNCOV
3095
      return MS_FAILURE;
×
3096
    }
3097
    msFree(pszSRS);
6✔
3098
  }
3099

3100
  psTileInfo->nTileId = nFeatureId;
11✔
3101

3102
  /* -------------------------------------------------------------------- */
3103
  /*      Initialize the spatial query on this file.                      */
3104
  /* -------------------------------------------------------------------- */
3105
  if (psInfo->rect.minx != 0 || psInfo->rect.maxx != 0) {
11✔
3106
    rectObj rect = psInfo->rect;
6✔
3107

3108
    if (layer->tileindex != NULL && psInfo->sTileProj.numargs > 0) {
6✔
3109
      msProjectRect(&(layer->projection), &(psInfo->sTileProj), &rect);
4✔
3110
    }
3111

3112
    status = msOGRFileWhichShapes(layer, rect, psTileInfo);
6✔
3113
    if (status != MS_SUCCESS)
6✔
UNCOV
3114
      return status;
×
3115
  }
3116

3117
  psInfo->poCurTile = psTileInfo;
11✔
3118

3119
  /* -------------------------------------------------------------------- */
3120
  /*      Update the iteminfo in case this layer has a different field    */
3121
  /*      list.                                                           */
3122
  /* -------------------------------------------------------------------- */
3123
  msOGRLayerInitItemInfo(layer);
11✔
3124

3125
  return MS_SUCCESS;
11✔
3126
}
3127

3128
/************************************************************************/
3129
/*                               msExprNode                             */
3130
/************************************************************************/
3131

3132
class msExprNode {
1,534✔
3133
public:
3134
  std::vector<std::unique_ptr<msExprNode>> m_aoChildren{};
3135
  int m_nToken = 0;
3136
  std::string m_osVal{};
3137
  double m_dfVal = 0;
3138
  struct tm m_tmVal {};
3139
};
3140

3141
/************************************************************************/
3142
/*                        exprGetPriority()                             */
3143
/************************************************************************/
3144

3145
static int exprGetPriority(int token) {
68✔
3146
  if (token == MS_TOKEN_LOGICAL_NOT)
3147
    return 9;
3148
  else if (token == '*' || token == '/' || token == '%')
UNCOV
3149
    return 8;
×
3150
  else if (token == '+' || token == '-')
UNCOV
3151
    return 7;
×
3152
  else if (token == MS_TOKEN_COMPARISON_GE || token == MS_TOKEN_COMPARISON_GT ||
3153
           token == MS_TOKEN_COMPARISON_LE || token == MS_TOKEN_COMPARISON_LT ||
56✔
3154
           token == MS_TOKEN_COMPARISON_IN)
3155
    return 6;
24✔
3156
  else if (token == MS_TOKEN_COMPARISON_EQ ||
3157
           token == MS_TOKEN_COMPARISON_IEQ ||
3158
           token == MS_TOKEN_COMPARISON_RE ||
3159
           token == MS_TOKEN_COMPARISON_IRE || token == MS_TOKEN_COMPARISON_NE)
3160
    return 5;
3161
  else if (token == MS_TOKEN_LOGICAL_AND)
3162
    return 4;
30✔
3163
  else if (token == MS_TOKEN_LOGICAL_OR)
3164
    return 3;
7✔
3165
  else
UNCOV
3166
    return 0;
×
3167
}
3168

3169
/************************************************************************/
3170
/*                           BuildExprTree()                            */
3171
/************************************************************************/
3172

3173
static std::unique_ptr<msExprNode> BuildExprTree(tokenListNodeObjPtr node,
444✔
3174
                                                 tokenListNodeObjPtr *pNodeNext,
3175
                                                 int nParenthesisLevel) {
3176
  std::vector<std::unique_ptr<msExprNode>> aoStackOp, aoStackVal;
3177
  while (node != NULL) {
1,164✔
3178
    if (node->token == '(') {
888✔
3179
      auto subExpr = BuildExprTree(node->next, &node, nParenthesisLevel + 1);
136✔
3180
      if (subExpr == NULL) {
136✔
3181
        return nullptr;
3182
      }
3183
      aoStackVal.emplace_back(std::move(subExpr));
136✔
3184
      continue;
3185
    } else if (node->token == ')') {
3186
      if (nParenthesisLevel > 0) {
168✔
3187
        break;
3188
      }
3189
      return nullptr;
3190
    } else if (node->token == '+' || node->token == '-' || node->token == '*' ||
3191
               node->token == '/' || node->token == '%' ||
3192
               node->token == MS_TOKEN_LOGICAL_NOT ||
3193
               node->token == MS_TOKEN_LOGICAL_AND ||
3194
               node->token == MS_TOKEN_LOGICAL_OR ||
3195
               node->token == MS_TOKEN_COMPARISON_GE ||
3196
               node->token == MS_TOKEN_COMPARISON_GT ||
3197
               node->token == MS_TOKEN_COMPARISON_LE ||
3198
               node->token == MS_TOKEN_COMPARISON_LT ||
3199
               node->token == MS_TOKEN_COMPARISON_EQ ||
3200
               node->token == MS_TOKEN_COMPARISON_IEQ ||
3201
               node->token == MS_TOKEN_COMPARISON_NE ||
3202
               node->token == MS_TOKEN_COMPARISON_RE ||
3203
               node->token == MS_TOKEN_COMPARISON_IRE ||
3204
               node->token == MS_TOKEN_COMPARISON_IN) {
3205
      while (!aoStackOp.empty() &&
210✔
3206
             exprGetPriority(node->token) <=
34✔
3207
                 exprGetPriority(aoStackOp.back()->m_nToken)) {
34✔
3208
        std::unique_ptr<msExprNode> val1;
22✔
3209
        std::unique_ptr<msExprNode> val2;
22✔
3210
        if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT) {
22✔
3211
          if (aoStackVal.empty())
22✔
3212
            return nullptr;
3213
          val2.reset(aoStackVal.back().release());
3214
          aoStackVal.pop_back();
3215
        }
3216
        if (aoStackVal.empty())
22✔
3217
          return nullptr;
3218
        val1.reset(aoStackVal.back().release());
3219
        aoStackVal.pop_back();
3220

3221
        std::unique_ptr<msExprNode> newNode(new msExprNode);
22✔
3222
        newNode->m_nToken = aoStackOp.back()->m_nToken;
22✔
3223
        newNode->m_aoChildren.emplace_back(std::move(val1));
22✔
3224
        if (val2)
22✔
3225
          newNode->m_aoChildren.emplace_back(std::move(val2));
22✔
3226
        aoStackVal.emplace_back(std::move(newNode));
22✔
3227
        aoStackOp.pop_back();
3228
      }
3229

3230
      std::unique_ptr<msExprNode> newNode(new msExprNode);
188✔
3231
      newNode->m_nToken = node->token;
188✔
3232
      aoStackOp.emplace_back(std::move(newNode));
188✔
3233
    } else if (node->token == ',') {
188✔
3234
    } else if (node->token == MS_TOKEN_COMPARISON_INTERSECTS ||
3235
               node->token == MS_TOKEN_COMPARISON_DISJOINT ||
3236
               node->token == MS_TOKEN_COMPARISON_TOUCHES ||
3237
               node->token == MS_TOKEN_COMPARISON_OVERLAPS ||
359✔
3238
               node->token == MS_TOKEN_COMPARISON_CROSSES ||
3239
               node->token == MS_TOKEN_COMPARISON_DWITHIN ||
3240
               node->token == MS_TOKEN_COMPARISON_BEYOND ||
3241
               node->token == MS_TOKEN_COMPARISON_WITHIN ||
3242
               node->token == MS_TOKEN_COMPARISON_CONTAINS ||
3243
               node->token == MS_TOKEN_COMPARISON_EQUALS ||
3244
               node->token == MS_TOKEN_FUNCTION_LENGTH ||
3245
               node->token == MS_TOKEN_FUNCTION_TOSTRING ||
3246
               node->token == MS_TOKEN_FUNCTION_COMMIFY ||
3247
               node->token == MS_TOKEN_FUNCTION_AREA ||
3248
               node->token == MS_TOKEN_FUNCTION_ROUND ||
3249
               node->token == MS_TOKEN_FUNCTION_FROMTEXT ||
3250
               node->token == MS_TOKEN_FUNCTION_BUFFER ||
3251
               node->token == MS_TOKEN_FUNCTION_DIFFERENCE ||
3252
               node->token == MS_TOKEN_FUNCTION_SIMPLIFY ||
3253
               node->token == MS_TOKEN_FUNCTION_SIMPLIFYPT ||
3254
               node->token == MS_TOKEN_FUNCTION_GENERALIZE ||
3255
               node->token == MS_TOKEN_FUNCTION_SMOOTHSIA ||
3256
               node->token == MS_TOKEN_FUNCTION_JAVASCRIPT ||
3257
               node->token == MS_TOKEN_FUNCTION_UPPER ||
3258
               node->token == MS_TOKEN_FUNCTION_LOWER ||
3259
               node->token == MS_TOKEN_FUNCTION_INITCAP ||
3260
               node->token == MS_TOKEN_FUNCTION_FIRSTCAP) {
3261
      if (node->next && node->next->token == '(') {
32✔
3262
        int node_token = node->token;
3263
        auto subExpr =
3264
            BuildExprTree(node->next->next, &node, nParenthesisLevel + 1);
32✔
3265
        if (subExpr == NULL) {
32✔
3266
          return nullptr;
3267
        }
3268
        std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3269
        newNode->m_nToken = node_token;
32✔
3270
        if (subExpr->m_nToken == 0) {
32✔
3271
          newNode->m_aoChildren = std::move(subExpr->m_aoChildren);
32✔
3272
        } else {
UNCOV
3273
          newNode->m_aoChildren.emplace_back(std::move(subExpr));
×
3274
        }
3275
        aoStackVal.emplace_back(std::move(newNode));
32✔
3276
        continue;
3277
      } else
3278
        return nullptr;
3279
    } else if (node->token == MS_TOKEN_LITERAL_NUMBER ||
3280
               node->token == MS_TOKEN_LITERAL_BOOLEAN) {
3281
      std::unique_ptr<msExprNode> newNode(new msExprNode);
76✔
3282
      newNode->m_nToken = node->token;
76✔
3283
      newNode->m_dfVal = node->tokenval.dblval;
76✔
3284
      aoStackVal.emplace_back(std::move(newNode));
76✔
3285
    } else if (node->token == MS_TOKEN_LITERAL_STRING) {
76✔
3286
      std::unique_ptr<msExprNode> newNode(new msExprNode);
62✔
3287
      newNode->m_nToken = node->token;
62✔
3288
      newNode->m_osVal = node->tokenval.strval;
62✔
3289
      aoStackVal.emplace_back(std::move(newNode));
62✔
3290
    } else if (node->token == MS_TOKEN_LITERAL_TIME) {
3291
      std::unique_ptr<msExprNode> newNode(new msExprNode);
12✔
3292
      newNode->m_nToken = node->token;
12✔
3293
      newNode->m_tmVal = node->tokenval.tmval;
12✔
3294
      aoStackVal.emplace_back(std::move(newNode));
12✔
3295
    } else if (node->token == MS_TOKEN_LITERAL_SHAPE) {
3296
      std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3297
      newNode->m_nToken = node->token;
32✔
3298
      char *wkt = msShapeToWKT(node->tokenval.shpval);
32✔
3299
      newNode->m_osVal = wkt;
32✔
3300
      msFree(wkt);
32✔
3301
      aoStackVal.emplace_back(std::move(newNode));
32✔
3302
    } else if (node->token == MS_TOKEN_BINDING_DOUBLE ||
3303
               node->token == MS_TOKEN_BINDING_INTEGER ||
3304
               node->token == MS_TOKEN_BINDING_STRING ||
3305
               node->token == MS_TOKEN_BINDING_TIME) {
3306
      std::unique_ptr<msExprNode> newNode(new msExprNode);
113✔
3307
      newNode->m_nToken = node->token;
113✔
3308
      newNode->m_osVal = node->tokenval.bindval.item;
113✔
3309
      aoStackVal.emplace_back(std::move(newNode));
113✔
3310
    } else {
3311
      std::unique_ptr<msExprNode> newNode(new msExprNode);
32✔
3312
      newNode->m_nToken = node->token;
32✔
3313
      aoStackVal.emplace_back(std::move(newNode));
32✔
3314
    }
3315

3316
    node = node->next;
552✔
3317
  }
3318

3319
  while (!aoStackOp.empty()) {
610✔
3320
    std::unique_ptr<msExprNode> val1 = NULL;
3321
    std::unique_ptr<msExprNode> val2 = NULL;
3322
    if (aoStackOp.back()->m_nToken != MS_TOKEN_LOGICAL_NOT) {
166✔
3323
      if (aoStackVal.empty())
161✔
3324
        return nullptr;
3325
      val2.reset(aoStackVal.back().release());
3326
      aoStackVal.pop_back();
3327
    }
3328
    if (aoStackVal.empty())
166✔
3329
      return nullptr;
3330
    val1.reset(aoStackVal.back().release());
3331
    aoStackVal.pop_back();
3332

3333
    std::unique_ptr<msExprNode> newNode(new msExprNode);
166✔
3334
    newNode->m_nToken = aoStackOp.back()->m_nToken;
166✔
3335
    newNode->m_aoChildren.emplace_back(std::move(val1));
166✔
3336
    if (val2)
166✔
3337
      newNode->m_aoChildren.emplace_back(std::move(val2));
161✔
3338
    aoStackVal.emplace_back(std::move(newNode));
166✔
3339
    aoStackOp.pop_back();
3340
  }
3341

3342
  std::unique_ptr<msExprNode> poRet;
444✔
3343
  if (aoStackVal.size() == 1)
444✔
3344
    poRet.reset(aoStackVal.back().release());
3345
  else if (aoStackVal.size() > 1) {
201✔
3346
    poRet.reset(new msExprNode);
32✔
3347
    poRet->m_aoChildren = std::move(aoStackVal);
32✔
3348
  }
3349

3350
  if (pNodeNext)
444✔
3351
    *pNodeNext = node ? node->next : NULL;
168✔
3352

3353
  return poRet;
3354
}
444✔
3355

3356
/**********************************************************************
3357
 *                 msOGRExtractTopSpatialFilter()
3358
 *
3359
 * Recognize expressions like "Intersects([shape], wkt) == TRUE [AND ....]"
3360
 **********************************************************************/
3361
static int msOGRExtractTopSpatialFilter(msOGRFileInfo *info,
149✔
3362
                                        const msExprNode *expr,
3363
                                        const msExprNode **pSpatialFilterNode) {
3364
  if (expr == NULL)
180✔
3365
    return MS_FALSE;
3366

3367
  if (expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
66✔
3368
      expr->m_aoChildren.size() == 2 &&
66✔
3369
      expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_BOOLEAN &&
246✔
3370
      expr->m_aoChildren[1]->m_dfVal == 1.0) {
31✔
3371
    return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0].get(),
3372
                                        pSpatialFilterNode);
31✔
3373
  }
3374

3375
  if ((((expr->m_nToken == MS_TOKEN_COMPARISON_INTERSECTS ||
138✔
3376
         expr->m_nToken == MS_TOKEN_COMPARISON_OVERLAPS ||
136✔
3377
         expr->m_nToken == MS_TOKEN_COMPARISON_CROSSES ||
134✔
3378
         expr->m_nToken == MS_TOKEN_COMPARISON_WITHIN ||
132✔
3379
         expr->m_nToken == MS_TOKEN_COMPARISON_CONTAINS) &&
19✔
3380
        expr->m_aoChildren.size() == 2) ||
130✔
3381
       (expr->m_nToken == MS_TOKEN_COMPARISON_DWITHIN &&
3✔
3382
        expr->m_aoChildren.size() == 3)) &&
149✔
3383
      expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_SHAPE) {
22✔
3384
    if (info->rect_is_defined) {
22✔
3385
      // Several intersects...
UNCOV
3386
      *pSpatialFilterNode = NULL;
×
UNCOV
3387
      info->rect_is_defined = MS_FALSE;
×
UNCOV
3388
      return MS_FALSE;
×
3389
    }
3390
    OGRGeometryH hSpatialFilter = NULL;
22✔
3391
    char *wkt = const_cast<char *>(expr->m_aoChildren[1]->m_osVal.c_str());
22✔
3392
    OGRErr e = OGR_G_CreateFromWkt(&wkt, NULL, &hSpatialFilter);
22✔
3393
    if (e == OGRERR_NONE) {
22✔
3394
      OGREnvelope env;
3395
      if (expr->m_nToken == MS_TOKEN_COMPARISON_DWITHIN) {
22✔
3396
        OGRGeometryH hBuffer =
3397
            OGR_G_Buffer(hSpatialFilter, expr->m_aoChildren[2]->m_dfVal, 30);
3✔
3398
        OGR_G_GetEnvelope(hBuffer ? hBuffer : hSpatialFilter, &env);
3✔
3399
        OGR_G_DestroyGeometry(hBuffer);
3✔
3400
      } else {
3401
        OGR_G_GetEnvelope(hSpatialFilter, &env);
19✔
3402
      }
3403
      info->rect.minx = env.MinX;
22✔
3404
      info->rect.miny = env.MinY;
22✔
3405
      info->rect.maxx = env.MaxX;
22✔
3406
      info->rect.maxy = env.MaxY;
22✔
3407
      info->rect_is_defined = true;
22✔
3408
      *pSpatialFilterNode = expr;
22✔
3409
      OGR_G_DestroyGeometry(hSpatialFilter);
22✔
3410
      return MS_TRUE;
3411
    }
3412
    return MS_FALSE;
3413
  }
3414

3415
  if (expr->m_nToken == MS_TOKEN_LOGICAL_AND &&
127✔
3416
      expr->m_aoChildren.size() == 2) {
3417
    return msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[0].get(),
21✔
3418
                                        pSpatialFilterNode) &&
42✔
3419
           msOGRExtractTopSpatialFilter(info, expr->m_aoChildren[1].get(),
21✔
3420
                                        pSpatialFilterNode);
21✔
3421
  }
3422

3423
  return MS_TRUE;
3424
}
3425

3426
/**********************************************************************
3427
 *                 msOGRTranslatePartialMSExpressionToOGRSQL()
3428
 *
3429
 * Tries to partially translate a mapserver expression to SQL
3430
 **********************************************************************/
3431

3432
static std::string msOGRGetTokenText(int nToken) {
55✔
3433
  switch (nToken) {
55✔
3434
  case '*':
3435
  case '+':
3436
  case '-':
3437
  case '/':
3438
  case '%':
3439
    return std::string(1, static_cast<char>(nToken));
3440

3441
  case MS_TOKEN_COMPARISON_GE:
3442
    return ">=";
5✔
3443
  case MS_TOKEN_COMPARISON_GT:
3444
    return ">";
1✔
3445
  case MS_TOKEN_COMPARISON_LE:
3446
    return "<=";
5✔
3447
  case MS_TOKEN_COMPARISON_LT:
3448
    return "<";
1✔
3449
  case MS_TOKEN_COMPARISON_EQ:
3450
    return "=";
42✔
3451
  case MS_TOKEN_COMPARISON_NE:
3452
    return "!=";
1✔
3453

3454
  default:
3455
    return std::string();
3456
  }
3457
}
3458

3459
static std::string
3460
msOGRTranslatePartialInternal(layerObj *layer, const msExprNode *expr,
285✔
3461
                              const msExprNode *spatialFilterNode,
3462
                              bool &bPartialFilter) {
3463
  switch (expr->m_nToken) {
285✔
3464
  case MS_TOKEN_LOGICAL_NOT: {
4✔
3465
    std::string osTmp(msOGRTranslatePartialInternal(
3466
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
4✔
3467
    if (osTmp.empty())
4✔
3468
      return std::string();
3469
    return "(NOT " + osTmp + ")";
8✔
3470
  }
3471

3472
  case MS_TOKEN_LOGICAL_AND: {
17✔
3473
    // We can deal with partially translated children
3474
    std::string osTmp1(msOGRTranslatePartialInternal(
3475
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
17✔
3476
    std::string osTmp2(msOGRTranslatePartialInternal(
3477
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
17✔
3478
    if (!osTmp1.empty() && !osTmp2.empty()) {
17✔
3479
      return "(" + osTmp1 + " AND " + osTmp2 + ")";
18✔
3480
    } else if (!osTmp1.empty())
8✔
UNCOV
3481
      return osTmp1;
×
3482
    else
3483
      return osTmp2;
8✔
3484
  }
3485

3486
  case MS_TOKEN_LOGICAL_OR: {
9✔
3487
    // We can NOT deal with partially translated children
3488
    std::string osTmp1(msOGRTranslatePartialInternal(
3489
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
9✔
3490
    std::string osTmp2(msOGRTranslatePartialInternal(
3491
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
9✔
3492
    if (!osTmp1.empty() && !osTmp2.empty()) {
9✔
3493
      return "(" + osTmp1 + " OR " + osTmp2 + ")";
16✔
3494
    } else
3495
      return std::string();
3496
  }
3497

3498
  case '*':
82✔
3499
  case '+':
3500
  case '-':
3501
  case '/':
3502
  case '%':
3503
  case MS_TOKEN_COMPARISON_GE:
3504
  case MS_TOKEN_COMPARISON_GT:
3505
  case MS_TOKEN_COMPARISON_LE:
3506
  case MS_TOKEN_COMPARISON_LT:
3507
  case MS_TOKEN_COMPARISON_EQ:
3508
  case MS_TOKEN_COMPARISON_NE: {
3509
    std::string osTmp1(msOGRTranslatePartialInternal(
3510
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
82✔
3511
    std::string osTmp2(msOGRTranslatePartialInternal(
3512
        layer, expr->m_aoChildren[1].get(), spatialFilterNode, bPartialFilter));
82✔
3513
    if (!osTmp1.empty() && !osTmp2.empty()) {
82✔
3514
      if (expr->m_nToken == MS_TOKEN_COMPARISON_EQ &&
55✔
3515
          osTmp2 == "'_MAPSERVER_NULL_'") {
42✔
UNCOV
3516
        return "(" + osTmp1 + " IS NULL )";
×
3517
      }
3518
      if (expr->m_aoChildren[1]->m_nToken == MS_TOKEN_LITERAL_STRING) {
55✔
3519
        bool bIsCharacter = msLayerPropertyIsCharacter(
32✔
3520
            layer, expr->m_aoChildren[0]->m_osVal.c_str());
3521
        // Cast if needed (or unsure)
3522
        if (!bIsCharacter) {
32✔
3523
          osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
87✔
3524
        }
3525
      }
3526
      return "(" + osTmp1 + " " + msOGRGetTokenText(expr->m_nToken) + " " +
165✔
3527
             osTmp2 + ")";
3528
    } else
3529
      return std::string();
3530
  }
3531

3532
  case MS_TOKEN_COMPARISON_RE: {
4✔
3533
    std::string osTmp1(msOGRTranslatePartialInternal(
3534
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
4✔
3535
    if (expr->m_aoChildren[1]->m_nToken != MS_TOKEN_LITERAL_STRING) {
4✔
3536
      return std::string();
3537
    }
3538
    std::string osRE("'");
4✔
3539
    const size_t nSize = expr->m_aoChildren[1]->m_osVal.size();
3540
    bool bHasUsedEscape = false;
3541
    for (size_t i = 0; i < nSize; i++) {
18✔
3542
      if (i == 0 && expr->m_aoChildren[1]->m_osVal[i] == '^')
18✔
3543
        continue;
4✔
3544
      if (i == nSize - 1 && expr->m_aoChildren[1]->m_osVal[i] == '$')
14✔
3545
        break;
3546
      if (expr->m_aoChildren[1]->m_osVal[i] == '.') {
10✔
3547
        if (i + 1 < nSize && expr->m_aoChildren[1]->m_osVal[i + 1] == '*') {
3✔
3548
          osRE += "%";
3549
          i++;
3550
        } else {
3551
          osRE += "_";
3552
        }
3553
      } else if (expr->m_aoChildren[1]->m_osVal[i] == '\\' && i + 1 < nSize) {
7✔
3554
        bHasUsedEscape = true;
3555
        osRE += 'X';
UNCOV
3556
        osRE += expr->m_aoChildren[1]->m_osVal[i + 1];
×
3557
        i++;
3558
      } else if (expr->m_aoChildren[1]->m_osVal[i] == 'X' ||
3559
                 expr->m_aoChildren[1]->m_osVal[i] == '%' ||
3560
                 expr->m_aoChildren[1]->m_osVal[i] == '_') {
3561
        bHasUsedEscape = true;
3562
        osRE += 'X';
3563
        osRE += expr->m_aoChildren[1]->m_osVal[i];
×
3564
      } else {
3565
        osRE += expr->m_aoChildren[1]->m_osVal[i];
3566
      }
3567
    }
3568
    osRE += "'";
3569
    char md_item_name[256];
3570
    snprintf(md_item_name, sizeof(md_item_name), "gml_%s_type",
3571
             expr->m_aoChildren[0]->m_osVal.c_str());
3572
    const char *type = msLookupHashTable(&(layer->metadata), md_item_name);
4✔
3573
    // Cast if needed (or unsure)
3574
    if (type == NULL || !EQUAL(type, "Character")) {
4✔
3575
      osTmp1 = "CAST(" + osTmp1 + " AS CHARACTER(4096))";
12✔
3576
    }
3577
    std::string osRet("(" + osTmp1 + " LIKE " + osRE);
4✔
3578
    if (bHasUsedEscape)
4✔
3579
      osRet += " ESCAPE 'X'";
3580
    osRet += ")";
3581
    return osRet;
4✔
3582
  }
3583

UNCOV
3584
  case MS_TOKEN_COMPARISON_IN: {
×
3585
    std::string osTmp1(msOGRTranslatePartialInternal(
UNCOV
3586
        layer, expr->m_aoChildren[0].get(), spatialFilterNode, bPartialFilter));
×
UNCOV
3587
    std::string osRet = "(" + osTmp1 + " IN (";
×
UNCOV
3588
    for (size_t i = 0; i < expr->m_aoChildren[1]->m_aoChildren.size(); ++i) {
×
UNCOV
3589
      if (i > 0)
×
3590
        osRet += ", ";
UNCOV
3591
      osRet += msOGRTranslatePartialInternal(
×
3592
          layer, expr->m_aoChildren[1]->m_aoChildren[i].get(),
3593
          spatialFilterNode, bPartialFilter);
3594
    }
3595
    osRet += ")";
UNCOV
3596
    return osRet;
×
3597
  }
3598

3599
  case MS_TOKEN_LITERAL_NUMBER:
3600
  case MS_TOKEN_LITERAL_BOOLEAN: {
3601
    return std::string(CPLSPrintf("%.18g", expr->m_dfVal));
38✔
3602
  }
3603

3604
  case MS_TOKEN_LITERAL_STRING: {
32✔
3605
    char *stresc = msOGREscapeSQLParam(layer, expr->m_osVal.c_str());
32✔
3606
    std::string osRet("'" + std::string(stresc) + "'");
32✔
3607
    msFree(stresc);
32✔
3608
    return osRet;
32✔
3609
  }
3610

3611
  case MS_TOKEN_LITERAL_TIME: {
3612
#ifdef notdef
3613
    // Breaks tests in msautotest/wxs/wfs_time_ogr.map
3614
    return std::string(CPLSPrintf(
3615
        "'%04d/%02d/%02d %02d:%02d:%02d'", expr->m_tmVal.tm_year + 1900,
3616
        expr->m_tmVal.tm_mon + 1, expr->m_tmVal.tm_mday, expr->m_tmVal.tm_hour,
3617
        expr->m_tmVal.tm_min, expr->m_tmVal.tm_sec));
3618
#endif
3619
    return std::string();
3620
  }
3621

3622
  case MS_TOKEN_BINDING_DOUBLE:
71✔
3623
  case MS_TOKEN_BINDING_INTEGER:
3624
  case MS_TOKEN_BINDING_STRING:
3625
  case MS_TOKEN_BINDING_TIME: {
3626
    char *pszTmp = msLayerEscapePropertyName(layer, expr->m_osVal.c_str());
71✔
3627
    std::string osRet;
3628
    osRet += '"';
3629
    osRet += pszTmp;
3630
    osRet += '"';
3631
    msFree(pszTmp);
71✔
3632
    return osRet;
71✔
3633
  }
3634

3635
  case MS_TOKEN_COMPARISON_INTERSECTS: {
4✔
3636
    if (expr != spatialFilterNode)
4✔
UNCOV
3637
      bPartialFilter = true;
×
3638
    return std::string();
3639
  }
3640

3641
  default: {
12✔
3642
    bPartialFilter = true;
12✔
3643
    return std::string();
3644
  }
3645
  }
3646
}
3647

3648
/* ==================================================================
3649
 * Here comes the REAL stuff... the functions below are called by maplayer.c
3650
 * ================================================================== */
3651

3652
/**********************************************************************
3653
 *                     msOGRTranslateMsExpressionToOGRSQL()
3654
 *
3655
 * Tries to translate a mapserver expression to OGR or driver native SQL
3656
 **********************************************************************/
3657
static int msOGRTranslateMsExpressionToOGRSQL(layerObj *layer,
310✔
3658
                                              expressionObj *psFilter,
3659
                                              char *filteritem) {
3660
  msOGRFileInfo *info = (msOGRFileInfo *)layer->layerinfo;
310✔
3661

3662
  msFree(layer->filter.native_string);
310✔
3663
  layer->filter.native_string = NULL;
310✔
3664

3665
  msFree(info->pszWHERE);
310✔
3666
  info->pszWHERE = NULL;
310✔
3667

3668
  // reasons to not produce native string: not simple layer, or an explicit deny
3669
  const char *do_this =
3670
      msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
310✔
3671
  if (do_this && strcmp(do_this, "NO") == 0) {
310✔
3672
    return MS_SUCCESS;
3673
  }
3674

3675
  tokenListNodeObjPtr node = psFilter->tokens;
276✔
3676
  auto expr = BuildExprTree(node, NULL, 0);
276✔
3677
  info->rect_is_defined = MS_FALSE;
276✔
3678
  const msExprNode *spatialFilterNode = NULL;
276✔
3679
  if (expr)
276✔
3680
    msOGRExtractTopSpatialFilter(info, expr.get(), &spatialFilterNode);
107✔
3681

3682
  // more reasons to not produce native string: not a recognized driver
3683
  if (!info->dialect) {
276✔
3684
    // in which case we might still want to try to get a partial WHERE clause
3685
    if (filteritem == NULL && expr) {
210✔
3686
      bool bPartialFilter = false;
61✔
3687
      std::string osSQL(msOGRTranslatePartialInternal(
3688
          layer, expr.get(), spatialFilterNode, bPartialFilter));
61✔
3689
      if (!osSQL.empty()) {
61✔
3690
        info->pszWHERE = msStrdup(osSQL.c_str());
42✔
3691
        if (bPartialFilter) {
42✔
UNCOV
3692
          msDebug("Full filter has only been partially "
×
3693
                  "translated to OGR filter %s\n",
3694
                  info->pszWHERE);
3695
        }
3696
      } else if (bPartialFilter) {
19✔
3697
        msDebug("Filter could not be translated to OGR filter\n");
12✔
3698
      }
3699
    }
3700
    return MS_SUCCESS;
210✔
3701
  }
3702

3703
  char *sql = NULL;
3704

3705
  // node may be NULL if layer->filter.string != NULL and filteritem != NULL
3706
  // this is simple filter but string is regex
3707
  if (node == NULL && filteritem != NULL && layer->filter.string != NULL) {
66✔
UNCOV
3708
    sql = msStringConcatenate(sql, "\"");
×
UNCOV
3709
    sql = msStringConcatenate(sql, filteritem);
×
UNCOV
3710
    sql = msStringConcatenate(sql, "\"");
×
UNCOV
3711
    if (EQUAL(info->dialect, "PostgreSQL")) {
×
UNCOV
3712
      sql = msStringConcatenate(sql, " ~ ");
×
3713
    } else {
UNCOV
3714
      sql = msStringConcatenate(sql, " LIKE ");
×
3715
    }
UNCOV
3716
    sql = msStringConcatenate(sql, "'");
×
3717
    sql = msStringConcatenate(sql, layer->filter.string);
×
UNCOV
3718
    sql = msStringConcatenate(sql, "'");
×
3719
  }
3720

3721
  while (node != NULL) {
303✔
3722

3723
    if (node->next && node->next->token == MS_TOKEN_COMPARISON_IEQ) {
237✔
3724
      char *left = msOGRGetToken(layer, &node);
2✔
3725
      node = node->next; // skip =
2✔
3726
      char *right = msOGRGetToken(layer, &node);
2✔
3727
      sql = msStringConcatenate(sql, "upper(");
2✔
3728
      sql = msStringConcatenate(sql, left);
2✔
3729
      sql = msStringConcatenate(sql, ")");
2✔
3730
      sql = msStringConcatenate(sql, "=");
2✔
3731
      sql = msStringConcatenate(sql, "upper(");
2✔
3732
      sql = msStringConcatenate(sql, right);
2✔
3733
      sql = msStringConcatenate(sql, ")");
2✔
3734
      int ok = left && right;
2✔
3735
      msFree(left);
2✔
3736
      msFree(right);
2✔
3737
      if (!ok) {
2✔
UNCOV
3738
        goto fail;
×
3739
      }
3740
      continue;
2✔
3741
    }
2✔
3742

3743
    switch (node->token) {
235✔
3744
    case MS_TOKEN_COMPARISON_INTERSECTS:
17✔
3745
    case MS_TOKEN_COMPARISON_DISJOINT:
3746
    case MS_TOKEN_COMPARISON_TOUCHES:
3747
    case MS_TOKEN_COMPARISON_OVERLAPS:
3748
    case MS_TOKEN_COMPARISON_CROSSES:
3749
    case MS_TOKEN_COMPARISON_DWITHIN:
3750
    case MS_TOKEN_COMPARISON_BEYOND:
3751
    case MS_TOKEN_COMPARISON_WITHIN:
3752
    case MS_TOKEN_COMPARISON_CONTAINS:
3753
    case MS_TOKEN_COMPARISON_EQUALS: {
3754
      int token = node->token;
3755
      char *fct = msOGRGetToken(layer, &node);
17✔
3756
      node = node->next; // skip (
17✔
3757
      char *a1 = msOGRGetToken(layer, &node);
17✔
3758
      node = node->next; // skip ,
17✔
3759
      char *a2 = msOGRGetToken(layer, &node);
17✔
3760
      char *a3 = NULL;
3761
      if (token == MS_TOKEN_COMPARISON_DWITHIN ||
17✔
3762
          token == MS_TOKEN_COMPARISON_BEYOND) {
3763
        node = node->next; // skip ,
2✔
3764
        a3 = msOGRGetToken(layer, &node);
2✔
3765
      }
3766
      node = node->next; // skip )
17✔
3767
      char *eq = msOGRGetToken(layer, &node);
17✔
3768
      char *rval = msOGRGetToken(layer, &node);
17✔
3769
      if ((eq && strcmp(eq, " != ") == 0) ||
17✔
3770
          (rval && strcmp(rval, "FALSE") == 0)) {
17✔
UNCOV
3771
        sql = msStringConcatenate(sql, "NOT ");
×
3772
      }
3773
      // FIXME: case rval is more complex
3774
      sql = msStringConcatenate(sql, fct);
17✔
3775
      sql = msStringConcatenate(sql, "(");
17✔
3776
      sql = msStringConcatenate(sql, a1);
17✔
3777
      sql = msStringConcatenate(sql, ",");
17✔
3778
      sql = msStringConcatenate(sql, a2);
17✔
3779
      if (token == MS_TOKEN_COMPARISON_DWITHIN ||
17✔
3780
          token == MS_TOKEN_COMPARISON_BEYOND) {
3781
        sql = msStringConcatenate(sql, ")");
2✔
3782
        if (token == MS_TOKEN_COMPARISON_DWITHIN)
2✔
3783
          sql = msStringConcatenate(sql, "<=");
1✔
3784
        else
3785
          sql = msStringConcatenate(sql, ">");
1✔
3786
        sql = msStringConcatenate(sql, a3);
2✔
3787
      } else {
3788
        sql = msStringConcatenate(sql, ")");
15✔
3789
      }
3790
      int ok = fct && a1 && a2 && eq && rval;
17✔
3791
      if (token == MS_TOKEN_COMPARISON_DWITHIN) {
17✔
3792
        ok = ok && a3;
1✔
3793
      }
3794
      msFree(fct);
17✔
3795
      msFree(a1);
17✔
3796
      msFree(a2);
17✔
3797
      msFree(a3);
17✔
3798
      msFree(eq);
17✔
3799
      msFree(rval);
17✔
3800
      if (!ok) {
17✔
UNCOV
3801
        goto fail;
×
3802
      }
3803
      break;
3804
    }
3805
    default: {
218✔
3806
      char *token = msOGRGetToken(layer, &node);
218✔
3807
      if (!token) {
218✔
3808
        goto fail;
×
3809
      }
3810
      sql = msStringConcatenate(sql, token);
218✔
3811
      msFree(token);
218✔
3812
    }
3813
    }
3814
  }
3815

3816
  layer->filter.native_string = sql;
66✔
3817
  return MS_SUCCESS;
66✔
UNCOV
3818
fail:
×
3819
  // error producing native string
UNCOV
3820
  msDebug("Note: Error parsing token list, could produce only: %s. Trying in "
×
3821
          "partial mode\n",
3822
          sql);
UNCOV
3823
  msFree(sql);
×
3824

3825
  // in which case we might still want to try to get a partial WHERE clause
UNCOV
3826
  if (expr) {
×
UNCOV
3827
    bool bPartialFilter = false;
×
3828
    std::string osSQL(msOGRTranslatePartialInternal(
UNCOV
3829
        layer, expr.get(), spatialFilterNode, bPartialFilter));
×
UNCOV
3830
    if (!osSQL.empty()) {
×
UNCOV
3831
      info->pszWHERE = msStrdup(osSQL.c_str());
×
UNCOV
3832
      if (bPartialFilter) {
×
UNCOV
3833
        msDebug("Full filter has only been partially "
×
3834
                "translated to OGR filter %s\n",
3835
                info->pszWHERE);
3836
      }
UNCOV
3837
    } else if (bPartialFilter) {
×
UNCOV
3838
      msDebug("Filter could not be translated to OGR filter\n");
×
3839
    }
3840
  }
3841

3842
  return MS_SUCCESS;
3843
}
3844

3845
/**********************************************************************
3846
 *                     msOGRLayerOpen()
3847
 *
3848
 * Open OGR data source for the specified map layer.
3849
 *
3850
 * If pszOverrideConnection != NULL then this value is used as the connection
3851
 * string instead of lp->connection.  This is used for instance to open
3852
 * a WFS layer, in this case lp->connection is the WFS url, but we want
3853
 * OGR to open the local file on disk that was previously downloaded.
3854
 *
3855
 * An OGR connection string is:   <dataset_filename>[,<layer_index>]
3856
 *  <dataset_filename>   is file format specific
3857
 *  <layer_index>        (optional) is the OGR layer index
3858
 *                       default is 0, the first layer.
3859
 *
3860
 * One can use the "ogrinfo" program to find out the layer indices in a dataset
3861
 *
3862
 * Returns MS_SUCCESS/MS_FAILURE
3863
 **********************************************************************/
3864
int msOGRLayerOpen(layerObj *layer, const char *pszOverrideConnection) {
1,068✔
3865
  msOGRFileInfo *psInfo;
3866

3867
  if (layer->layerinfo != NULL) {
1,068✔
3868
    return MS_SUCCESS; // Nothing to do... layer is already opened
3869
  }
3870

3871
  /* -------------------------------------------------------------------- */
3872
  /*      If this is not a tiled layer, just directly open the target.    */
3873
  /* -------------------------------------------------------------------- */
3874
  if (layer->tileindex == NULL) {
909✔
3875
    psInfo = msOGRFileOpen(layer, (pszOverrideConnection ? pszOverrideConnection
903✔
3876
                                                         : layer->connection));
3877
    layer->layerinfo = psInfo;
903✔
3878
    layer->tileitemindex = -1;
903✔
3879

3880
    if (layer->layerinfo == NULL)
903✔
3881
      return MS_FAILURE;
3882
  }
3883

3884
  /* -------------------------------------------------------------------- */
3885
  /*      Otherwise we open the tile index, identify the tile item        */
3886
  /*      index and try to select the first file matching our query       */
3887
  /*      region.                                                         */
3888
  /* -------------------------------------------------------------------- */
3889
  else {
3890
    // Open tile index
3891

3892
    psInfo = msOGRFileOpen(layer, layer->tileindex);
6✔
3893
    layer->layerinfo = psInfo;
6✔
3894

3895
    if (layer->layerinfo == NULL)
6✔
3896
      return MS_FAILURE;
3897

3898
    // Identify TILEITEM
3899
    OGRFeatureDefnH hDefn = OGR_L_GetLayerDefn(psInfo->hLayer);
6✔
3900
    layer->tileitemindex = OGR_FD_GetFieldIndex(hDefn, layer->tileitem);
6✔
3901
    if (layer->tileitemindex < 0) {
6✔
UNCOV
3902
      msSetError(MS_OGRERR,
×
3903
                 "Can't identify TILEITEM %s field in TILEINDEX `%s'.",
3904
                 "msOGRLayerOpen()", layer->tileitem, layer->tileindex);
3905
      msOGRFileClose(layer, psInfo);
×
3906
      layer->layerinfo = NULL;
×
UNCOV
3907
      return MS_FAILURE;
×
3908
    }
3909

3910
    // Identify TILESRS
3911
    if (layer->tilesrs != NULL &&
9✔
3912
        OGR_FD_GetFieldIndex(hDefn, layer->tilesrs) < 0) {
3✔
UNCOV
3913
      msSetError(MS_OGRERR,
×
3914
                 "Can't identify TILESRS %s field in TILEINDEX `%s'.",
3915
                 "msOGRLayerOpen()", layer->tilesrs, layer->tileindex);
UNCOV
3916
      msOGRFileClose(layer, psInfo);
×
UNCOV
3917
      layer->layerinfo = NULL;
×
UNCOV
3918
      return MS_FAILURE;
×
3919
    }
3920
    if (layer->tilesrs != NULL && layer->projection.numargs == 0) {
6✔
3921
      msSetError(MS_OGRERR,
×
3922
                 "A layer with TILESRS set in TILEINDEX `%s' must have a "
3923
                 "projection set on itself.",
3924
                 "msOGRLayerOpen()", layer->tileindex);
UNCOV
3925
      msOGRFileClose(layer, psInfo);
×
UNCOV
3926
      layer->layerinfo = NULL;
×
UNCOV
3927
      return MS_FAILURE;
×
3928
    }
3929
  }
3930

3931
  /* ------------------------------------------------------------------
3932
   * If projection was "auto" then set proj to the dataset's projection.
3933
   * For a tile index, it is assume the tile index has the projection.
3934
   * ------------------------------------------------------------------ */
3935
  if (layer->projection.numargs > 0 &&
909✔
3936
      EQUAL(layer->projection.args[0], "auto")) {
812✔
3937
    ACQUIRE_OGR_LOCK;
1✔
3938
    OGRSpatialReferenceH hSRS = OGR_L_GetSpatialRef(psInfo->hLayer);
1✔
3939

3940
    if (msOGRSpatialRef2ProjectionObj(hSRS, &(layer->projection),
1✔
3941
                                      layer->debug) != MS_SUCCESS) {
UNCOV
3942
      errorObj *ms_error = msGetErrorObj();
×
3943

UNCOV
3944
      RELEASE_OGR_LOCK;
×
UNCOV
3945
      msSetError(MS_OGRERR,
×
3946
                 "%s  "
3947
                 "PROJECTION AUTO cannot be used for this "
3948
                 "OGR connection (in layer `%s').",
UNCOV
3949
                 "msOGRLayerOpen()", ms_error->message,
×
UNCOV
3950
                 layer->name ? layer->name : "(null)");
×
UNCOV
3951
      msOGRFileClose(layer, psInfo);
×
UNCOV
3952
      layer->layerinfo = NULL;
×
UNCOV
3953
      return (MS_FAILURE);
×
3954
    }
3955
    RELEASE_OGR_LOCK;
1✔
3956
  }
3957

3958
  return MS_SUCCESS;
3959
}
3960

3961
/**********************************************************************
3962
 *                     msOGRLayerOpenVT()
3963
 *
3964
 * Overloaded version of msOGRLayerOpen for virtual table architecture
3965
 **********************************************************************/
3966
static int msOGRLayerOpenVT(layerObj *layer) {
977✔
3967
  return msOGRLayerOpen(layer, NULL);
1,064✔
3968
}
3969

3970
/**********************************************************************
3971
 *                     msOGRLayerClose()
3972
 **********************************************************************/
3973
int msOGRLayerClose(layerObj *layer) {
928✔
3974
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
928✔
3975

3976
  if (psInfo) {
928✔
3977
    if (layer->debug)
909✔
3978
      msDebug("msOGRLayerClose(%s).\n", layer->connection);
5✔
3979

3980
    msOGRFileClose(layer, psInfo);
909✔
3981
    layer->layerinfo = NULL;
909✔
3982
  }
3983

3984
  return MS_SUCCESS;
928✔
3985
}
3986

3987
/**********************************************************************
3988
 *                     msOGRLayerIsOpen()
3989
 **********************************************************************/
3990
static int msOGRLayerIsOpen(layerObj *layer) {
1,612✔
3991
  if (layer->layerinfo)
2,124✔
3992
    return MS_TRUE;
418✔
3993

3994
  return MS_FALSE;
3995
}
3996

3997
int msOGRSupportsIsNull(layerObj *layer) {
4✔
3998
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
4✔
3999
  if (psInfo && psInfo->dialect &&
4✔
4000
      (EQUAL(psInfo->dialect, "Spatialite") ||
2✔
4001
       EQUAL(psInfo->dialect, "GPKG"))) {
1✔
4002
    // reasons to not produce native string: not simple layer, or an explicit
4003
    // deny
4004
    const char *do_this =
4005
        msLayerGetProcessingKey(layer, "NATIVE_SQL"); // default is YES
2✔
4006
    if (do_this && strcmp(do_this, "NO") == 0) {
2✔
4007
      return MS_FALSE;
4008
    }
4009
    return MS_TRUE;
4010
  }
4011

4012
  return MS_FALSE;
4013
}
4014

4015
/**********************************************************************
4016
 *                     msOGRLayerWhichShapes()
4017
 *
4018
 * Init OGR layer structs ready for calls to msOGRLayerNextShape().
4019
 *
4020
 * Returns MS_SUCCESS/MS_FAILURE, or MS_DONE if no shape matching the
4021
 * layer's FILTER overlaps the selected region.
4022
 **********************************************************************/
4023
int msOGRLayerWhichShapes(layerObj *layer, rectObj rect, int /*isQuery*/) {
314✔
4024
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
314✔
4025
  int status;
4026

4027
  if (psInfo == NULL || psInfo->hLayer == NULL) {
314✔
UNCOV
4028
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4029
               "msOGRLayerWhichShapes()");
UNCOV
4030
    return (MS_FAILURE);
×
4031
  }
4032

4033
  status = msOGRFileWhichShapes(layer, rect, psInfo);
314✔
4034

4035
  // Update itemindexes / layer->iteminfo
4036
  if (status == MS_SUCCESS)
314✔
4037
    msOGRLayerInitItemInfo(layer);
314✔
4038

4039
  if (status != MS_SUCCESS || layer->tileindex == NULL)
314✔
4040
    return status;
312✔
4041

4042
  // If we are using a tile index, we need to advance to the first
4043
  // tile matching the spatial query, and load it.
4044

4045
  return msOGRFileReadTile(layer, psInfo);
2✔
4046
}
4047

4048
/**********************************************************************
4049
 *                     msOGRLayerGetItems()
4050
 *
4051
 * Load item (i.e. field) names in a char array.  If we are working
4052
 * with a tiled layer, ensure a tile is loaded and use it for the items.
4053
 * It is implicitly assumed that the schemas will match on all tiles.
4054
 **********************************************************************/
4055
int msOGRLayerGetItems(layerObj *layer) {
693✔
4056
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
693✔
4057

4058
  if (psInfo == NULL || psInfo->hLayer == NULL) {
693✔
UNCOV
4059
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4060
               "msOGRLayerGetItems()");
UNCOV
4061
    return (MS_FAILURE);
×
4062
  }
4063

4064
  if (layer->tileindex != NULL) {
693✔
4065
    if (psInfo->poCurTile == NULL &&
10✔
4066
        msOGRFileReadTile(layer, psInfo) != MS_SUCCESS)
5✔
4067
      return MS_FAILURE;
4068

4069
    psInfo = psInfo->poCurTile;
5✔
4070
  }
4071

4072
  layer->numitems = 0;
693✔
4073
  layer->items = msOGRFileGetItems(layer, psInfo);
693✔
4074
  if (layer->items == NULL)
693✔
4075
    return MS_FAILURE;
4076

4077
  while (layer->items[layer->numitems] != NULL)
6,913✔
4078
    layer->numitems++;
6,220✔
4079

4080
  return msOGRLayerInitItemInfo(layer);
693✔
4081
}
4082

4083
/**********************************************************************
4084
 *                     msOGRLayerInitItemInfo()
4085
 *
4086
 * Init the itemindexes array after items[] has been reset in a layer.
4087
 **********************************************************************/
4088
static int msOGRLayerInitItemInfo(layerObj *layer) {
1,308✔
4089
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
1,308✔
4090
  int i;
4091
  OGRFeatureDefnH hDefn;
4092

4093
  if (layer->numitems == 0)
1,308✔
4094
    return MS_SUCCESS;
4095

4096
  if (layer->tileindex != NULL) {
1,271✔
4097
    if (psInfo->poCurTile == NULL &&
15✔
UNCOV
4098
        msOGRFileReadTile(layer, psInfo, -2) != MS_SUCCESS)
×
4099
      return MS_FAILURE;
4100

4101
    psInfo = psInfo->poCurTile;
15✔
4102
  }
4103

4104
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,271✔
UNCOV
4105
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4106
               "msOGRLayerInitItemInfo()");
UNCOV
4107
    return (MS_FAILURE);
×
4108
  }
4109

4110
  if ((hDefn = OGR_L_GetLayerDefn(psInfo->hLayer)) == NULL) {
1,271✔
UNCOV
4111
    msSetError(MS_OGRERR, "Layer contains no fields.",
×
4112
               "msOGRLayerInitItemInfo()");
UNCOV
4113
    return (MS_FAILURE);
×
4114
  }
4115

4116
  if (layer->iteminfo)
1,271✔
4117
    free(layer->iteminfo);
534✔
4118
  if ((layer->iteminfo = (int *)malloc(sizeof(int) * layer->numitems)) ==
1,271✔
4119
      NULL) {
4120
    msSetError(MS_MEMERR, NULL, "msOGRLayerInitItemInfo()");
×
UNCOV
4121
    return (MS_FAILURE);
×
4122
  }
4123

4124
  int *itemindexes = (int *)layer->iteminfo;
4125
  for (i = 0; i < layer->numitems; i++) {
11,845✔
4126
    // Special case for handling text string and angle coming from
4127
    // OGR style strings.  We use special attribute snames.
4128
    if (EQUAL(layer->items[i], MSOGR_LABELFONTNAMENAME))
10,574✔
UNCOV
4129
      itemindexes[i] = MSOGR_LABELFONTNAMEINDEX;
×
4130
    else if (EQUAL(layer->items[i], MSOGR_LABELSIZENAME))
10,574✔
UNCOV
4131
      itemindexes[i] = MSOGR_LABELSIZEINDEX;
×
4132
    else if (EQUAL(layer->items[i], MSOGR_LABELTEXTNAME))
10,574✔
UNCOV
4133
      itemindexes[i] = MSOGR_LABELTEXTINDEX;
×
4134
    else if (EQUAL(layer->items[i], MSOGR_LABELANGLENAME))
10,574✔
UNCOV
4135
      itemindexes[i] = MSOGR_LABELANGLEINDEX;
×
4136
    else if (EQUAL(layer->items[i], MSOGR_LABELFCOLORNAME))
10,574✔
UNCOV
4137
      itemindexes[i] = MSOGR_LABELFCOLORINDEX;
×
4138
    else if (EQUAL(layer->items[i], MSOGR_LABELBCOLORNAME))
10,574✔
UNCOV
4139
      itemindexes[i] = MSOGR_LABELBCOLORINDEX;
×
4140
    else if (EQUAL(layer->items[i], MSOGR_LABELPLACEMENTNAME))
10,574✔
UNCOV
4141
      itemindexes[i] = MSOGR_LABELPLACEMENTINDEX;
×
4142
    else if (EQUAL(layer->items[i], MSOGR_LABELANCHORNAME))
10,574✔
UNCOV
4143
      itemindexes[i] = MSOGR_LABELANCHORINDEX;
×
4144
    else if (EQUAL(layer->items[i], MSOGR_LABELDXNAME))
10,574✔
UNCOV
4145
      itemindexes[i] = MSOGR_LABELDXINDEX;
×
4146
    else if (EQUAL(layer->items[i], MSOGR_LABELDYNAME))
10,574✔
UNCOV
4147
      itemindexes[i] = MSOGR_LABELDYINDEX;
×
4148
    else if (EQUAL(layer->items[i], MSOGR_LABELPERPNAME))
10,574✔
UNCOV
4149
      itemindexes[i] = MSOGR_LABELPERPINDEX;
×
4150
    else if (EQUAL(layer->items[i], MSOGR_LABELBOLDNAME))
10,574✔
4151
      itemindexes[i] = MSOGR_LABELBOLDINDEX;
×
4152
    else if (EQUAL(layer->items[i], MSOGR_LABELITALICNAME))
10,574✔
UNCOV
4153
      itemindexes[i] = MSOGR_LABELITALICINDEX;
×
4154
    else if (EQUAL(layer->items[i], MSOGR_LABELUNDERLINENAME))
10,574✔
4155
      itemindexes[i] = MSOGR_LABELUNDERLINEINDEX;
×
4156
    else if (EQUAL(layer->items[i], MSOGR_LABELPRIORITYNAME))
10,574✔
UNCOV
4157
      itemindexes[i] = MSOGR_LABELPRIORITYINDEX;
×
4158
    else if (EQUAL(layer->items[i], MSOGR_LABELSTRIKEOUTNAME))
10,574✔
4159
      itemindexes[i] = MSOGR_LABELSTRIKEOUTINDEX;
×
4160
    else if (EQUAL(layer->items[i], MSOGR_LABELSTRETCHNAME))
10,574✔
UNCOV
4161
      itemindexes[i] = MSOGR_LABELSTRETCHINDEX;
×
4162
    else if (EQUAL(layer->items[i], MSOGR_LABELADJHORNAME))
10,574✔
4163
      itemindexes[i] = MSOGR_LABELADJHORINDEX;
×
4164
    else if (EQUAL(layer->items[i], MSOGR_LABELADJVERTNAME))
10,574✔
UNCOV
4165
      itemindexes[i] = MSOGR_LABELADJVERTINDEX;
×
4166
    else if (EQUAL(layer->items[i], MSOGR_LABELHCOLORNAME))
10,574✔
4167
      itemindexes[i] = MSOGR_LABELHCOLORINDEX;
×
4168
    else if (EQUAL(layer->items[i], MSOGR_LABELOCOLORNAME))
10,574✔
UNCOV
4169
      itemindexes[i] = MSOGR_LABELOCOLORINDEX;
×
4170
    else if (EQUALN(layer->items[i], MSOGR_LABELPARAMNAME,
10,574✔
4171
                    MSOGR_LABELPARAMNAMELEN))
UNCOV
4172
      itemindexes[i] = MSOGR_LABELPARAMINDEX +
×
4173
                       atoi(layer->items[i] + MSOGR_LABELPARAMNAMELEN);
×
4174
    else if (EQUALN(layer->items[i], MSOGR_BRUSHPARAMNAME,
10,574✔
4175
                    MSOGR_BRUSHPARAMNAMELEN))
4176
      itemindexes[i] = MSOGR_BRUSHPARAMINDEX +
×
UNCOV
4177
                       atoi(layer->items[i] + MSOGR_BRUSHPARAMNAMELEN);
×
4178
    else if (EQUALN(layer->items[i], MSOGR_PENPARAMNAME, MSOGR_PENPARAMNAMELEN))
10,574✔
UNCOV
4179
      itemindexes[i] =
×
UNCOV
4180
          MSOGR_PENPARAMINDEX + atoi(layer->items[i] + MSOGR_PENPARAMNAMELEN);
×
4181
    else if (EQUALN(layer->items[i], MSOGR_SYMBOLPARAMNAME,
10,574✔
4182
                    MSOGR_SYMBOLPARAMNAMELEN))
UNCOV
4183
      itemindexes[i] = MSOGR_SYMBOLPARAMINDEX +
×
UNCOV
4184
                       atoi(layer->items[i] + MSOGR_SYMBOLPARAMNAMELEN);
×
4185
    else {
4186
      itemindexes[i] = OGR_FD_GetFieldIndex(hDefn, layer->items[i]);
10,574✔
4187
      if (itemindexes[i] == -1) {
10,574✔
4188
        if (EQUAL(layer->items[i], OGR_L_GetFIDColumn(psInfo->hLayer))) {
21✔
4189
          itemindexes[i] = MSOGR_FID_INDEX;
21✔
4190
        }
4191
      }
4192
    }
4193
    if (itemindexes[i] == -1) {
10,574✔
UNCOV
4194
      msSetError(MS_OGRERR, "Invalid Field name: %s in layer `%s'",
×
UNCOV
4195
                 "msOGRLayerInitItemInfo()", layer->items[i],
×
UNCOV
4196
                 layer->name ? layer->name : "(null)");
×
UNCOV
4197
      return (MS_FAILURE);
×
4198
    }
4199
  }
4200

4201
  return (MS_SUCCESS);
4202
}
4203

4204
/**********************************************************************
4205
 *                     msOGRLayerFreeItemInfo()
4206
 *
4207
 * Free the itemindexes array in a layer.
4208
 **********************************************************************/
4209
void msOGRLayerFreeItemInfo(layerObj *layer) {
1,945✔
4210

4211
  if (layer->iteminfo)
1,945✔
4212
    free(layer->iteminfo);
737✔
4213
  layer->iteminfo = NULL;
1,945✔
4214
}
1,945✔
4215

4216
/**********************************************************************
4217
 *                     msOGRLayerNextShape()
4218
 *
4219
 * Returns shape sequentially from OGR data source.
4220
 * msOGRLayerWhichShape() must have been called first.
4221
 *
4222
 * Returns MS_SUCCESS/MS_FAILURE
4223
 **********************************************************************/
4224
int msOGRLayerNextShape(layerObj *layer, shapeObj *shape) {
16,642✔
4225
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
16,642✔
4226
  int status;
4227

4228
  if (psInfo == NULL || psInfo->hLayer == NULL) {
16,642✔
UNCOV
4229
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4230
               "msOGRLayerNextShape()");
UNCOV
4231
    return (MS_FAILURE);
×
4232
  }
4233

4234
  if (layer->tileindex == NULL)
16,642✔
4235
    return msOGRFileNextShape(layer, shape, psInfo);
16,637✔
4236

4237
  // Do we need to load the first tile?
4238
  if (psInfo->poCurTile == NULL) {
5✔
UNCOV
4239
    status = msOGRFileReadTile(layer, psInfo);
×
UNCOV
4240
    if (status != MS_SUCCESS)
×
4241
      return status;
4242
  }
4243

4244
  do {
4245
    // Try getting a shape from this tile.
4246
    status = msOGRFileNextShape(layer, shape, psInfo->poCurTile);
6✔
4247
    if (status != MS_DONE) {
6✔
4248
      if (psInfo->sTileProj.numargs > 0) {
3✔
4249
        msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
2✔
4250
      }
4251

4252
      return status;
3✔
4253
    }
4254

4255
    // try next tile.
4256
    status = msOGRFileReadTile(layer, psInfo);
3✔
4257
    if (status != MS_SUCCESS)
3✔
4258
      return status;
2✔
4259
  } while (status == MS_SUCCESS);
4260
  return status; // make compiler happy. this is never reached however
4261
}
4262

4263
/**********************************************************************
4264
 *                     msOGRLayerGetShape()
4265
 *
4266
 * Returns shape from OGR data source by fid.
4267
 *
4268
 * Returns MS_SUCCESS/MS_FAILURE
4269
 **********************************************************************/
4270
int msOGRLayerGetShape(layerObj *layer, shapeObj *shape, resultObj *record) {
1,334✔
4271
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
1,334✔
4272

4273
  long shapeindex = record->shapeindex;
1,334✔
4274
  int tileindex = record->tileindex;
1,334✔
4275
  int resultindex = record->resultindex;
1,334✔
4276
  int record_is_fid = TRUE;
4277

4278
  /* set the resultindex as shapeindex if available */
4279
  if (resultindex >= 0) {
1,334✔
4280
    record_is_fid = FALSE;
4281
    shapeindex = resultindex;
1,332✔
4282
  }
4283

4284
  if (psInfo == NULL || psInfo->hLayer == NULL) {
1,334✔
UNCOV
4285
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4286
               "msOGRLayerGetShape()");
UNCOV
4287
    return (MS_FAILURE);
×
4288
  }
4289

4290
  if (layer->tileindex == NULL)
1,334✔
4291
    return msOGRFileGetShape(layer, shape, shapeindex, psInfo, record_is_fid);
1,331✔
4292
  else {
4293
    if (psInfo->poCurTile == NULL || psInfo->poCurTile->nTileId != tileindex) {
3✔
4294
      if (msOGRFileReadTile(layer, psInfo, tileindex) != MS_SUCCESS)
3✔
4295
        return MS_FAILURE;
4296
    }
4297

4298
    int status = msOGRFileGetShape(layer, shape, shapeindex, psInfo->poCurTile,
3✔
4299
                                   record_is_fid);
4300
    if (status == MS_SUCCESS && psInfo->sTileProj.numargs > 0) {
3✔
4301
      msProjectShape(&(psInfo->sTileProj), &(layer->projection), shape);
2✔
4302
    }
4303
    return status;
3✔
4304
  }
4305
}
4306

4307
/**********************************************************************
4308
 *                     msOGRLayerGetExtent()
4309
 *
4310
 * Returns the layer extents.
4311
 *
4312
 * Returns MS_SUCCESS/MS_FAILURE
4313
 **********************************************************************/
4314
int msOGRLayerGetExtent(layerObj *layer, rectObj *extent) {
89✔
4315
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
89✔
4316
  OGREnvelope oExtent;
4317

4318
  if (psInfo == NULL || psInfo->hLayer == NULL) {
89✔
UNCOV
4319
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4320
               "msOGRLayerGetExtent()");
UNCOV
4321
    return (MS_FAILURE);
×
4322
  }
4323

4324
  /* ------------------------------------------------------------------
4325
   * Call OGR's GetExtent()... note that for some formats this will
4326
   * result in a scan of the whole layer and can be an expensive call.
4327
   *
4328
   * For tile indexes layers we assume it is sufficient to get the
4329
   * extents of the tile index.
4330
   * ------------------------------------------------------------------ */
4331
  ACQUIRE_OGR_LOCK;
89✔
4332
  if (OGR_L_GetExtent(psInfo->hLayer, &oExtent, TRUE) != OGRERR_NONE) {
89✔
UNCOV
4333
    RELEASE_OGR_LOCK;
×
4334
    msSetError(MS_MISCERR, "Unable to get extents for this layer.",
×
4335
               "msOGRLayerGetExtent()");
UNCOV
4336
    return (MS_FAILURE);
×
4337
  }
4338
  RELEASE_OGR_LOCK;
89✔
4339

4340
  extent->minx = oExtent.MinX;
89✔
4341
  extent->miny = oExtent.MinY;
89✔
4342
  extent->maxx = oExtent.MaxX;
89✔
4343
  extent->maxy = oExtent.MaxY;
89✔
4344

4345
  return MS_SUCCESS;
89✔
4346
}
4347

4348
/**********************************************************************
4349
 *                     msOGRLayerGetNumFeatures()
4350
 *
4351
 * Returns the layer feature count.
4352
 *
4353
 * Returns the number of features on success, -1 on error
4354
 **********************************************************************/
UNCOV
4355
int msOGRLayerGetNumFeatures(layerObj *layer) {
×
UNCOV
4356
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
×
4357
  int result;
4358

UNCOV
4359
  if (psInfo == NULL || psInfo->hLayer == NULL) {
×
UNCOV
4360
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
4361
               "msOGRLayerGetNumFeatures()");
UNCOV
4362
    return -1;
×
4363
  }
4364

4365
  /* ------------------------------------------------------------------
4366
   * Call OGR's GetFeatureCount()... note that for some formats this will
4367
   * result in a scan of the whole layer and can be an expensive call.
4368
   * ------------------------------------------------------------------ */
UNCOV
4369
  ACQUIRE_OGR_LOCK;
×
UNCOV
4370
  result = (int)OGR_L_GetFeatureCount(psInfo->hLayer, TRUE);
×
UNCOV
4371
  RELEASE_OGR_LOCK;
×
4372

UNCOV
4373
  return result;
×
4374
}
4375

4376
/**********************************************************************
4377
 *                     msOGRGetSymbolId()
4378
 *
4379
 * Returns a MapServer symbol number matching one of the symbols from
4380
 * the OGR symbol id string.  If not found then try to locate the
4381
 * default symbol name, and if not found return 0.
4382
 **********************************************************************/
4383
static int msOGRGetSymbolId(symbolSetObj *symbolset, const char *pszSymbolId,
16✔
4384
                            const char *pszDefaultSymbol,
4385
                            int try_addimage_if_notfound) {
4386
  // Symbol name mapping:
4387
  // First look for the native symbol name, then the ogr-...
4388
  // generic name, and in last resort try pszDefaultSymbol if
4389
  // provided by user.
4390
  char **params;
4391
  int numparams;
4392
  int nSymbol = -1;
4393

4394
  if (pszSymbolId && pszSymbolId[0] != '\0') {
16✔
4395
    params = msStringSplit(pszSymbolId, ',', &numparams);
16✔
4396
    if (params != NULL) {
16✔
4397
      for (int j = 0; j < numparams && nSymbol == -1; j++) {
32✔
4398
        nSymbol =
4399
            msGetSymbolIndex(symbolset, params[j], try_addimage_if_notfound);
16✔
4400
      }
4401
      msFreeCharArray(params, numparams);
16✔
4402
    }
4403
  }
4404
  if (nSymbol == -1 && pszDefaultSymbol) {
16✔
UNCOV
4405
    nSymbol = msGetSymbolIndex(symbolset, (char *)pszDefaultSymbol,
×
4406
                               try_addimage_if_notfound);
4407
  }
4408
  if (nSymbol == -1)
16✔
4409
    nSymbol = 0;
4410

4411
  return nSymbol;
16✔
4412
}
4413

4414
static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4415
                                      OGRStyleToolH hLabelStyle);
4416
static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
4417
                                    OGRStyleToolH hPenStyle, int bIsBrush,
4418
                                    int *pbPriority);
4419
static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
4420
                                      OGRStyleToolH hBrushStyle, int *pbIsBrush,
4421
                                      int *pbPriority);
4422
static int msOGRUpdateStyleParseSymbol(mapObj *map, styleObj *s,
4423
                                       OGRStyleToolH hSymbolStyle,
4424
                                       int *pbPriority);
4425
static int msOGRAddBgColorStyleParseBrush(styleObj *s,
4426
                                          OGRStyleToolH hSymbolStyle);
4427

4428
static int msOGRUpdateStyleCheckPenBrushOnly(OGRStyleMgrH hStyleMgr) {
39✔
4429
  int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
39✔
4430
  int countPen = 0, countBrush = 0;
4431
  int bIsNull;
4432

4433
  for (int i = 0; i < numParts; i++) {
78✔
4434
    OGRSTClassId eStylePartType;
4435
    OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
42✔
4436
    if (!hStylePart)
42✔
UNCOV
4437
      continue;
×
4438

4439
    eStylePartType = OGR_ST_GetType(hStylePart);
42✔
4440
    if (eStylePartType == OGRSTCPen) {
42✔
4441
      countPen++;
34✔
4442
      OGR_ST_GetParamNum(hStylePart, OGRSTPenPriority, &bIsNull);
34✔
4443
      if (!bIsNull) {
34✔
4444
        OGR_ST_Destroy(hStylePart);
3✔
4445
        return MS_FALSE;
3✔
4446
      }
4447
    } else if (eStylePartType == OGRSTCBrush) {
8✔
4448
      countBrush++;
4✔
4449
      OGR_ST_GetParamNum(hStylePart, OGRSTBrushPriority, &bIsNull);
4✔
4450
      if (!bIsNull) {
4✔
UNCOV
4451
        OGR_ST_Destroy(hStylePart);
×
UNCOV
4452
        return MS_FALSE;
×
4453
      }
4454
    } else if (eStylePartType == OGRSTCSymbol) {
4✔
UNCOV
4455
      OGR_ST_Destroy(hStylePart);
×
UNCOV
4456
      return MS_FALSE;
×
4457
    }
4458
    OGR_ST_Destroy(hStylePart);
39✔
4459
  }
4460
  return (countPen == 1 && countBrush == 1);
36✔
4461
}
4462

4463
/**********************************************************************
4464
 *                     msOGRUpdateStyle()
4465
 *
4466
 * Update the mapserver style according to the ogr style.
4467
 * The function is called by msOGRGetAutoStyle and
4468
 * msOGRUpdateStyleFromString
4469
 **********************************************************************/
4470

4471
typedef struct {
4472
  int nPriority; /* the explicit priority as specified by the 'l' option of PEN,
4473
                    BRUSH and SYMBOL tools */
4474
  int nApparitionIndex; /* the index of the tool as parsed from the OGR feature
4475
                           style string */
4476
} StyleSortStruct;
4477

4478
static int msOGRUpdateStyleSortFct(const void *pA, const void *pB) {
4✔
4479
  StyleSortStruct *sssa = (StyleSortStruct *)pA;
4480
  StyleSortStruct *sssb = (StyleSortStruct *)pB;
4481
  if (sssa->nPriority < sssb->nPriority)
4✔
4482
    return -1;
4483
  else if (sssa->nPriority > sssb->nPriority)
3✔
4484
    return 1;
4485
  else if (sssa->nApparitionIndex < sssb->nApparitionIndex)
2✔
4486
    return -1;
4487
  else
UNCOV
4488
    return 1;
×
4489
}
4490

4491
static int msOGRUpdateStyle(OGRStyleMgrH hStyleMgr, mapObj *map,
39✔
4492
                            layerObj *layer, classObj *c) {
4493
  int bIsBrush = MS_FALSE;
39✔
4494
  int numParts = OGR_SM_GetPartCount(hStyleMgr, NULL);
39✔
4495
  int nPriority;
4496
  int bIsPenBrushOnly = msOGRUpdateStyleCheckPenBrushOnly(hStyleMgr);
39✔
4497
  StyleSortStruct *pasSortStruct =
4498
      (StyleSortStruct *)msSmallMalloc(sizeof(StyleSortStruct) * numParts);
39✔
4499
  int iSortStruct = 0;
4500
  int iBaseStyleIndex = c->numstyles;
39✔
4501
  int i;
4502

4503
  /* ------------------------------------------------------------------
4504
   * Handle each part
4505
   * ------------------------------------------------------------------ */
4506

4507
  for (i = 0; i < numParts; i++) {
84✔
4508
    OGRSTClassId eStylePartType;
4509
    OGRStyleToolH hStylePart = OGR_SM_GetPart(hStyleMgr, i, NULL);
45✔
4510
    if (!hStylePart)
45✔
4511
      continue;
×
4512
    eStylePartType = OGR_ST_GetType(hStylePart);
45✔
4513
    nPriority = INT_MIN;
45✔
4514

4515
    // We want all size values returned in pixels.
4516
    //
4517
    // The scale factor that OGR expect is the ground/paper scale
4518
    // e.g. if 1 ground unit = 0.01 paper unit then scale=1/0.01=100
4519
    // cellsize if number of ground units/pixel, and OGR assumes that
4520
    // there is 72*39.37 pixels/ground units (since meter is assumed
4521
    // for ground... but what ground units we have does not matter
4522
    // as long as use the same assumptions everywhere)
4523
    // That gives scale = cellsize*72*39.37
4524

4525
    OGR_ST_SetUnit(hStylePart, OGRSTUPixel,
45✔
4526
                   map->cellsize * map->resolution / map->defresolution * 72.0 *
45✔
4527
                       39.37);
4528

4529
    if (eStylePartType == OGRSTCLabel) {
45✔
4530
      int ret = msOGRUpdateStyleParseLabel(map, layer, c, hStylePart);
4✔
4531
      if (ret != MS_SUCCESS) {
4✔
4532
        OGR_ST_Destroy(hStylePart);
×
UNCOV
4533
        msFree(pasSortStruct);
×
UNCOV
4534
        return ret;
×
4535
      }
4536
    } else if (eStylePartType == OGRSTCPen) {
41✔
4537
      styleObj *s;
4538
      int nIndex;
4539
      if (bIsPenBrushOnly) {
37✔
4540
        /* Historic behavior when there is a PEN and BRUSH only */
4541
        if (bIsBrush || layer->type == MS_LAYER_POLYGON)
2✔
4542
          // This is a multipart symbology, so pen defn goes in the
4543
          // overlaysymbol params
4544
          nIndex = c->numstyles + 1;
2✔
4545
        else
UNCOV
4546
          nIndex = c->numstyles;
×
4547
      } else
4548
        nIndex = c->numstyles;
35✔
4549

4550
      if (msMaybeAllocateClassStyle(c, nIndex)) {
37✔
4551
        OGR_ST_Destroy(hStylePart);
×
UNCOV
4552
        msFree(pasSortStruct);
×
UNCOV
4553
        return (MS_FAILURE);
×
4554
      }
4555
      s = c->styles[nIndex];
37✔
4556

4557
      msOGRUpdateStyleParsePen(map, layer, s, hStylePart, bIsBrush, &nPriority);
37✔
4558

4559
    } else if (eStylePartType == OGRSTCBrush) {
4✔
4560

4561
      styleObj *s;
4562
      int nIndex = 0;
4563

4564
      int bBgColorIsNull = MS_TRUE;
4✔
4565
      OGR_ST_GetParamStr(hStylePart, OGRSTBrushBColor, &bBgColorIsNull);
4✔
4566

4567
      if (!bBgColorIsNull) {
4✔
4568

4569
        if (msMaybeAllocateClassStyle(c, nIndex)) {
3✔
4570
          OGR_ST_Destroy(hStylePart);
×
UNCOV
4571
          msFree(pasSortStruct);
×
UNCOV
4572
          return (MS_FAILURE);
×
4573
        }
4574

4575
        // add a backgroundcolor as a separate style
4576
        s = c->styles[nIndex];
3✔
4577
        msOGRAddBgColorStyleParseBrush(s, hStylePart);
3✔
4578
      }
4579

4580
      nIndex = (bIsPenBrushOnly) ? nIndex : c->numstyles;
4✔
4581

4582
      if (!bBgColorIsNull) {
4✔
4583
        // if we have a bgcolor style we need to increase the index
4584
        nIndex += 1;
3✔
4585
      }
4586

4587
      /* We need 1 style */
4588
      if (msMaybeAllocateClassStyle(c, nIndex)) {
4✔
UNCOV
4589
        OGR_ST_Destroy(hStylePart);
×
UNCOV
4590
        msFree(pasSortStruct);
×
UNCOV
4591
        return (MS_FAILURE);
×
4592
      }
4593
      s = c->styles[nIndex];
4✔
4594

4595
      msOGRUpdateStyleParseBrush(map, layer, s, hStylePart, &bIsBrush,
4✔
4596
                                 &nPriority);
4597

UNCOV
4598
    } else if (eStylePartType == OGRSTCSymbol) {
×
4599
      styleObj *s;
4600
      /* We need 1 style */
UNCOV
4601
      int nIndex = c->numstyles;
×
UNCOV
4602
      if (msMaybeAllocateClassStyle(c, nIndex)) {
×
UNCOV
4603
        OGR_ST_Destroy(hStylePart);
×
UNCOV
4604
        msFree(pasSortStruct);
×
UNCOV
4605
        return (MS_FAILURE);
×
4606
      }
UNCOV
4607
      s = c->styles[nIndex];
×
4608

UNCOV
4609
      msOGRUpdateStyleParseSymbol(map, s, hStylePart, &nPriority);
×
4610
    }
4611

4612
    /* Memorize the explicit priority and apparition order of the parsed
4613
     * tool/style */
4614
    if (!bIsPenBrushOnly &&
45✔
4615
        (eStylePartType == OGRSTCPen || eStylePartType == OGRSTCBrush ||
41✔
4616
         eStylePartType == OGRSTCSymbol)) {
4617
      pasSortStruct[iSortStruct].nPriority = nPriority;
37✔
4618
      pasSortStruct[iSortStruct].nApparitionIndex = iSortStruct;
37✔
4619
      iSortStruct++;
37✔
4620
    }
4621

4622
    OGR_ST_Destroy(hStylePart);
45✔
4623
  }
4624

4625
  if (iSortStruct > 1 && !bIsPenBrushOnly) {
39✔
4626
    /* Compute style order based on their explicit priority and apparition order
4627
     */
4628
    qsort(pasSortStruct, iSortStruct, sizeof(StyleSortStruct),
4✔
4629
          msOGRUpdateStyleSortFct);
4630

4631
    /* Now reorder styles in c->styles */
4632
    styleObj **ppsStyleTmp =
4633
        (styleObj **)msSmallMalloc(iSortStruct * sizeof(styleObj *));
4✔
4634
    memcpy(ppsStyleTmp, c->styles + iBaseStyleIndex,
4✔
4635
           iSortStruct * sizeof(styleObj *));
4636
    for (i = 0; i < iSortStruct; i++) {
12✔
4637
      c->styles[iBaseStyleIndex + i] =
8✔
4638
          ppsStyleTmp[pasSortStruct[i].nApparitionIndex];
8✔
4639
    }
4640
    msFree(ppsStyleTmp);
4✔
4641
  }
4642

4643
  msFree(pasSortStruct);
39✔
4644

4645
  return MS_SUCCESS;
39✔
4646
}
4647

4648
static int msOGRUpdateStyleParseLabel(mapObj *map, layerObj *layer, classObj *c,
4✔
4649
                                      OGRStyleToolH hLabelStyle) {
4650
  int bIsNull;
4651
  int r = 0, g = 0, b = 0, t = 0;
4✔
4652

4653
  // Enclose the text string inside quotes to make sure it is seen
4654
  // as a string by the parser inside loadExpression(). (bug185)
4655
  /* See bug 3481 about the isalnum hack */
4656
  const char *labelTextString =
4657
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelTextString, &bIsNull);
4✔
4658

4659
  if (c->numlabels == 0) {
4✔
4660
    /* allocate a new label object */
4661
    if (msGrowClassLabels(c) == NULL)
4✔
4662
      return MS_FAILURE;
4663
    c->numlabels++;
4✔
4664
    initLabel(c->labels[0]);
4✔
4665
  }
4666
  msFreeExpression(&c->labels[0]->text);
4✔
4667
  c->labels[0]->text.type = MS_STRING;
4✔
4668
  c->labels[0]->text.string = msStrdup(labelTextString);
4✔
4669

4670
  c->labels[0]->angle =
8✔
4671
      OGR_ST_GetParamDbl(hLabelStyle, OGRSTLabelAngle, &bIsNull);
4✔
4672

4673
  c->labels[0]->size =
4✔
4674
      OGR_ST_GetParamDbl(hLabelStyle, OGRSTLabelSize, &bIsNull);
4✔
4675
  if (c->labels[0]->size < 1) /* no point dropping to zero size */
4✔
4676
    c->labels[0]->size = 1;
×
4677

4678
  // OGR default is anchor point = LL, so label is at UR of anchor
4679
  c->labels[0]->position = MS_UR;
4✔
4680

4681
  int nPosition = OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelAnchor, &bIsNull);
4✔
4682
  if (!bIsNull) {
4✔
4683
    switch (nPosition) {
4✔
4684
    case 1:
4✔
4685
      c->labels[0]->position = MS_UR;
4✔
4686
      break;
4✔
4687
    case 2:
×
4688
      c->labels[0]->position = MS_UC;
×
4689
      break;
×
4690
    case 3:
×
4691
      c->labels[0]->position = MS_UL;
×
4692
      break;
×
4693
    case 4:
×
4694
      c->labels[0]->position = MS_CR;
×
4695
      break;
×
4696
    case 5:
×
4697
      c->labels[0]->position = MS_CC;
×
4698
      break;
×
UNCOV
4699
    case 6:
×
UNCOV
4700
      c->labels[0]->position = MS_CL;
×
UNCOV
4701
      break;
×
UNCOV
4702
    case 7:
×
UNCOV
4703
      c->labels[0]->position = MS_LR;
×
UNCOV
4704
      break;
×
UNCOV
4705
    case 8:
×
UNCOV
4706
      c->labels[0]->position = MS_LC;
×
UNCOV
4707
      break;
×
UNCOV
4708
    case 9:
×
UNCOV
4709
      c->labels[0]->position = MS_LL;
×
UNCOV
4710
      break;
×
UNCOV
4711
    case 10:
×
UNCOV
4712
      c->labels[0]->position = MS_UR;
×
4713
      break; /*approximate*/
×
4714
    case 11:
×
UNCOV
4715
      c->labels[0]->position = MS_UC;
×
UNCOV
4716
      break;
×
UNCOV
4717
    case 12:
×
UNCOV
4718
      c->labels[0]->position = MS_UL;
×
4719
      break;
×
4720
    default:
4721
      break;
4722
    }
4723
  }
4724

4725
  const char *pszColor =
4726
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFColor, &bIsNull);
4✔
4727
  if (!bIsNull &&
8✔
4728
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
4✔
4729
    MS_INIT_COLOR(c->labels[0]->color, r, g, b, t);
4✔
4730
  }
4731

4732
  pszColor = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelHColor, &bIsNull);
4✔
4733
  if (!bIsNull &&
4✔
UNCOV
4734
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
×
UNCOV
4735
    MS_INIT_COLOR(c->labels[0]->shadowcolor, r, g, b, t);
×
4736
  }
4737

4738
  pszColor = OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelOColor, &bIsNull);
4✔
4739
  if (!bIsNull &&
4✔
UNCOV
4740
      OGR_ST_GetRGBFromString(hLabelStyle, pszColor, &r, &g, &b, &t)) {
×
UNCOV
4741
    MS_INIT_COLOR(c->labels[0]->outlinecolor, r, g, b, t);
×
4742
  }
4743

4744
  const char *pszBold =
4745
      OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelBold, &bIsNull) ? "-bold" : "";
4✔
4746
  const char *pszItalic =
4747
      OGR_ST_GetParamNum(hLabelStyle, OGRSTLabelItalic, &bIsNull) ? "-italic"
4✔
4748
                                                                  : "";
4749
  const char *pszFontName =
4750
      OGR_ST_GetParamStr(hLabelStyle, OGRSTLabelFontName, &bIsNull);
4✔
4751
  /* replace spaces with hyphens to allow mapping to a valid hashtable entry*/
4752
  char *pszFontNameEscaped = NULL;
4753
  if (pszFontName != NULL) {
4✔
4754
    pszFontNameEscaped = msStrdup(pszFontName);
4✔
4755
    msReplaceChar(pszFontNameEscaped, ' ', '-');
4✔
4756
  }
4757

4758
  bool bFont = true;
4759

4760
  if (pszFontNameEscaped != NULL && !bIsNull && pszFontNameEscaped[0] != '\0') {
4✔
4761
    const char *pszName =
4762
        CPLSPrintf("%s%s%s", pszFontNameEscaped, pszBold, pszItalic);
4✔
4763
    if (msLookupHashTable(&(map->fontset.fonts), (char *)pszName) != NULL) {
4✔
UNCOV
4764
      c->labels[0]->font = msStrdup(pszName);
×
UNCOV
4765
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
UNCOV
4766
        msDebug("** Using '%s' TTF font **\n", pszName);
×
4767
    } else if ((strcmp(pszFontNameEscaped, pszName) != 0) &&
4✔
UNCOV
4768
               msLookupHashTable(&(map->fontset.fonts),
×
4769
                                 (char *)pszFontNameEscaped) != NULL) {
UNCOV
4770
      c->labels[0]->font = msStrdup(pszFontNameEscaped);
×
UNCOV
4771
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
UNCOV
4772
        msDebug("** Using '%s' TTF font **\n", pszFontNameEscaped);
×
4773
    } else if (msLookupHashTable(&(map->fontset.fonts), "default") != NULL) {
4✔
UNCOV
4774
      c->labels[0]->font = msStrdup("default");
×
UNCOV
4775
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
×
UNCOV
4776
        msDebug("** Using 'default' TTF font **\n");
×
4777
    } else
4778
      bFont = false;
4779
  }
4780

4781
  msFree(pszFontNameEscaped);
4✔
4782

4783
  if (!bFont) {
4✔
4784
    c->labels[0]->size = MS_MEDIUM;
4✔
4785
  }
4786

4787
  return MS_SUCCESS;
4788
}
4789

4790
static int msOGRUpdateStyleParsePen(mapObj *map, layerObj *layer, styleObj *s,
37✔
4791
                                    OGRStyleToolH hPenStyle, int bIsBrush,
4792
                                    int *pbPriority) {
4793
  int bIsNull;
4794
  int r = 0, g = 0, b = 0, t = 0;
37✔
4795

4796
  const char *pszPenName, *pszPattern, *pszCap, *pszJoin;
4797
  colorObj oPenColor;
4798
  int nPenSymbol = 0;
4799
  int nPenSize = 1;
4800
  t = -1;
37✔
4801
  double pattern[MS_MAXPATTERNLENGTH];
4802
  int patternlength = 0;
4803
  int linecap = MS_CJC_DEFAULT_CAPS;
4804
  int linejoin = MS_CJC_DEFAULT_JOINS;
4805
  double offsetx = 0.0;
4806
  double offsety = 0.0;
4807

4808
  // Make sure pen is always initialized
4809
  MS_INIT_COLOR(oPenColor, -1, -1, -1, 255);
4810

4811
  pszPenName = OGR_ST_GetParamStr(hPenStyle, OGRSTPenId, &bIsNull);
37✔
4812
  if (bIsNull)
37✔
4813
    pszPenName = NULL;
4814
  // Check for Pen Pattern "ogr-pen-1": the invisible pen
4815
  // If that's what we have then set pen color to -1
4816
  if (pszPenName && strstr(pszPenName, "ogr-pen-1") != NULL) {
12✔
4817
    MS_INIT_COLOR(oPenColor, -1, -1, -1, 255);
4818
  } else {
4819
    const char *pszColor =
4820
        OGR_ST_GetParamStr(hPenStyle, OGRSTPenColor, &bIsNull);
37✔
4821
    if (!bIsNull &&
74✔
4822
        OGR_ST_GetRGBFromString(hPenStyle, pszColor, &r, &g, &b, &t)) {
37✔
4823
      MS_INIT_COLOR(oPenColor, r, g, b, t);
37✔
4824
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
37✔
UNCOV
4825
        msDebug("** PEN COLOR = %d %d %d **\n", r, g, b);
×
4826
    }
4827

4828
    nPenSize = OGR_ST_GetParamNum(hPenStyle, OGRSTPenWidth, &bIsNull);
37✔
4829
    if (bIsNull)
37✔
4830
      nPenSize = 1;
4831
    if (pszPenName != NULL) {
37✔
4832
      // Try to match pen name in symbol file
4833
      nPenSymbol =
4834
          msOGRGetSymbolId(&(map->symbolset), pszPenName, NULL, MS_FALSE);
12✔
4835
    }
4836
  }
4837

4838
  if (layer->debug >= MS_DEBUGLEVEL_VVV)
37✔
UNCOV
4839
    msDebug("** PEN COLOR = %d %d %d **\n", oPenColor.red, oPenColor.green,
×
4840
            oPenColor.blue);
4841

4842
  pszPattern = OGR_ST_GetParamStr(hPenStyle, OGRSTPenPattern, &bIsNull);
37✔
4843
  if (bIsNull)
37✔
4844
    pszPattern = NULL;
4845
  if (pszPattern != NULL) {
3✔
4846
    char **papszTokens =
4847
        CSLTokenizeStringComplex(pszPattern, " ", FALSE, FALSE);
3✔
4848
    int nTokenCount = CSLCount(papszTokens);
3✔
4849
    int bValidFormat = TRUE;
4850
    if (nTokenCount >= 2 && nTokenCount <= MS_MAXPATTERNLENGTH) {
3✔
4851
      for (int i = 0; i < nTokenCount; i++) {
6✔
4852
        if (strlen(papszTokens[i]) > 2 &&
5✔
4853
            strcmp(papszTokens[i] + strlen(papszTokens[i]) - 2, "px") == 0) {
4✔
4854
          pattern[patternlength++] = CPLAtof(papszTokens[i]);
4✔
4855
        } else {
4856
          bValidFormat = FALSE;
4857
          patternlength = 0;
4858
          break;
4859
        }
4860
      }
4861
    } else
4862
      bValidFormat = FALSE;
4863
    if (!bValidFormat && layer->debug >= MS_DEBUGLEVEL_VVV)
3✔
UNCOV
4864
      msDebug("Invalid/unhandled pen pattern format = %s\n", pszPattern);
×
4865
    CSLDestroy(papszTokens);
3✔
4866
  }
4867

4868
  pszCap = OGR_ST_GetParamStr(hPenStyle, OGRSTPenCap, &bIsNull);
37✔
4869
  if (bIsNull)
37✔
4870
    pszCap = NULL;
4871
  if (pszCap != NULL) {
4✔
4872
    /* Note: the default in OGR Feature style is BUTT, but the MapServer */
4873
    /* default is ROUND. Currently use MapServer default. */
4874
    if (strcmp(pszCap, "b") == 0) /* BUTT */
4✔
4875
      linecap = MS_CJC_BUTT;
4876
    else if (strcmp(pszCap, "r") == 0) /* ROUND */
3✔
4877
      linecap = MS_CJC_ROUND;
4878
    else if (strcmp(pszCap, "p") == 0) /* PROJECTING */
2✔
4879
      linecap = MS_CJC_SQUARE;
4880
    else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
UNCOV
4881
      msDebug("Invalid/unhandled pen cap = %s\n", pszCap);
×
4882
  }
4883

4884
  pszJoin = OGR_ST_GetParamStr(hPenStyle, OGRSTPenJoin, &bIsNull);
37✔
4885
  if (bIsNull)
37✔
4886
    pszJoin = NULL;
4887
  if (pszJoin != NULL) {
4✔
4888
    /* Note: the default in OGR Feature style is MITER, but the MapServer */
4889
    /* default is NONE. Currently use MapServer default. */
4890
    if (strcmp(pszJoin, "m") == 0) /* MITTER */
4✔
4891
      linejoin = MS_CJC_MITER;
4892
    else if (strcmp(pszJoin, "r") == 0) /* ROUND */
3✔
4893
      linejoin = MS_CJC_ROUND;
4894
    else if (strcmp(pszJoin, "b") == 0) /* BEVEL */
2✔
4895
      linejoin = MS_CJC_BEVEL;
4896
    else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
UNCOV
4897
      msDebug("Invalid/unhandled pen join = %s\n", pszJoin);
×
4898
  }
4899

4900
  offsetx = OGR_ST_GetParamDbl(hPenStyle, OGRSTPenPerOffset, &bIsNull);
37✔
4901
  if (bIsNull)
37✔
4902
    offsetx = 0;
4903
  if (offsetx != 0.0) {
2✔
4904
    /* OGR feature style and MapServer conventions related to offset */
4905
    /* sign are the same : negative values for left of line, positive for */
4906
    /* right of line */
4907
    offsety = MS_STYLE_SINGLE_SIDED_OFFSET;
4908
  }
4909

4910
  if (bIsBrush || layer->type == MS_LAYER_POLYGON) {
37✔
4911
    // This is a multipart symbology, so pen defn goes in the
4912
    // overlaysymbol params
4913
    s->outlinecolor = oPenColor;
10✔
4914
  } else {
4915
    // Single part symbology
4916
    s->color = oPenColor;
27✔
4917
  }
4918

4919
  s->symbol = nPenSymbol;
37✔
4920
  s->size = nPenSize;
37✔
4921
  s->width = nPenSize;
37✔
4922
  s->linecap = linecap;
37✔
4923
  s->linejoin = linejoin;
37✔
4924
  s->offsetx = offsetx;
37✔
4925
  s->offsety = offsety;
37✔
4926
  s->patternlength = patternlength;
37✔
4927
  if (patternlength > 0)
37✔
4928
    memcpy(s->pattern, pattern, sizeof(double) * patternlength);
1✔
4929

4930
  int nPriority = OGR_ST_GetParamNum(hPenStyle, OGRSTPenPriority, &bIsNull);
37✔
4931
  if (!bIsNull)
37✔
4932
    *pbPriority = nPriority;
6✔
4933

4934
  return MS_SUCCESS;
37✔
4935
}
4936

4937
static int msOGRAddBgColorStyleParseBrush(styleObj *s,
3✔
4938
                                          OGRStyleToolH hBrushStyle) {
4939
  int bIsNull;
4940
  int r = 0, g = 0, b = 0, t = 0;
3✔
4941
  const char *pszColor =
4942
      OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushBColor, &bIsNull);
3✔
4943

4944
  if (!bIsNull &&
6✔
4945
      OGR_ST_GetRGBFromString(hBrushStyle, pszColor, &r, &g, &b, &t)) {
3✔
4946
    MS_INIT_COLOR(s->color, r, g, b, t);
3✔
4947
  }
4948
  return MS_SUCCESS;
3✔
4949
}
4950

4951
static int msOGRUpdateStyleParseBrush(mapObj *map, layerObj *layer, styleObj *s,
4✔
4952
                                      OGRStyleToolH hBrushStyle, int *pbIsBrush,
4953
                                      int *pbPriority) {
4954
  int bIsNull;
4955
  int r = 0, g = 0, b = 0, t = 0;
4✔
4956

4957
  const char *pszBrushName =
4958
      OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushId, &bIsNull);
4✔
4959
  if (bIsNull)
4✔
4960
    pszBrushName = NULL;
4961

4962
  // Check for Brush Pattern "ogr-brush-1": the invisible fill
4963
  // If that's what we have then set fill color to -1
4964
  if (pszBrushName && strstr(pszBrushName, "ogr-brush-1") != NULL) {
4✔
UNCOV
4965
    MS_INIT_COLOR(s->color, -1, -1, -1, 255);
×
4966
  } else {
4967
    *pbIsBrush = TRUE;
4✔
4968
    const char *pszColor =
4969
        OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushFColor, &bIsNull);
4✔
4970
    if (!bIsNull &&
8✔
4971
        OGR_ST_GetRGBFromString(hBrushStyle, pszColor, &r, &g, &b, &t)) {
4✔
4972
      MS_INIT_COLOR(s->color, r, g, b, t);
4✔
4973

4974
      if (layer->debug >= MS_DEBUGLEVEL_VVV)
4✔
UNCOV
4975
        msDebug("** BRUSH COLOR = %d %d %d **\n", r, g, b);
×
4976
    }
4977

4978
    // Symbol name mapping:
4979
    // First look for the native symbol name, then the ogr-...
4980
    // generic name.
4981
    // If none provided or found then use 0: solid fill
4982

4983
    const char *pszName =
4984
        OGR_ST_GetParamStr(hBrushStyle, OGRSTBrushId, &bIsNull);
4✔
4985
    s->symbol = msOGRGetSymbolId(&(map->symbolset), pszName, NULL, MS_FALSE);
4✔
4986

4987
    double angle = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushAngle, &bIsNull);
4✔
4988
    if (!bIsNull)
4✔
4989
      s->angle = angle;
1✔
4990

4991
    double size = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushSize, &bIsNull);
4✔
4992
    if (!bIsNull)
4✔
4993
      s->size = size;
3✔
4994

4995
    double spacingx = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDx, &bIsNull);
4✔
4996
    if (!bIsNull) {
4✔
4997
      double spacingy = OGR_ST_GetParamDbl(hBrushStyle, OGRSTBrushDy, &bIsNull);
4✔
4998
      if (!bIsNull) {
4✔
4999
        if (spacingx == spacingy)
4✔
5000
          s->gap = spacingx;
3✔
5001
        else if (layer->debug >= MS_DEBUGLEVEL_VVV)
1✔
5002
          msDebug("Ignoring brush dx and dy since they don't have the same "
×
5003
                  "value\n");
5004
      }
5005
    }
5006
  }
5007

5008
  int nPriority = OGR_ST_GetParamNum(hBrushStyle, OGRSTBrushPriority, &bIsNull);
4✔
5009
  if (!bIsNull)
4✔
5010
    *pbPriority = nPriority;
×
5011

5012
  return MS_SUCCESS;
4✔
5013
}
5014

5015
static int msOGRUpdateStyleParseSymbol(mapObj *map, styleObj *s,
×
5016
                                       OGRStyleToolH hSymbolStyle,
5017
                                       int *pbPriority) {
5018
  int bIsNull;
UNCOV
5019
  int r = 0, g = 0, b = 0, t = 0;
×
5020

5021
  const char *pszColor =
UNCOV
5022
      OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolColor, &bIsNull);
×
5023
  if (!bIsNull &&
×
5024
      OGR_ST_GetRGBFromString(hSymbolStyle, pszColor, &r, &g, &b, &t)) {
×
UNCOV
5025
    MS_INIT_COLOR(s->color, r, g, b, t);
×
5026
  }
5027

UNCOV
5028
  pszColor = OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolOColor, &bIsNull);
×
5029
  if (!bIsNull &&
×
UNCOV
5030
      OGR_ST_GetRGBFromString(hSymbolStyle, pszColor, &r, &g, &b, &t)) {
×
UNCOV
5031
    MS_INIT_COLOR(s->outlinecolor, r, g, b, t);
×
5032
  }
5033

UNCOV
5034
  s->angle = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolAngle, &bIsNull);
×
UNCOV
5035
  double dfTmp = OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolSize, &bIsNull);
×
UNCOV
5036
  if (!bIsNull)
×
5037
    s->size = dfTmp;
×
5038

5039
  // Symbol name mapping:
5040
  // First look for the native symbol name, then the ogr-...
5041
  // generic name, and in last resort try "default-marker" if
5042
  // provided by user.
5043
  const char *pszName =
UNCOV
5044
      OGR_ST_GetParamStr(hSymbolStyle, OGRSTSymbolId, &bIsNull);
×
UNCOV
5045
  if (bIsNull)
×
5046
    pszName = NULL;
5047

5048
  int try_addimage_if_notfound = MS_FALSE;
5049
#ifdef USE_CURL
UNCOV
5050
  if (pszName && strncasecmp(pszName, "http", 4) == 0)
×
5051
    try_addimage_if_notfound = MS_TRUE;
5052
#endif
UNCOV
5053
  if (!s->symbolname)
×
UNCOV
5054
    s->symbol = msOGRGetSymbolId(&(map->symbolset), pszName, "default-marker",
×
5055
                                 try_addimage_if_notfound);
5056

5057
  int nPriority =
UNCOV
5058
      OGR_ST_GetParamNum(hSymbolStyle, OGRSTSymbolPriority, &bIsNull);
×
UNCOV
5059
  if (!bIsNull)
×
5060
    *pbPriority = nPriority;
×
5061

5062
  return MS_SUCCESS;
×
5063
}
5064

5065
/**********************************************************************
5066
 *                     msOGRLayerGetAutoStyle()
5067
 *
5068
 * Fills a classObj with style info from the specified shape.
5069
 * For optimal results, this should be called immediately after
5070
 * GetNextShape() or GetShape() so that the shape doesn't have to be read
5071
 * twice.
5072
 *
5073
 * The returned classObj is a ref. to a static structure valid only until
5074
 * the next call and that shouldn't be freed by the caller.
5075
 **********************************************************************/
5076
static int msOGRLayerGetAutoStyle(mapObj *map, layerObj *layer, classObj *c,
38✔
5077
                                  shapeObj *shape) {
5078
  msOGRFileInfo *psInfo = (msOGRFileInfo *)layer->layerinfo;
38✔
5079

5080
  if (psInfo == NULL || psInfo->hLayer == NULL) {
38✔
5081
    msSetError(MS_MISCERR, "Assertion failed: OGR layer not opened!!!",
×
5082
               "msOGRLayerGetAutoStyle()");
UNCOV
5083
    return (MS_FAILURE);
×
5084
  }
5085

5086
  if (layer->tileindex != NULL) {
38✔
UNCOV
5087
    if ((psInfo->poCurTile == NULL ||
×
UNCOV
5088
         shape->tileindex != psInfo->poCurTile->nTileId) &&
×
UNCOV
5089
        msOGRFileReadTile(layer, psInfo) != MS_SUCCESS)
×
5090
      return MS_FAILURE;
5091

UNCOV
5092
    psInfo = psInfo->poCurTile;
×
5093
  }
5094

5095
  /* ------------------------------------------------------------------
5096
   * Read shape or reuse ref. to last shape read.
5097
   * ------------------------------------------------------------------ */
5098
  ACQUIRE_OGR_LOCK;
38✔
5099
  if (psInfo->hLastFeature == NULL ||
38✔
5100
      psInfo->last_record_index_read != shape->resultindex) {
38✔
UNCOV
5101
    RELEASE_OGR_LOCK;
×
UNCOV
5102
    msSetError(MS_MISCERR,
×
5103
               "Assertion failed: AutoStyle not requested on loaded shape.",
5104
               "msOGRLayerGetAutoStyle()");
UNCOV
5105
    return (MS_FAILURE);
×
5106
  }
5107

5108
  /* ------------------------------------------------------------------
5109
   * Reset style info in the class to defaults
5110
   * the only members we don't touch are name, expression, and join/query stuff
5111
   * ------------------------------------------------------------------ */
5112
  resetClassStyle(c);
38✔
5113
  if (msMaybeAllocateClassStyle(c, 0)) {
38✔
UNCOV
5114
    RELEASE_OGR_LOCK;
×
UNCOV
5115
    return (MS_FAILURE);
×
5116
  }
5117

5118
  // __TODO__ label cache incompatible with styleitem feature.
5119
  layer->labelcache = MS_OFF;
38✔
5120

5121
  int nRetVal = MS_SUCCESS;
5122
  if (psInfo->hLastFeature) {
38✔
5123
    OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
38✔
5124
    OGR_SM_InitFromFeature(hStyleMgr, psInfo->hLastFeature);
38✔
5125
    nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
38✔
5126
    OGR_SM_Destroy(hStyleMgr);
38✔
5127
  }
5128

5129
  RELEASE_OGR_LOCK;
38✔
5130
  return nRetVal;
38✔
5131
}
5132

5133
/**********************************************************************
5134
 *                     msOGRUpdateStyleFromString()
5135
 *
5136
 * Fills a classObj with style info from the specified style string.
5137
 * For optimal results, this should be called immediately after
5138
 * GetNextShape() or GetShape() so that the shape doesn't have to be read
5139
 * twice.
5140
 *
5141
 * The returned classObj is a ref. to a static structure valid only until
5142
 * the next call and that shouldn't be freed by the caller.
5143
 **********************************************************************/
5144
int msOGRUpdateStyleFromString(mapObj *map, layerObj *layer, classObj *c,
1✔
5145
                               const char *stylestring) {
5146
  /* ------------------------------------------------------------------
5147
   * Reset style info in the class to defaults
5148
   * the only members we don't touch are name, expression, and join/query stuff
5149
   * ------------------------------------------------------------------ */
5150
  resetClassStyle(c);
1✔
5151
  if (msMaybeAllocateClassStyle(c, 0)) {
1✔
5152
    return (MS_FAILURE);
5153
  }
5154

5155
  // __TODO__ label cache incompatible with styleitem feature.
5156
  layer->labelcache = MS_OFF;
1✔
5157

5158
  int nRetVal = MS_SUCCESS;
5159

5160
  ACQUIRE_OGR_LOCK;
1✔
5161

5162
  OGRStyleMgrH hStyleMgr = OGR_SM_Create(NULL);
1✔
5163
  OGR_SM_InitStyleString(hStyleMgr, stylestring);
1✔
5164
  nRetVal = msOGRUpdateStyle(hStyleMgr, map, layer, c);
1✔
5165
  OGR_SM_Destroy(hStyleMgr);
1✔
5166

5167
  RELEASE_OGR_LOCK;
1✔
5168
  return nRetVal;
1✔
5169
}
5170

5171
/************************************************************************/
5172
/*                           msOGRLCleanup()                            */
5173
/************************************************************************/
5174

5175
void msOGRCleanup(void)
2,831✔
5176

5177
{
5178
  ACQUIRE_OGR_LOCK;
2,831✔
5179
  if (bOGRDriversRegistered == MS_TRUE) {
2,831✔
5180
    CPLPopErrorHandler();
369✔
5181
    bOGRDriversRegistered = MS_FALSE;
369✔
5182
  }
5183
  RELEASE_OGR_LOCK;
2,831✔
5184
}
2,831✔
5185

5186
/************************************************************************/
5187
/*                           msOGREscapeSQLParam                        */
5188
/************************************************************************/
5189
char *msOGREscapePropertyName(layerObj *layer, const char *pszString) {
1,689✔
5190
  char *pszEscapedStr = NULL;
5191
  if (layer && pszString && strlen(pszString) > 0) {
1,689✔
5192
    pszEscapedStr = (char *)msSmallMalloc(strlen(pszString) * 2 + 1);
1,689✔
5193
    int j = 0;
5194
    for (int i = 0; pszString[i] != '\0'; ++i) {
13,650✔
5195
      if (pszString[i] == '"') {
11,961✔
5196
        pszEscapedStr[j++] = '"';
1✔
5197
        pszEscapedStr[j++] = '"';
1✔
5198
      } else
5199
        pszEscapedStr[j++] = pszString[i];
11,960✔
5200
    }
5201
    pszEscapedStr[j] = 0;
1,689✔
5202
  }
5203
  return pszEscapedStr;
1,689✔
5204
}
5205

5206
static void msOGREnablePaging(layerObj *layer, int value) {
242✔
5207
  msOGRFileInfo *layerinfo = NULL;
5208

5209
  if (layer->debug) {
242✔
5210
    msDebug("msOGREnablePaging(%d) called.\n", value);
2✔
5211
  }
5212

5213
  if (!msOGRLayerIsOpen(layer)) {
UNCOV
5214
    if (msOGRLayerOpenVT(layer) != MS_SUCCESS) {
×
5215
      return;
5216
    }
5217
  }
5218

5219
  assert(layer->layerinfo != NULL);
5220

5221
  layerinfo = (msOGRFileInfo *)layer->layerinfo;
242✔
5222
  layerinfo->bPaging = value;
242✔
5223
}
5224

5225
static int msOGRGetPaging(layerObj *layer) {
270✔
5226
  msOGRFileInfo *layerinfo = NULL;
5227

5228
  if (layer->debug) {
270✔
5229
    msDebug("msOGRGetPaging called.\n");
2✔
5230
  }
5231

5232
  if (!msOGRLayerIsOpen(layer)) {
5233
    if (msOGRLayerOpenVT(layer) != MS_SUCCESS) {
87✔
5234
      return FALSE;
5235
    }
5236
  }
5237

5238
  assert(layer->layerinfo != NULL);
5239

5240
  layerinfo = (msOGRFileInfo *)layer->layerinfo;
270✔
5241
  return layerinfo->bPaging;
270✔
5242
}
5243

5244
/************************************************************************/
5245
/*                  msOGRLayerInitializeVirtualTable()                  */
5246
/************************************************************************/
5247
int msOGRLayerInitializeVirtualTable(layerObj *layer) {
1,163✔
5248
  assert(layer != NULL);
5249
  assert(layer->vtable != NULL);
5250

5251
  layer->vtable->LayerTranslateFilter = msOGRTranslateMsExpressionToOGRSQL;
1,163✔
5252

5253
  /* layer->vtable->LayerSupportsCommonFilters, use default */
5254
  layer->vtable->LayerInitItemInfo = msOGRLayerInitItemInfo;
1,163✔
5255
  layer->vtable->LayerFreeItemInfo = msOGRLayerFreeItemInfo;
1,163✔
5256
  layer->vtable->LayerOpen = msOGRLayerOpenVT;
1,163✔
5257
  layer->vtable->LayerIsOpen = msOGRLayerIsOpen;
1,163✔
5258
  layer->vtable->LayerWhichShapes = msOGRLayerWhichShapes;
1,163✔
5259
  layer->vtable->LayerNextShape = msOGRLayerNextShape;
1,163✔
5260
  layer->vtable->LayerGetShape = msOGRLayerGetShape;
1,163✔
5261
  /* layer->vtable->LayerGetShapeCount, use default */
5262
  layer->vtable->LayerClose = msOGRLayerClose;
1,163✔
5263
  layer->vtable->LayerGetItems = msOGRLayerGetItems;
1,163✔
5264
  layer->vtable->LayerGetExtent = msOGRLayerGetExtent;
1,163✔
5265
  layer->vtable->LayerGetAutoStyle = msOGRLayerGetAutoStyle;
1,163✔
5266
  /* layer->vtable->LayerCloseConnection, use default */
5267
  /* layer->vtable->LayerApplyFilterToLayer, use default */
5268
  layer->vtable->LayerSetTimeFilter = msLayerMakeBackticsTimeFilter;
1,163✔
5269
  /* layer->vtable->LayerCreateItems, use default */
5270
  layer->vtable->LayerGetNumFeatures = msOGRLayerGetNumFeatures;
1,163✔
5271
  /* layer->vtable->LayerGetAutoProjection, use default*/
5272

5273
  layer->vtable->LayerEscapeSQLParam = msOGREscapeSQLParam;
1,163✔
5274
  layer->vtable->LayerEscapePropertyName = msOGREscapePropertyName;
1,163✔
5275
  layer->vtable->LayerEnablePaging = msOGREnablePaging;
1,163✔
5276
  layer->vtable->LayerGetPaging = msOGRGetPaging;
1,163✔
5277
  return MS_SUCCESS;
1,163✔
5278
}
5279

5280
/************************************************************************/
5281
/*                         msOGRShapeFromWKT()                          */
5282
/************************************************************************/
UNCOV
5283
shapeObj *msOGRShapeFromWKT(const char *string) {
×
5284

5285
  OGRGeometryH hGeom = NULL;
×
5286
  shapeObj *shape = NULL;
5287

UNCOV
5288
  if (!string)
×
5289
    return NULL;
5290

UNCOV
5291
  if (OGR_G_CreateFromWkt((char **)&string, NULL, &hGeom) != OGRERR_NONE) {
×
UNCOV
5292
    msSetError(MS_OGRERR, "Failed to parse WKT string.", "msOGRShapeFromWKT()");
×
UNCOV
5293
    return NULL;
×
5294
  }
5295

5296
  /* Initialize a corresponding shapeObj */
5297

5298
  shape = (shapeObj *)malloc(sizeof(shapeObj));
×
UNCOV
5299
  msInitShape(shape);
×
5300

5301
  /* translate WKT into an OGRGeometry. */
5302

5303
  if (msOGRGeometryToShape(hGeom, shape,
×
5304
                           wkbFlatten(OGR_G_GetGeometryType(hGeom))) ==
5305
      MS_FAILURE) {
5306
    msFreeShape(shape);
×
5307
    free(shape);
×
5308
    shape = NULL;
5309
  }
5310

5311
  OGR_G_DestroyGeometry(hGeom);
×
5312

5313
  return shape;
5314
}
5315

5316
/************************************************************************/
5317
/*                          msOGRShapeToWKT()                           */
5318
/************************************************************************/
5319
char *msOGRShapeToWKT(shapeObj *shape) {
×
5320
  OGRGeometryH hGeom = NULL;
5321
  int i;
5322
  char *wkt = NULL;
5323

5324
  if (!shape)
×
5325
    return NULL;
5326

UNCOV
5327
  if (shape->type == MS_SHAPE_POINT && shape->numlines == 1 &&
×
5328
      shape->line[0].numpoints == 1) {
×
5329
    hGeom = OGR_G_CreateGeometry(wkbPoint);
×
UNCOV
5330
    OGR_G_SetPoint_2D(hGeom, 0, shape->line[0].point[0].x,
×
UNCOV
5331
                      shape->line[0].point[0].y);
×
5332
  } else if (shape->type == MS_SHAPE_POINT && shape->numlines == 1 &&
×
5333
             shape->line[0].numpoints > 1) {
×
5334
    hGeom = OGR_G_CreateGeometry(wkbMultiPoint);
×
5335
    for (i = 0; i < shape->line[0].numpoints; i++) {
×
5336
      OGRGeometryH hPoint;
5337

UNCOV
5338
      hPoint = OGR_G_CreateGeometry(wkbPoint);
×
5339
      OGR_G_SetPoint_2D(hPoint, 0, shape->line[0].point[i].x,
×
UNCOV
5340
                        shape->line[0].point[i].y);
×
UNCOV
5341
      OGR_G_AddGeometryDirectly(hGeom, hPoint);
×
5342
    }
5343
  } else if (shape->type == MS_SHAPE_LINE && shape->numlines == 1) {
×
UNCOV
5344
    hGeom = OGR_G_CreateGeometry(wkbLineString);
×
UNCOV
5345
    for (i = 0; i < shape->line[0].numpoints; i++) {
×
UNCOV
5346
      OGR_G_AddPoint_2D(hGeom, shape->line[0].point[i].x,
×
5347
                        shape->line[0].point[i].y);
×
5348
    }
UNCOV
5349
  } else if (shape->type == MS_SHAPE_LINE && shape->numlines > 1) {
×
5350
    OGRGeometryH hMultiLine = OGR_G_CreateGeometry(wkbMultiLineString);
×
5351
    int iLine;
5352

5353
    for (iLine = 0; iLine < shape->numlines; iLine++) {
×
5354
      hGeom = OGR_G_CreateGeometry(wkbLineString);
×
UNCOV
5355
      for (i = 0; i < shape->line[iLine].numpoints; i++) {
×
5356
        OGR_G_AddPoint_2D(hGeom, shape->line[iLine].point[i].x,
×
UNCOV
5357
                          shape->line[iLine].point[i].y);
×
5358
      }
5359

UNCOV
5360
      OGR_G_AddGeometryDirectly(hMultiLine, hGeom);
×
5361
    }
5362

5363
    hGeom = hMultiLine;
UNCOV
5364
  } else if (shape->type == MS_SHAPE_POLYGON) {
×
5365
    int iLine;
5366

5367
    /* actually, it is pretty hard to be sure rings 1+ are interior */
UNCOV
5368
    hGeom = OGR_G_CreateGeometry(wkbPolygon);
×
UNCOV
5369
    for (iLine = 0; iLine < shape->numlines; iLine++) {
×
5370
      OGRGeometryH hRing;
UNCOV
5371
      hRing = OGR_G_CreateGeometry(wkbLinearRing);
×
5372

UNCOV
5373
      for (i = 0; i < shape->line[iLine].numpoints; i++) {
×
UNCOV
5374
        OGR_G_AddPoint_2D(hRing, shape->line[iLine].point[i].x,
×
UNCOV
5375
                          shape->line[iLine].point[i].y);
×
5376
      }
UNCOV
5377
      OGR_G_AddGeometryDirectly(hGeom, hRing);
×
5378
    }
5379
  } else {
UNCOV
5380
    msSetError(MS_OGRERR, "OGR support is not available.", "msOGRShapeToWKT()");
×
5381
  }
5382

UNCOV
5383
  if (hGeom != NULL) {
×
5384
    char *pszOGRWkt;
5385

UNCOV
5386
    OGR_G_ExportToWkt(hGeom, &pszOGRWkt);
×
UNCOV
5387
    wkt = msStrdup(pszOGRWkt);
×
UNCOV
5388
    CPLFree(pszOGRWkt);
×
5389
  }
5390

5391
  return wkt;
5392
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc