1
/******************************************************************************
2
* $Id: mappostgis.c 10760 2010-11-24 17:29:47Z pramsey $
5
* Purpose: PostGIS CONNECTIONTYPE support.
6
* Author: Paul Ramsey <pramsey@cleverelephant.ca>
7
* Dave Blasby <dblasby@gmail.com>
9
******************************************************************************
10
* Copyright (c) 2008 Paul Ramsey
11
* Copyright (c) 2002 Refractions Research
13
* Permission is hereby granted, free of charge, to any person obtaining a
14
* copy of this software and associated documentation files (the "Software"),
15
* to deal in the Software without restriction, including without limitation
16
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
17
* and/or sell copies of the Software, and to permit persons to whom the
18
* Software is furnished to do so, subject to the following conditions:
20
* The above copyright notice and this permission notice shall be included in
21
* all copies of this Software or works derived from this Software.
23
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29
* DEALINGS IN THE SOFTWARE.
30
****************************************************************************/
33
** Some theory of operation:
35
** Build SQL from DATA statement and LAYER state. SQL is always of the form:
37
** SELECT [this, that, other], geometry, uid
38
** FROM [table|(subquery) as sub]
39
** WHERE [box] AND [filter]
41
** So the geometry always resides at layer->numitems and the uid always
42
** resides at layer->numitems + 1
44
** Geometry is requested as Base64 encoded WKB. The endian is always requested
45
** as the client endianness.
47
** msPostGISLayerWhichShapes creates SQL based on DATA and LAYER state,
48
** executes it, and places the un-read PGresult handle in the layerinfo->pgresult,
49
** setting the layerinfo->rownum to 0.
51
** msPostGISNextShape reads a row, increments layerinfo->rownum, and returns
52
** MS_SUCCESS, until rownum reaches ntuples, and it returns MS_DONE instead.
56
/* GNU needs this for strcasestr */
61
#include "mapserver.h"
63
#include "mappostgis.h"
66
#define FLT_MAX 25000000.0
71
MS_CVSID("$Id: mappostgis.c 10760 2010-11-24 17:29:47Z pramsey $")
74
** msPostGISCloseConnection()
76
** Handler registered witih msConnPoolRegister so that Mapserver
77
** can clean up open connections during a shutdown.
79
void msPostGISCloseConnection(void *pgconn) {
80
PQfinish((PGconn*)pgconn);
84
** msPostGISCreateLayerInfo()
86
msPostGISLayerInfo *msPostGISCreateLayerInfo(void) {
87
msPostGISLayerInfo *layerinfo = malloc(sizeof(msPostGISLayerInfo));
88
layerinfo->sql = NULL;
89
layerinfo->srid = NULL;
90
layerinfo->uid = NULL;
91
layerinfo->pgconn = NULL;
92
layerinfo->pgresult = NULL;
93
layerinfo->geomcolumn = NULL;
94
layerinfo->fromsource = NULL;
95
layerinfo->endian = 0;
96
layerinfo->rownum = 0;
101
** msPostGISFreeLayerInfo()
103
void msPostGISFreeLayerInfo(layerObj *layer) {
104
msPostGISLayerInfo *layerinfo = NULL;
105
layerinfo = (msPostGISLayerInfo*)layer->layerinfo;
106
if ( layerinfo->sql ) free(layerinfo->sql);
107
if ( layerinfo->uid ) free(layerinfo->uid);
108
if ( layerinfo->srid ) free(layerinfo->srid);
109
if ( layerinfo->geomcolumn ) free(layerinfo->geomcolumn);
110
if ( layerinfo->fromsource ) free(layerinfo->fromsource);
111
if ( layerinfo->pgresult ) PQclear(layerinfo->pgresult);
112
if ( layerinfo->pgconn ) msConnPoolRelease(layer, layerinfo->pgconn);
114
layer->layerinfo = NULL;
119
** postgresqlNoticeHandler()
121
** Propagate messages from the database to the Mapserver log,
122
** set in PQsetNoticeProcessor during layer open.
124
void postresqlNoticeHandler(void *arg, const char *message) {
129
msDebug("%s\n", message);
134
** TODO, review and clean
136
static int force_to_points(unsigned char *wkb, shapeObj *shape)
138
/* we're going to make a 'line' for each entity (point, line or ring) in the geom collection */
144
int type, nrings, npoints;
145
lineObj line = {0, NULL};
147
shape->type = MS_SHAPE_NULL; /* nothing in it */
149
memcpy(&ngeoms, &wkb[5], 4);
150
offset = 9; /* where the first geometry is */
152
for(t=0; t < ngeoms; t++) {
153
memcpy(&type, &wkb[offset + 1], 4); /* type of this geometry */
157
shape->type = MS_SHAPE_POINT;
159
line.point = (pointObj *) malloc(sizeof(pointObj));
161
memcpy(&line.point[0].x, &wkb[offset + 5], 8);
162
memcpy(&line.point[0].y, &wkb[offset + 5 + 8], 8);
164
msAddLine(shape, &line);
166
} else if(type == 2) {
168
shape->type = MS_SHAPE_POINT;
170
memcpy(&line.numpoints, &wkb[offset+5], 4); /* num points */
171
line.point = (pointObj *) malloc(sizeof(pointObj) * line.numpoints);
172
for(u = 0; u < line.numpoints; u++) {
173
memcpy( &line.point[u].x, &wkb[offset+9 + (16 * u)], 8);
174
memcpy( &line.point[u].y, &wkb[offset+9 + (16 * u)+8], 8);
176
offset += 9 + 16 * line.numpoints; /* length of object */
177
msAddLine(shape, &line);
179
} else if(type == 3) {
181
shape->type = MS_SHAPE_POINT;
183
memcpy(&nrings, &wkb[offset+5],4); /* num rings */
184
/* add a line for each polygon ring */
186
offset += 9; /* now points at 1st linear ring */
187
for(u = 0; u < nrings; u++) {
188
/* for each ring, make a line */
189
memcpy(&npoints, &wkb[offset], 4); /* num points */
190
line.numpoints = npoints;
191
line.point = (pointObj *) malloc(sizeof(pointObj)* npoints);
193
for(v = 0; v < npoints; v++)
195
memcpy(&line.point[v].x, &wkb[offset + 4 + (16 * v)], 8);
196
memcpy(&line.point[v].y, &wkb[offset + 4 + (16 * v) + 8], 8);
198
/* make offset point to next linear ring */
199
msAddLine(shape, &line);
201
offset += 4 + 16 *npoints;
209
/* convert the wkb into lines */
210
/* points-> remove */
211
/* lines -> pass through */
212
/* polys -> treat rings as lines */
214
static int force_to_lines(unsigned char *wkb, shapeObj *shape)
220
int type, nrings, npoints;
221
lineObj line = {0, NULL};
223
shape->type = MS_SHAPE_NULL; /* nothing in it */
225
memcpy(&ngeoms, &wkb[5], 4);
226
offset = 9; /* were the first geometry is */
227
for(t=0; t < ngeoms; t++) {
228
memcpy(&type, &wkb[offset + 1], 4); /* type of this geometry */
230
/* cannot do anything with a point */
234
shape->type = MS_SHAPE_LINE;
236
memcpy(&line.numpoints, &wkb[offset + 5], 4);
237
line.point = (pointObj*) malloc(sizeof(pointObj) * line.numpoints );
238
for(u=0; u < line.numpoints; u++) {
239
memcpy(&line.point[u].x, &wkb[offset + 9 + (16 * u)], 8);
240
memcpy(&line.point[u].y, &wkb[offset + 9 + (16 * u)+8], 8);
242
offset += 9 + 16 * line.numpoints; /* length of object */
243
msAddLine(shape, &line);
245
} else if(type == 3) {
247
shape->type = MS_SHAPE_LINE;
249
memcpy(&nrings, &wkb[offset + 5], 4); /* num rings */
250
/* add a line for each polygon ring */
252
offset += 9; /* now points at 1st linear ring */
253
for(u = 0; u < nrings; u++) {
254
/* for each ring, make a line */
255
memcpy(&npoints, &wkb[offset], 4);
256
line.numpoints = npoints;
257
line.point = (pointObj*) malloc(sizeof(pointObj) * npoints);
259
for(v = 0; v < npoints; v++) {
260
memcpy(&line.point[v].x, &wkb[offset + 4 + (16 * v)], 8);
261
memcpy(&line.point[v].y, &wkb[offset + 4 + (16 * v) + 8], 8);
263
/* make offset point to next linear ring */
264
msAddLine(shape, &line);
266
offset += 4 + 16 * npoints;
274
/* point -> reject */
276
/* polygon -> lines of linear rings */
277
static int force_to_polygons(unsigned char *wkb, shapeObj *shape)
283
int type, nrings, npoints;
284
lineObj line = {0, NULL};
286
shape->type = MS_SHAPE_NULL; /* nothing in it */
288
memcpy(&ngeoms, &wkb[5], 4);
289
offset = 9; /* were the first geometry is */
290
for(t = 0; t < ngeoms; t++) {
291
memcpy(&type, &wkb[offset + 1], 4); /* type of this geometry */
295
shape->type = MS_SHAPE_POLYGON;
297
memcpy(&nrings, &wkb[offset + 5], 4); /* num rings */
298
/* add a line for each polygon ring */
300
offset += 9; /* now points at 1st linear ring */
301
for(u=0; u < nrings; u++) {
302
/* for each ring, make a line */
303
memcpy(&npoints, &wkb[offset], 4); /* num points */
304
line.numpoints = npoints;
305
line.point = (pointObj*) malloc(sizeof(pointObj) * npoints);
306
for(v=0; v < npoints; v++) {
307
memcpy(&line.point[v].x, &wkb[offset + 4 + (16 * v)], 8);
308
memcpy(&line.point[v].y, &wkb[offset + 4 + (16 * v) + 8], 8);
310
/* make offset point to next linear ring */
311
msAddLine(shape, &line);
313
offset += 4 + 16 * npoints;
321
/* if there is any polygon in wkb, return force_polygon */
322
/* if there is any line in wkb, return force_line */
323
/* otherwise return force_point */
325
static int dont_force(unsigned char *wkb, shapeObj *shape)
332
best_type = MS_SHAPE_NULL; /* nothing in it */
334
memcpy(&ngeoms, &wkb[5], 4);
335
offset = 9; /* were the first geometry is */
336
for(t = 0; t < ngeoms; t++) {
337
memcpy(&type, &wkb[offset + 1], 4); /* type of this geometry */
340
best_type = MS_SHAPE_POLYGON;
341
} else if(type ==2 && best_type != MS_SHAPE_POLYGON) {
342
best_type = MS_SHAPE_LINE;
343
} else if(type == 1 && best_type == MS_SHAPE_NULL) {
344
best_type = MS_SHAPE_POINT;
348
if(best_type == MS_SHAPE_POINT) {
349
return force_to_points(wkb, shape);
351
if(best_type == MS_SHAPE_LINE) {
352
return force_to_lines(wkb, shape);
354
if(best_type == MS_SHAPE_POLYGON) {
355
return force_to_polygons(wkb, shape);
358
return MS_FAILURE; /* unknown type */
361
/* find the bounds of the shape */
362
static void find_bounds(shapeObj *shape)
367
for(t = 0; t < shape->numlines; t++) {
368
for(u = 0; u < shape->line[t].numpoints; u++) {
370
shape->bounds.minx = shape->line[t].point[u].x;
371
shape->bounds.maxx = shape->line[t].point[u].x;
373
shape->bounds.miny = shape->line[t].point[u].y;
374
shape->bounds.maxy = shape->line[t].point[u].y;
377
if(shape->line[t].point[u].x < shape->bounds.minx) {
378
shape->bounds.minx = shape->line[t].point[u].x;
380
if(shape->line[t].point[u].x > shape->bounds.maxx) {
381
shape->bounds.maxx = shape->line[t].point[u].x;
384
if(shape->line[t].point[u].y < shape->bounds.miny) {
385
shape->bounds.miny = shape->line[t].point[u].y;
387
if(shape->line[t].point[u].y > shape->bounds.maxy) {
388
shape->bounds.maxy = shape->line[t].point[u].y;
394
/* TODO Review and clean above! */
397
static int msPostGISRetrievePgVersion(PGconn *pgconn) {
398
#ifndef POSTGIS_HAS_SERVER_VERSION
400
char *strVersion = NULL;
401
char *strParts[3] = { NULL, NULL, NULL };
402
int i = 0, j = 0, len = 0;
405
if (pgconn == NULL) {
406
msSetError(MS_QUERYERR, "Layer does not have a postgis connection.", "msPostGISRetrievePgVersion()");
410
if (! PQparameterStatus(pgconn, "server_version") )
413
strVersion = strdup(PQparameterStatus(pgconn, "server_version"));
416
strParts[j] = strVersion;
418
len = strlen(strVersion);
419
for( i = 0; i < len; i++ ) {
420
if( strVersion[i] == '.' ) {
421
strVersion[i] = '\0';
424
strParts[j] = strVersion + i + 1;
429
msSetError(MS_QUERYERR, "Too many parts in version string.", "msPostGISRetrievePgVersion()");
435
for( j = 0; j < 3 && strParts[j]; j++ ) {
436
if( atoi(strParts[j]) ) {
437
pgVersion += factor * atoi(strParts[j]);
441
msSetError(MS_QUERYERR, "Unable to parse version string.", "msPostGISRetrievePgVersion()");
444
factor = factor / 100;
449
return PQserverVersion(pgconn);
454
** msPostGISRetrievePK()
456
** Find out that the primary key is for this layer.
457
** The layerinfo->fromsource must already be populated and
458
** must not be a subquery.
460
int msPostGISRetrievePK(layerObj *layer) {
461
PGresult *pgresult = NULL;
463
msPostGISLayerInfo *layerinfo = 0;
471
msDebug("msPostGISRetrievePK called.\n");
474
layerinfo = (msPostGISLayerInfo *) layer->layerinfo;
476
/* Attempt to separate fromsource into schema.table */
477
pos_sep = strstr(layerinfo->fromsource, ".");
479
length = strlen(layerinfo->fromsource) - strlen(pos_sep);
480
schema = (char*)malloc(length + 1);
481
strncpy(schema, layerinfo->fromsource, length);
482
schema[length] = '\0';
484
length = strlen(pos_sep);
485
table = (char*)malloc(length);
486
strncpy(table, pos_sep + 1, length - 1);
487
table[length - 1] = '\0';
490
msDebug("msPostGISRetrievePK(): Found schema %s, table %s.\n", schema, table);
494
if (layerinfo->pgconn == NULL) {
495
msSetError(MS_QUERYERR, "Layer does not have a postgis connection.", "msPostGISRetrievePK()");
498
pgVersion = msPostGISRetrievePgVersion(layerinfo->pgconn);
500
if (pgVersion < 70000) {
502
msDebug("msPostGISRetrievePK(): Major version below 7.\n");
506
if (pgVersion < 70200) {
508
msDebug("msPostGISRetrievePK(): Version below 7.2.\n");
512
if (pgVersion < 70300) {
514
** PostgreSQL v7.2 has a different representation of primary keys that
515
** later versions. This currently does not explicitly exclude
516
** multicolumn primary keys.
518
static char *v72sql = "select b.attname from pg_class as a, pg_attribute as b, (select oid from pg_class where relname = '%s') as c, pg_index as d where d.indexrelid = a.oid and d.indrelid = c.oid and d.indisprimary and b.attrelid = a.oid and a.relnatts = 1";
519
sql = malloc(strlen(layerinfo->fromsource) + strlen(v72sql));
520
sprintf(sql, v72sql, layerinfo->fromsource);
523
** PostgreSQL v7.3 and later treat primary keys as constraints.
524
** We only support single column primary keys, so multicolumn
525
** pks are explicitly excluded from the query.
527
if (schema && table) {
528
static char *v73sql = "select attname from pg_attribute, pg_constraint, pg_class, pg_namespace where pg_constraint.conrelid = pg_class.oid and pg_class.oid = pg_attribute.attrelid and pg_constraint.contype = 'p' and pg_constraint.conkey[1] = pg_attribute.attnum and pg_class.relname = '%s' and pg_class.relnamespace = pg_namespace.oid and pg_namespace.nspname = '%s' and pg_constraint.conkey[2] is null";
529
sql = malloc(strlen(schema) + strlen(table) + strlen(v73sql));
530
sprintf(sql, v73sql, table, schema);
534
static char *v73sql = "select attname from pg_attribute, pg_constraint, pg_class where pg_constraint.conrelid = pg_class.oid and pg_class.oid = pg_attribute.attrelid and pg_constraint.contype = 'p' and pg_constraint.conkey[1] = pg_attribute.attnum and pg_class.relname = '%s' and pg_table_is_visible(pg_class.oid) and pg_constraint.conkey[2] is null";
535
sql = malloc(strlen(layerinfo->fromsource) + strlen(v73sql));
536
sprintf(sql, v73sql, layerinfo->fromsource);
540
if (layer->debug > 1) {
541
msDebug("msPostGISRetrievePK: %s\n", sql);
544
layerinfo = (msPostGISLayerInfo *) layer->layerinfo;
546
if (layerinfo->pgconn == NULL) {
547
msSetError(MS_QUERYERR, "Layer does not have a postgis connection.", "msPostGISRetrievePK()");
552
pgresult = PQexec(layerinfo->pgconn, sql);
553
if ( !pgresult || PQresultStatus(pgresult) != PGRES_TUPLES_OK) {
557
tmp1 = "Error executing POSTGIS statement (msPostGISRetrievePK():";
558
tmp2 = (char*)malloc(sizeof(char)*(strlen(tmp1) + strlen(sql) + 1));
561
msSetError(MS_QUERYERR, tmp2, "msPostGISRetrievePK()");
568
if (PQntuples(pgresult) < 1) {
570
msDebug("msPostGISRetrievePK: No results found.\n");
576
if (PQntuples(pgresult) > 1) {
578
msDebug("msPostGISRetrievePK: Multiple results found.\n");
585
if (PQgetisnull(pgresult, 0, 0)) {
587
msDebug("msPostGISRetrievePK: Null result returned.\n");
594
layerinfo->uid = (char*)malloc(PQgetlength(pgresult, 0, 0) + 1);
595
strcpy(layerinfo->uid, PQgetvalue(pgresult, 0, 0));
604
** msPostGISParseData()
606
** Parse the DATA string for geometry column name, table name,
607
** unique id column, srid, and SQL string.
609
int msPostGISParseData(layerObj *layer) {
610
char *pos_opt, *pos_scn, *tmp, *pos_srid, *pos_uid, *data;
612
msPostGISLayerInfo *layerinfo;
614
assert(layer != NULL);
615
assert(layer->layerinfo != NULL);
617
layerinfo = (msPostGISLayerInfo*)(layer->layerinfo);
620
msDebug("msPostGISParseData called.\n");
624
msSetError(MS_QUERYERR, "Missing DATA clause. DATA statement must contain 'geometry_column from table_name' or 'geometry_column from (sub-query) as sub'.", "msPostGISParseData()");
630
** Clean up any existing strings first, as we will be populating these fields.
632
if( layerinfo->srid ) {
633
free(layerinfo->srid);
634
layerinfo->srid = NULL;
636
if( layerinfo->uid ) {
637
free(layerinfo->uid);
638
layerinfo->uid = NULL;
640
if( layerinfo->geomcolumn ) {
641
free(layerinfo->geomcolumn);
642
layerinfo->geomcolumn = NULL;
644
if( layerinfo->fromsource ) {
645
free(layerinfo->fromsource);
646
layerinfo->fromsource = NULL;
650
** Look for the optional ' using unique ID' string first.
652
pos_uid = strcasestr(data, " using unique ");
654
/* Find the end of this case 'using unique ftab_id using srid=33' */
655
tmp = strstr(pos_uid + 14, " ");
656
/* Find the end of this case 'using srid=33 using unique ftab_id' */
658
tmp = pos_uid + strlen(pos_uid);
660
layerinfo->uid = (char*) malloc((tmp - (pos_uid + 14)) + 1);
661
strncpy(layerinfo->uid, pos_uid + 14, tmp - (pos_uid + 14));
662
(layerinfo->uid)[tmp - (pos_uid + 14)] = '\0'; /* null terminate it */
663
msStringTrim(layerinfo->uid);
667
** Look for the optional ' using srid=333 ' string next.
669
pos_srid = strcasestr(data, " using srid=");
671
layerinfo->srid = (char*) malloc(1);
672
(layerinfo->srid)[0] = '\0'; /* no SRID, so return just null terminator*/
674
slength = strspn(pos_srid + 12, "-0123456789");
676
msSetError(MS_QUERYERR, "Error parsing PostGIS DATA variable. You specified 'USING SRID' but didnt have any numbers! %s", "msPostGISParseData()", data);
679
layerinfo->srid = (char*) malloc(slength + 1);
680
strncpy(layerinfo->srid, pos_srid + 12, slength);
681
(layerinfo->srid)[slength] = '\0'; /* null terminate it */
682
msStringTrim(layerinfo->srid);
687
** This is a little hack so the rest of the code works.
688
** pos_opt should point to the start of the optional blocks.
690
** If they are both set, return the smaller one.
692
if (pos_srid && pos_uid) {
693
pos_opt = (pos_srid > pos_uid) ? pos_uid : pos_srid;
695
/* If one or none is set, return the larger one. */
697
pos_opt = (pos_srid > pos_uid) ? pos_srid : pos_uid;
699
/* No pos_opt? Move it to the end of the string. */
701
pos_opt = data + strlen(data);
705
** Scan for the 'geometry from table' or 'geometry from () as foo' clause.
707
pos_scn = strcasestr(data, " from ");
709
msSetError(MS_QUERYERR, "Error parsing PostGIS DATA variable. Must contain 'geometry from table' or 'geometry from (subselect) as foo'. %s", "msPostGISParseData()", data);
713
/* Copy the geometry column name */
714
layerinfo->geomcolumn = (char*) malloc((pos_scn - data) + 1);
715
strncpy(layerinfo->geomcolumn, data, pos_scn - data);
716
(layerinfo->geomcolumn)[pos_scn - data] = '\0';
717
msStringTrim(layerinfo->geomcolumn);
719
/* Copy the table name or sub-select clause */
720
layerinfo->fromsource = (char*) malloc((pos_opt - (pos_scn + 6)) + 1);
721
strncpy(layerinfo->fromsource, pos_scn + 6, pos_opt - (pos_scn + 6));
722
(layerinfo->fromsource)[pos_opt - (pos_scn + 6)] = '\0';
723
msStringTrim(layerinfo->fromsource);
725
/* Something is wrong, our goemetry column and table references are not there. */
726
if (strlen(layerinfo->fromsource) < 1 || strlen(layerinfo->geomcolumn) < 1) {
727
msSetError(MS_QUERYERR, "Error parsing PostGIS DATA variable. Must contain 'geometry from table' or 'geometry from (subselect) as foo'. %s", "msPostGISParseData()", data);
732
** We didn't find a ' using unique ' in the DATA string so try and find a
733
** primary key on the table.
735
if ( ! (layerinfo->uid) ) {
736
if ( strstr(layerinfo->fromsource, " ") ) {
737
msSetError(MS_QUERYERR, "Error parsing PostGIS DATA variable. You must specifiy 'using unique' when supplying a subselect in the data definition.", "msPostGISParseData()");
740
if ( msPostGISRetrievePK(layer) != MS_SUCCESS ) {
741
/* No user specified unique id so we will use the PostgreSQL oid */
742
/* TODO: Deprecate this, oids are deprecated in PostgreSQL */
743
layerinfo->uid = strdup("oid");
748
msDebug("msPostGISParseData: unique_column=%s, srid=%s, geom_column_name=%s, table_name=%s\n", layerinfo->uid, layerinfo->srid, layerinfo->geomcolumn, layerinfo->fromsource);
756
** Decode a hex character.
758
static unsigned char msPostGISHexDecodeChar[256] = {
759
/* not Base64 characters */
760
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
761
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
762
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
764
0,1,2,3,4,5,6,7,8,9,
765
/* not Hex characters */
766
64,64,64,64,64,64,64,
769
/* not Hex characters */
770
64,64,64,64,64,64,64,64,64,
771
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
772
64,10,11,12,13,14,15,64,64,64,64,64,64,64,64,64,
773
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
774
/* not Hex characters (upper 128 characters) */
775
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
776
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
777
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
778
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
779
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
780
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
781
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
782
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
786
** Decode hex string "src" (null terminated)
787
** into "dest" (not null terminated).
788
** Returns length of decoded array or 0 on failure.
790
int msPostGISHexDecode(unsigned char *dest, const char *src, int srclen) {
792
if (src && *src && (srclen % 2 == 0) ) {
794
unsigned char *p = dest;
797
for ( i=0; i<srclen; i+=2 ) {
798
register unsigned char b1=0, b2=0;
799
register unsigned char c1 = src[i];
800
register unsigned char c2 = src[i + 1];
802
b1 = msPostGISHexDecodeChar[c1];
803
b2 = msPostGISHexDecodeChar[c2];
805
*p++ = (b1 << 4) | b2;
814
** Decode a base64 character.
816
static unsigned char msPostGISBase64DecodeChar[256] = {
817
/* not Base64 characters */
818
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
819
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
820
64,64,64,64,64,64,64,64,64,64,64,
823
/* not Base64 characters */
828
52,53,54,55,56,57,58,59,60,61,
829
/* not Base64 characters */
830
64,64,64,64,64,64,64,
832
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,
833
/* not Base64 characters */
836
26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
837
/* not Base64 characters */
839
/* not Base64 characters (upper 128 characters) */
840
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
841
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
842
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
843
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
844
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
845
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
846
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
847
64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
851
** Decode base64 string "src" (null terminated)
852
** into "dest" (not null terminated).
853
** Returns length of decoded array or 0 on failure.
855
int msPostGISBase64Decode(unsigned char *dest, const char *src, int srclen) {
859
unsigned char *p = dest;
861
unsigned char *buf = calloc(srclen + 1, sizeof(unsigned char));
863
/* Drop illegal chars first */
864
for (i=0, j=0; src[i]; i++) {
865
unsigned char c = src[i];
866
if ( (msPostGISBase64DecodeChar[c] != 64) || (c == '=') ) {
871
for (k=0; k<j; k+=4) {
872
register unsigned char c1='A', c2='A', c3='A', c4='A';
873
register unsigned char b1=0, b2=0, b3=0, b4=0;
887
b1 = msPostGISBase64DecodeChar[c1];
888
b2 = msPostGISBase64DecodeChar[c2];
889
b3 = msPostGISBase64DecodeChar[c3];
890
b4 = msPostGISBase64DecodeChar[c4];
892
*p++=((b1<<2)|(b2>>4) );
894
*p++=(((b2&0xf)<<4)|(b3>>2) );
897
*p++=(((b3&0x3)<<6)|b4 );
908
** msPostGISBuildSQLBox()
910
** Returns malloc'ed char* that must be freed by caller.
912
char *msPostGISBuildSQLBox(layerObj *layer, rectObj *rect, char *strSRID) {
918
msDebug("msPostGISBuildSQLBox called.\n");
922
static char *strBoxTemplate = "GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))',%s)";
923
/* 10 doubles + 1 integer + template characters */
924
sz = 10 * 22 + strlen(strSRID) + strlen(strBoxTemplate);
925
strBox = (char*)malloc(sz+1); /* add space for terminating NULL */
926
if ( sz <= snprintf(strBox, sz, strBoxTemplate,
927
rect->minx, rect->miny,
928
rect->minx, rect->maxy,
929
rect->maxx, rect->maxy,
930
rect->maxx, rect->miny,
931
rect->minx, rect->miny,
934
msSetError(MS_MISCERR,"Bounding box digits truncated.","msPostGISBuildSQLBox");
938
static char *strBoxTemplate = "GeomFromText('POLYGON((%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g,%.15g %.15g))')";
939
/* 10 doubles + template characters */
940
sz = 10 * 22 + strlen(strBoxTemplate);
941
strBox = (char*)malloc(sz+1); /* add space for terminating NULL */
942
if ( sz <= snprintf(strBox, sz, strBoxTemplate,
943
rect->minx, rect->miny,
944
rect->minx, rect->maxy,
945
rect->maxx, rect->maxy,
946
rect->maxx, rect->miny,
947
rect->minx, rect->miny) )
949
msSetError(MS_MISCERR,"Bounding box digits truncated.","msPostGISBuildSQLBox");
960
** msPostGISBuildSQLItems()
962
** Returns malloc'ed char* that must be freed by caller.
964
char *msPostGISBuildSQLItems(layerObj *layer) {
966
char *strEndian = NULL;
967
char *strGeom = NULL;
968
char *strItems = NULL;
969
msPostGISLayerInfo *layerinfo = NULL;
972
msDebug("msPostGISBuildSQLItems called.\n");
975
assert( layer->layerinfo != NULL);
977
layerinfo = (msPostGISLayerInfo *)layer->layerinfo;
979
if ( ! layerinfo->geomcolumn ) {
980
msSetError(MS_MISCERR, "layerinfo->geomcolumn is not initialized.", "msPostGISBuildSQLItems()");
985
** Get the server to transform the geometry into our
986
** native endian before transmitting it to us..
988
if (layerinfo->endian == LITTLE_ENDIAN) {
996
** We transfer the geometry from server to client as a
997
** base64 encoded WKB byte-array. We will have to decode this
998
** data once we get it. Forcing 2D removes ordinates we don't
999
** need, saving time. Forcing collection reduces the number
1000
** of input types to handle.
1003
#if TRANSFER_ENCODING == 64
1004
static char *strGeomTemplate = "encode(AsBinary(force_collection(force_2d(\"%s\")),'%s'),'base64') as geom,\"%s\"";
1006
static char *strGeomTemplate = "encode(AsBinary(force_collection(force_2d(\"%s\")),'%s'),'hex') as geom,\"%s\"";
1008
strGeom = (char*)malloc(strlen(strGeomTemplate) + strlen(strEndian) + strlen(layerinfo->geomcolumn) + strlen(layerinfo->uid));
1009
sprintf(strGeom, strGeomTemplate, layerinfo->geomcolumn, strEndian, layerinfo->uid);
1012
if( layer->debug > 1 ) {
1013
msDebug("msPostGISBuildSQLItems: %d items requested.\n",layer->numitems);
1017
** Not requesting items? We just need geometry and unique id.
1019
if (layer->numitems == 0) {
1020
strItems = strdup(strGeom);
1023
** Build SQL to pull all the items.
1026
int length = strlen(strGeom) + 2;
1028
for ( t = 0; t < layer->numitems; t++ ) {
1029
length += strlen(layer->items[t]) + 3; /* itemname + "", */
1031
strItems = (char*)malloc(length);
1033
for ( t = 0; t < layer->numitems; t++ ) {
1034
strcat(strItems, "\"");
1035
strcat(strItems, layer->items[t]);
1036
strcat(strItems, "\",");
1038
strcat(strItems, strGeom);
1046
** msPostGISBuildSQLSRID()
1048
** Returns malloc'ed char* that must be freed by caller.
1050
char *msPostGISBuildSQLSRID(layerObj *layer) {
1052
char *strSRID = NULL;
1053
msPostGISLayerInfo *layerinfo = NULL;
1056
msDebug("msPostGISBuildSQLSRID called.\n");
1059
assert( layer->layerinfo != NULL);
1061
layerinfo = (msPostGISLayerInfo *)layer->layerinfo;
1063
/* An SRID was already provided in the DATA line. */
1064
if ( layerinfo->srid && (strlen(layerinfo->srid) > 0) ) {
1065
strSRID = strdup(layerinfo->srid);
1066
if( layer->debug > 1 ) {
1067
msDebug("msPostGISBuildSQLSRID: SRID provided (%s)\n", strSRID);
1071
** No SRID in data line, so extract target table from the 'fromsource'.
1072
** Either of form "thetable" (one word) or "(select ... from thetable)"
1073
** or "(select ... from thetable where ...)".
1077
char *strSRIDTemplate = "find_srid('','%s','%s')";
1078
char *pos = strstr(layerinfo->fromsource, " ");
1079
if( layer->debug > 1 ) {
1080
msDebug("msPostGISBuildSQLSRID: Building find_srid line.\n", strSRID);
1084
/* target table is one word */
1085
f_table_name = strdup(layerinfo->fromsource);
1086
if( layer->debug > 1 ) {
1087
msDebug("msPostGISBuildSQLSRID: Found table (%s)\n", f_table_name);
1090
/* target table is hiding in sub-select clause */
1091
pos = strcasestr(layerinfo->fromsource, " from ");
1095
pos += 6; /* should be start of table name */
1096
pos_paren = strstr(pos, ")"); /* first ) after table name */
1097
pos_space = strstr(pos, " "); /* first space after table name */
1098
if ( pos_space < pos_paren ) {
1099
/* found space first */
1100
f_table_name = (char*)malloc(pos_space - pos + 1);
1101
strncpy(f_table_name, pos, pos_space - pos);
1102
f_table_name[pos_space - pos] = '\0';
1105
f_table_name = (char*)malloc(pos_paren - pos + 1);
1106
strncpy(f_table_name, pos, pos_paren - pos);
1107
f_table_name[pos_paren - pos] = '\0';
1110
/* should not happen */
1114
strSRID = malloc(strlen(strSRIDTemplate) + strlen(f_table_name) + strlen(layerinfo->geomcolumn));
1115
sprintf(strSRID, strSRIDTemplate, f_table_name, layerinfo->geomcolumn);
1116
if ( f_table_name ) free(f_table_name);
1122
** msPostGISBuildSQLFrom()
1124
** Returns malloc'ed char* that must be freed by caller.
1126
char *msPostGISBuildSQLFrom(layerObj *layer, rectObj *rect) {
1127
char *fromsource =0;
1129
static char *boxToken = "!BOX!";
1130
static int boxTokenLength = 5;
1131
msPostGISLayerInfo *layerinfo;
1134
msDebug("msPostGISBuildSQLFrom called.\n");
1137
assert( layer->layerinfo != NULL);
1139
layerinfo = (msPostGISLayerInfo *)layer->layerinfo;
1141
if ( ! layerinfo->fromsource ) {
1142
msSetError(MS_MISCERR, "Layerinfo->fromsource is not initialized.", "msPostGISBuildSQLFrom()");
1146
fromsource = layerinfo->fromsource;
1149
** If there's a '!BOX!' in our source we need to substitute the
1150
** current rectangle for it...
1152
if ( strstr(fromsource, boxToken) && rect ) {
1153
char *result = NULL;
1157
/* We see to set the SRID on the box, but to what SRID? */
1158
strSRID = msPostGISBuildSQLSRID(layer);
1163
/* Create a suitable SQL string from the rectangle and SRID. */
1164
strBox = msPostGISBuildSQLBox(layer, rect, strSRID);
1166
msSetError(MS_MISCERR, "Unable to build box SQL.", "msPostGISBuildSQLFrom()");
1167
if (strSRID) free(strSRID);
1171
/* Do the substitution. */
1172
while ( strstr(fromsource,boxToken) ) {
1174
char *oldresult = result;
1175
start = strstr(fromsource, boxToken);
1176
end = start + boxTokenLength;
1178
result = (char*)malloc((start - fromsource) + strlen(strBox) + strlen(end) +1);
1180
strncpy(result, fromsource, start - fromsource);
1181
strcpy(result + (start - fromsource), strBox);
1182
strcat(result, end);
1184
fromsource = result;
1185
if (oldresult != NULL)
1189
if (strSRID) free(strSRID);
1190
if (strBox) free(strBox);
1194
** Otherwise we can just take the fromsource "as is".
1196
strFrom = strdup(fromsource);
1202
** msPostGISBuildSQLWhere()
1204
** Returns malloc'ed char* that must be freed by caller.
1206
char *msPostGISBuildSQLWhere(layerObj *layer, rectObj *rect, long *uid) {
1208
char *strFilter = 0;
1212
size_t strRectLength = 0;
1213
size_t strFilterLength = 0;
1214
size_t strUidLength = 0;
1215
size_t strLimitLength = 0;
1217
msPostGISLayerInfo *layerinfo;
1220
msDebug("msPostGISBuildSQLWhere called.\n");
1223
assert( layer->layerinfo != NULL);
1225
layerinfo = (msPostGISLayerInfo *)layer->layerinfo;
1227
if ( ! layerinfo->fromsource ) {
1228
msSetError(MS_MISCERR, "Layerinfo->fromsource is not initialized.", "msPostGISBuildSQLFrom()");
1232
/* Populate strLimit, if necessary. */
1233
if( layer->maxfeatures >= 0 ) {
1234
static char *strLimitTemplate = " limit %d";
1235
strLimit = malloc(strlen(strLimitTemplate) + 12);
1236
sprintf(strLimit, strLimitTemplate, layer->maxfeatures);
1237
strLimitLength = strlen(strLimit);
1240
/* Populate strRect, if necessary. */
1241
if ( rect && layerinfo->geomcolumn ) {
1244
size_t strBoxLength = 0;
1245
static char *strRectTemplate = "%s && %s";
1247
/* We see to set the SRID on the box, but to what SRID? */
1248
strSRID = msPostGISBuildSQLSRID(layer);
1253
strBox = msPostGISBuildSQLBox(layer, rect, strSRID);
1255
strBoxLength = strlen(strBox);
1258
msSetError(MS_MISCERR, "Unable to build box SQL.", "msPostGISBuildSQLWhere()");
1262
strRect = (char*)malloc(strlen(strRectTemplate) + strBoxLength + strlen(layerinfo->geomcolumn));
1263
sprintf(strRect, strRectTemplate, layerinfo->geomcolumn, strBox);
1264
strRectLength = strlen(strRect);
1265
if (strBox) free(strBox);
1266
if (strSRID) free(strSRID);
1269
/* Populate strFilter, if necessary. */
1270
if ( layer->filter.string ) {
1271
static char *strFilterTemplate = "(%s)";
1272
strFilter = (char*)malloc(strlen(strFilterTemplate) + strlen(layer->filter.string));
1273
sprintf(strFilter, strFilterTemplate, layer->filter.string);
1274
strFilterLength = strlen(strFilter);
1277
/* Populate strUid, if necessary. */
1279
static char *strUidTemplate = "\"%s\" = %ld";
1280
strUid = (char*)malloc(strlen(strUidTemplate) + strlen(layerinfo->uid) + 64);
1281
sprintf(strUid, strUidTemplate, layerinfo->uid, *uid);
1282
strUidLength = strlen(strUid);
1285
strWhere = (char*)malloc(strRectLength + 5 + strFilterLength + 5 + strUidLength + strLimitLength);
1288
strcat(strWhere, strRect);
1294
strcat(strWhere, " and ");
1296
strcat(strWhere, strFilter);
1302
strcat(strWhere, " and ");
1304
strcat(strWhere, strUid);
1309
strcat(strWhere, strLimit);
1317
** msPostGISBuildSQL()
1319
** Returns malloc'ed char* that must be freed by caller.
1321
char *msPostGISBuildSQL(layerObj *layer, rectObj *rect, long *uid) {
1323
msPostGISLayerInfo *layerinfo = 0;
1328
static char *strSQLTemplate = "select %s from %s where %s";
1331
msDebug("msPostGISBuildSQL called.\n");
1334
assert( layer->layerinfo != NULL);
1336
layerinfo = (msPostGISLayerInfo *)layer->layerinfo;
1338
strItems = msPostGISBuildSQLItems(layer);
1340
msSetError(MS_MISCERR, "Failed to build SQL items.", "msPostGISBuildSQL()");
1344
strFrom = msPostGISBuildSQLFrom(layer, rect);
1346
msSetError(MS_MISCERR, "Failed to build SQL 'from'.", "msPostGISBuildSQL()");
1350
strWhere = msPostGISBuildSQLWhere(layer, rect, uid);
1352
msSetError(MS_MISCERR, "Failed to build SQL 'where'.", "msPostGISBuildSQL()");
1356
strSQL = malloc(strlen(strSQLTemplate) + strlen(strFrom) + strlen(strItems) + strlen(strWhere));
1357
sprintf(strSQL, strSQLTemplate, strItems, strFrom, strWhere);
1358
if (strItems) free(strItems);
1359
if (strFrom) free(strFrom);
1360
if (strWhere) free(strWhere);
1366
int msPostGISReadShape(layerObj *layer, shapeObj *shape) {
1368
char *wkbstr = NULL;
1369
unsigned char *wkb = NULL;
1370
msPostGISLayerInfo *layerinfo = NULL;
1375
msDebug("msPostGISReadShape called.\n");
1378
assert(layer->layerinfo != NULL);
1379
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
1381
/* Retrieve the geometry. */
1382
wkbstr = (char*)PQgetvalue(layerinfo->pgresult, layerinfo->rownum, layer->numitems );
1383
wkbstrlen = PQgetlength(layerinfo->pgresult, layerinfo->rownum, layer->numitems);
1386
msSetError(MS_QUERYERR, "String encoded WKB returned is null!", "msPostGISReadShape()");
1391
wkb = calloc(wkbstrlen, sizeof(char));
1392
#if TRANSFER_ENCODING == 64
1393
result = msPostGISBase64Decode(wkb, wkbstr, wkbstrlen - 1);
1395
result = msPostGISHexDecode(wkb, wkbstr, wkbstrlen);
1403
switch (layer->type) {
1405
case MS_LAYER_POINT:
1406
result = force_to_points(wkb, shape);
1410
result = force_to_lines(wkb, shape);
1413
case MS_LAYER_POLYGON:
1414
result = force_to_polygons(wkb, shape);
1417
case MS_LAYER_ANNOTATION:
1418
case MS_LAYER_QUERY:
1419
case MS_LAYER_CHART:
1420
result = dont_force(wkb, shape);
1423
case MS_LAYER_RASTER:
1424
msDebug( "Ignoring MS_LAYER_RASTER in msPostGISReadShape.\n" );
1427
case MS_LAYER_CIRCLE:
1428
msDebug( "Ignoring MS_LAYER_RASTER in msPostGISReadShape.\n" );
1432
msDebug( "Unsupported layer type in msPostGISReadShape()!\n" );
1436
if (shape->type != MS_SHAPE_NULL) {
1440
/* Found a drawable shape, so now retreive the attributes. */
1442
shape->values = (char**) malloc(sizeof(char*) * layer->numitems);
1443
for ( t = 0; t < layer->numitems; t++) {
1444
int size = PQgetlength(layerinfo->pgresult, layerinfo->rownum, t);
1445
char *val = (char*)PQgetvalue(layerinfo->pgresult, layerinfo->rownum, t);
1446
int isnull = PQgetisnull(layerinfo->pgresult, layerinfo->rownum, t);
1448
shape->values[t] = strdup("");
1451
shape->values[t] = (char*) malloc(size + 1);
1452
memcpy(shape->values[t], val, size);
1453
shape->values[t][size] = '\0'; /* null terminate it */
1454
msStringTrimBlanks(shape->values[t]);
1456
if( layer->debug > 4 ) {
1457
msDebug("msPostGISReadShape: PQgetlength = %d\n", size);
1459
if( layer->debug > 1 ) {
1460
msDebug("msPostGISReadShape: [%s] \"%s\"\n", layer->items[t], shape->values[t]);
1464
/* t is the geometry, t+1 is the uid */
1465
tmp = PQgetvalue(layerinfo->pgresult, layerinfo->rownum, t + 1);
1467
uid = strtol( tmp, NULL, 10 );
1472
if( layer->debug > 4 ) {
1473
msDebug("msPostGISReadShape: Setting shape->index = %d\n", uid);
1474
msDebug("msPostGISReadShape: Setting shape->tileindex = %d\n", layerinfo->rownum);
1477
shape->tileindex = layerinfo->rownum;
1479
if( layer->debug > 2 ) {
1480
msDebug("msPostGISReadShape: [index] %d\n", shape->index);
1483
shape->numvalues = layer->numitems;
1488
if( layer->debug > 2 ) {
1489
char *tmp = msShapeToWKT(shape);
1490
msDebug("msPostGISReadShape: [shape] %s\n", tmp);
1498
#endif /* USE_POSTGIS */
1502
** msPostGISLayerOpen()
1504
** Registered vtable->LayerOpen function.
1506
int msPostGISLayerOpen(layerObj *layer) {
1508
msPostGISLayerInfo *layerinfo;
1511
assert(layer != NULL);
1514
msDebug("msPostGISLayerOpen called: %s\n", layer->data);
1517
if (layer->layerinfo) {
1519
msDebug("msPostGISLayerOpen: Layer is already open!\n");
1521
return MS_SUCCESS; /* already open */
1525
msSetError(MS_QUERYERR, "Nothing specified in DATA statement.", "msPostGISLayerOpen()");
1530
** Initialize the layerinfo
1532
layerinfo = msPostGISCreateLayerInfo();
1534
if (((char*) &order_test)[0] == 1) {
1535
layerinfo->endian = LITTLE_ENDIAN;
1537
layerinfo->endian = BIG_ENDIAN;
1541
** Get a database connection from the pool.
1543
layerinfo->pgconn = (PGconn *) msConnPoolRequest(layer);
1545
/* No connection in the pool, so set one up. */
1546
if (!layerinfo->pgconn) {
1547
char *conn_decrypted;
1549
msDebug("msPostGISLayerOpen: No connection in pool, creating a fresh one.\n");
1552
if (!layer->connection) {
1553
msSetError(MS_MISCERR, "Missing CONNECTION keyword.", "msPostGISLayerOpen()");
1558
** Decrypt any encrypted token in connection string and attempt to connect.
1560
conn_decrypted = msDecryptStringTokens(layer->map, layer->connection);
1561
if (conn_decrypted == NULL) {
1562
return MS_FAILURE; /* An error should already have been produced */
1564
layerinfo->pgconn = PQconnectdb(conn_decrypted);
1565
msFree(conn_decrypted);
1566
conn_decrypted = NULL;
1569
** Connection failed, return error message with passwords ***ed out.
1571
if (!layerinfo->pgconn || PQstatus(layerinfo->pgconn) == CONNECTION_BAD) {
1572
char *index, *maskeddata;
1574
msDebug("msPostGISLayerOpen: Connection failure.\n");
1576
maskeddata = strdup(layer->connection);
1577
index = strstr(maskeddata, "password=");
1578
if (index != NULL) {
1579
index = (char*)(index + 9);
1580
while (*index != '\0' && *index != ' ') {
1586
msSetError(MS_QUERYERR, "Database connection failed (%s) with connect string '%s'\nIs the database running? Is it allowing connections? Does the specified user exist? Is the password valid? Is the database on the standard port?", "msPostGISLayerOpen()", PQerrorMessage(layerinfo->pgconn), maskeddata);
1593
/* Register to receive notifications from the database. */
1594
PQsetNoticeProcessor(layerinfo->pgconn, postresqlNoticeHandler, (void *) layer);
1596
/* Save this connection in the pool for later. */
1597
msConnPoolRegister(layer, layerinfo->pgconn, msPostGISCloseConnection);
1600
/* Connection in the pool should be tested to see if backend is alive. */
1601
if( PQstatus(layerinfo->pgconn) != CONNECTION_OK ) {
1602
/* Uh oh, bad connection. Can we reset it? */
1603
PQreset(layerinfo->pgconn);
1604
if( PQstatus(layerinfo->pgconn) != CONNECTION_OK ) {
1605
/* Nope, time to bail out. */
1606
msSetError(MS_QUERYERR, "PostgreSQL database connection gone bad (%s)", "msPostGISLayerOpen()", PQerrorMessage(layerinfo->pgconn));
1613
/* Save the layerinfo in the layerObj. */
1614
layer->layerinfo = (void*)layerinfo;
1618
msSetError( MS_MISCERR,
1619
"PostGIS support is not available.",
1620
"msPostGISLayerOpen()");
1626
** msPostGISLayerClose()
1628
** Registered vtable->LayerClose function.
1630
int msPostGISLayerClose(layerObj *layer) {
1634
msDebug("msPostGISLayerClose called: %s\n", layer->data);
1637
if( layer->layerinfo ) {
1638
msPostGISFreeLayerInfo(layer);
1643
msSetError( MS_MISCERR,
1644
"PostGIS support is not available.",
1645
"msPostGISLayerOpen()");
1652
** msPostGISLayerIsOpen()
1654
** Registered vtable->LayerIsOpen function.
1656
int msPostGISLayerIsOpen(layerObj *layer) {
1660
msDebug("msPostGISLayerIsOpen called.\n");
1663
if (layer->layerinfo)
1668
msSetError( MS_MISCERR,
1669
"PostGIS support is not available.",
1670
"msPostGISLayerIsOpen()");
1677
** msPostGISLayerFreeItemInfo()
1679
** Registered vtable->LayerFreeItemInfo function.
1681
void msPostGISLayerFreeItemInfo(layerObj *layer) {
1684
msDebug("msPostGISLayerFreeItemInfo called.\n");
1687
if (layer->iteminfo) {
1688
free(layer->iteminfo);
1690
layer->iteminfo = NULL;
1695
** msPostGISLayerInitItemInfo()
1697
** Registered vtable->LayerInitItemInfo function.
1698
** Our iteminfo is list of indexes from 1..numitems.
1700
int msPostGISLayerInitItemInfo(layerObj *layer) {
1706
msDebug("msPostGISLayerInitItemInfo called.\n");
1709
if (layer->numitems == 0) {
1713
if (layer->iteminfo) {
1714
free(layer->iteminfo);
1717
layer->iteminfo = malloc(sizeof(int) * layer->numitems);
1718
if (!layer->iteminfo) {
1719
msSetError(MS_MEMERR, "Out of memory.", "msPostGISLayerInitItemInfo()");
1723
itemindexes = (int*)layer->iteminfo;
1724
for (i = 0; i < layer->numitems; i++) {
1725
itemindexes[i] = i; /* Last item is always the geometry. The rest are non-geometry. */
1730
msSetError( MS_MISCERR,
1731
"PostGIS support is not available.",
1732
"msPostGISLayerInitItemInfo()");
1738
** msPostGISLayerWhichShapes()
1740
** Registered vtable->LayerWhichShapes function.
1742
int msPostGISLayerWhichShapes(layerObj *layer, rectObj rect) {
1744
msPostGISLayerInfo *layerinfo = NULL;
1745
char *strSQL = NULL;
1746
PGresult *pgresult = NULL;
1748
assert(layer != NULL);
1749
assert(layer->layerinfo != NULL);
1752
msDebug("msPostGISLayerWhichShapes called.\n");
1755
/* Fill out layerinfo with our current DATA state. */
1756
if ( msPostGISParseData(layer) != MS_SUCCESS) {
1761
** This comes *after* parsedata, because parsedata fills in
1762
** layer->layerinfo.
1764
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
1766
/* Build a SQL query based on our current state. */
1767
strSQL = msPostGISBuildSQL(layer, &rect, NULL);
1769
msSetError(MS_QUERYERR, "Failed to build query SQL.", "msPostGISLayerWhichShapes()");
1774
msDebug("msPostGISLayerWhichShapes query: %s\n", strSQL);
1777
pgresult = PQexec(layerinfo->pgconn, strSQL);
1779
if ( layer->debug > 1 ) {
1780
msDebug("msPostGISLayerWhichShapes query status: %s (%d)\n", PQresStatus(PQresultStatus(pgresult)), PQresultStatus(pgresult));
1783
/* Something went wrong. */
1784
if (!pgresult || PQresultStatus(pgresult) != PGRES_TUPLES_OK) {
1785
msSetError(MS_QUERYERR, "Error (%s) executing query: %s", "msPostGISLayerWhichShapes()", PQerrorMessage(layerinfo->pgconn), strSQL);
1793
if ( layer->debug ) {
1794
msDebug("msPostGISLayerWhichShapes got %d records in result.\n", PQntuples(pgresult));
1797
/* Clean any existing pgresult before storing current one. */
1798
if(layerinfo->pgresult) PQclear(layerinfo->pgresult);
1799
layerinfo->pgresult = pgresult;
1801
/* Clean any existing SQL before storing current. */
1802
if(layerinfo->sql) free(layerinfo->sql);
1803
layerinfo->sql = strSQL;
1805
layerinfo->rownum = 0;
1809
msSetError( MS_MISCERR,
1810
"PostGIS support is not available.",
1811
"msPostGISLayerWhichShapes()");
1817
** msPostGISLayerNextShape()
1819
** Registered vtable->LayerNextShape function.
1821
int msPostGISLayerNextShape(layerObj *layer, shapeObj *shape) {
1823
msPostGISLayerInfo *layerinfo;
1826
msDebug("msPostGISLayerNextShape called.\n");
1829
assert(layer != NULL);
1830
assert(layer->layerinfo != NULL);
1832
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
1834
shape->type = MS_SHAPE_NULL;
1837
** Roll through pgresult until we hit non-null shape (usually right away).
1839
while (shape->type == MS_SHAPE_NULL) {
1840
if (layerinfo->rownum < PQntuples(layerinfo->pgresult)) {
1842
/* Retrieve this shape, cursor access mode. */
1843
rv = msPostGISReadShape(layer, shape);
1844
if( shape->type != MS_SHAPE_NULL ) {
1845
(layerinfo->rownum)++; /* move to next shape */
1848
(layerinfo->rownum)++; /* move to next shape */
1855
/* Found nothing, clean up and exit. */
1860
msSetError( MS_MISCERR,
1861
"PostGIS support is not available.",
1862
"msPostGISLayerNextShape()");
1868
** msPostGISLayerResultsGetShape()
1870
** Registered vtable->LayerGetShape function. For pulling from a prepared and
1871
** undisposed result set. We ignore the 'tile' parameter, as it means nothing to us.
1873
int msPostGISLayerResultsGetShape(layerObj *layer, shapeObj *shape, int tile, long record) {
1876
PGresult *pgresult = NULL;
1877
msPostGISLayerInfo *layerinfo = NULL;
1878
int result = MS_SUCCESS;
1881
assert(layer != NULL);
1882
assert(layer->layerinfo != NULL);
1885
msDebug("msPostGISLayerResultsGetShape called for record = %i\n", record);
1889
msDebug("msPostGISLayerResultsGetShape called for record = %i\n", record);
1890
return msPostGISLayerResultsGetShape(layer, shape, tile, record);
1893
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
1895
/* Check the validity of the open result. */
1896
pgresult = layerinfo->pgresult;
1898
msSetError( MS_MISCERR,
1899
"PostgreSQL result set is null.",
1900
"msPostGISLayerResultsGetShape()");
1903
status = PQresultStatus(pgresult);
1904
if ( layer->debug > 1 ) {
1905
msDebug("msPostGISLayerResultsGetShape query status: %s (%d)\n", PQresStatus(status), status);
1907
if( ! ( status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK) ) {
1908
msSetError( MS_MISCERR,
1909
"PostgreSQL result set is not ready.",
1910
"msPostGISLayerResultsGetShape()");
1914
/* Check the validity of the requested record number. */
1915
if( tile >= PQntuples(pgresult) ) {
1916
msDebug("msPostGISLayerResultsGetShape got request for (%d) but only has %d tuples.\n", tile, PQntuples(pgresult));
1917
msSetError( MS_MISCERR,
1918
"Got request larger than result set.",
1919
"msPostGISLayerResultsGetShape()");
1923
layerinfo->rownum = tile; /* Only return one result. */
1925
/* We don't know the shape type until we read the geometry. */
1926
shape->type = MS_SHAPE_NULL;
1928
/* Return the shape, cursor access mode. */
1929
result = msPostGISReadShape(layer, shape);
1931
return (shape->type == MS_SHAPE_NULL) ? MS_FAILURE : MS_SUCCESS;
1933
msSetError( MS_MISCERR,
1934
"PostGIS support is not available.",
1935
"msPostGISLayerResultsGetShape()");
1941
** msPostGISLayerGetShape()
1943
** Registered vtable->LayerGetShape function. We ignore the 'tile'
1944
** parameter, as it means nothing to us.
1946
int msPostGISLayerGetShape(layerObj *layer, shapeObj *shape, int tile, long record) {
1949
msPostGISLayerInfo *layerinfo;
1950
int result, num_tuples;
1953
assert(layer != NULL);
1954
assert(layer->layerinfo != NULL);
1957
msDebug("msPostGISLayerGetShape called for record = %i\n", record);
1960
/* Fill out layerinfo with our current DATA state. */
1961
if ( msPostGISParseData(layer) != MS_SUCCESS) {
1966
** This comes *after* parsedata, because parsedata fills in
1967
** layer->layerinfo.
1969
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
1971
/* Build a SQL query based on our current state. */
1972
strSQL = msPostGISBuildSQL(layer, 0, &record);
1974
msSetError(MS_QUERYERR, "Failed to build query SQL.", "msPostGISLayerGetShape()");
1979
msDebug("msPostGISLayerGetShape query: %s\n", strSQL);
1982
pgresult = PQexec(layerinfo->pgconn, strSQL);
1984
/* Something went wrong. */
1985
if ( (!pgresult) || (PQresultStatus(pgresult) != PGRES_TUPLES_OK) ) {
1986
msSetError(MS_QUERYERR, "Error (%s) executing SQL: %s", "msPostGISLayerGetShape()", PQerrorMessage(layerinfo->pgconn), strSQL );
1996
/* Clean any existing pgresult before storing current one. */
1997
if(layerinfo->pgresult) PQclear(layerinfo->pgresult);
1998
layerinfo->pgresult = pgresult;
2000
/* Clean any existing SQL before storing current. */
2001
if(layerinfo->sql) free(layerinfo->sql);
2002
layerinfo->sql = strSQL;
2004
layerinfo->rownum = 0; /* Only return one result. */
2006
/* We don't know the shape type until we read the geometry. */
2007
shape->type = MS_SHAPE_NULL;
2009
num_tuples = PQntuples(pgresult);
2011
msDebug("msPostGISLayerGetShape number of records: %d\n", num_tuples);
2014
if (num_tuples > 0) {
2015
/* Get shape in random access mode. */
2016
result = msPostGISReadShape(layer, shape);
2019
return (shape->type == MS_SHAPE_NULL) ? MS_FAILURE : ( (num_tuples > 0) ? MS_SUCCESS : MS_DONE );
2021
msSetError( MS_MISCERR,
2022
"PostGIS support is not available.",
2023
"msPostGISLayerGetShape()");
2029
** msPostGISLayerGetItems()
2031
** Registered vtable->LayerGetItems function. Query the database for
2032
** column information about the requested layer. Rather than look in
2033
** system tables, we just run a zero-cost query and read out of the
2036
int msPostGISLayerGetItems(layerObj *layer) {
2038
msPostGISLayerInfo *layerinfo;
2040
static char *strSQLTemplate = "select * from %s where false limit 0";
2044
char found_geom = 0;
2047
assert(layer != NULL);
2048
assert(layer->layerinfo != NULL);
2050
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
2052
assert(layerinfo->pgconn);
2055
msDebug("msPostGISLayerGetItems called.\n");
2058
/* Fill out layerinfo with our current DATA state. */
2059
if ( msPostGISParseData(layer) != MS_SUCCESS) {
2063
layerinfo = (msPostGISLayerInfo*) layer->layerinfo;
2066
** Both the "table" and "(select ...) as sub" cases can be handled with the
2069
sql = (char*) malloc(strlen(strSQLTemplate) + strlen(layerinfo->fromsource));
2070
sprintf(sql, strSQLTemplate, layerinfo->fromsource);
2073
msDebug("msPostGISLayerGetItems executing SQL: %s\n", sql);
2076
pgresult = PQexec(layerinfo->pgconn, sql);
2078
if ( (!pgresult) || (PQresultStatus(pgresult) != PGRES_TUPLES_OK) ) {
2079
msSetError(MS_QUERYERR, "Error (%s) executing SQL: %s", "msPostGISLayerGetItems()", PQerrorMessage(layerinfo->pgconn), sql);
2089
layer->numitems = PQnfields(pgresult) - 1; /* dont include the geometry column (last entry)*/
2090
layer->items = malloc(sizeof(char*) * (layer->numitems + 1)); /* +1 in case there is a problem finding geometry column */
2092
found_geom = 0; /* havent found the geom field */
2095
for (t = 0; t < PQnfields(pgresult); t++) {
2096
col = PQfname(pgresult, t);
2097
if ( strcmp(col, layerinfo->geomcolumn) != 0 ) {
2098
/* this isnt the geometry column */
2099
layer->items[item_num] = strdup(col);
2109
msSetError(MS_QUERYERR, "Tried to find the geometry column in the database, but couldn't find it. Is it mis-capitalized? '%s'", "msPostGISLayerGetItems()", layerinfo->geomcolumn);
2113
return msPostGISLayerInitItemInfo(layer);
2115
msSetError( MS_MISCERR,
2116
"PostGIS support is not available.",
2117
"msPostGISLayerGetShape()");
2123
** msPostGISLayerGetExtent()
2125
** Registered vtable->LayerGetExtent function.
2127
** TODO: Update to use proper PostGIS functions to pull
2128
** extent quickly and accurately when available.
2130
int msPostGISLayerGetExtent(layerObj *layer, rectObj *extent) {
2132
msDebug("msPOSTGISLayerGetExtent called.\n");
2135
extent->minx = extent->miny = -1.0 * FLT_MAX ;
2136
extent->maxx = extent->maxy = FLT_MAX;
2142
int msPostGISLayerSetTimeFilter(layerObj *lp, const char *timestring, const char *timefield)
2144
char *tmpstimestring = NULL;
2145
char *timeresolution = NULL;
2146
int timesresol = -1;
2147
char **atimes, **tokens = NULL;
2148
int numtimes=0,i=0,ntmp=0,nlength=0;
2153
if (!lp || !timestring || !timefield)
2156
if (strstr(timestring, ",") == NULL &&
2157
strstr(timestring, "/") == NULL) /* discrete time */
2158
tmpstimestring = strdup(timestring);
2161
atimes = msStringSplit (timestring, ',', &numtimes);
2162
if (atimes == NULL || numtimes < 1)
2167
tokens = msStringSplit(atimes[0], '/', &ntmp);
2168
if (ntmp == 2) /* ranges */
2170
tmpstimestring = strdup(tokens[0]);
2171
msFreeCharArray(tokens, ntmp);
2173
else if (ntmp == 1) /* multiple times */
2175
tmpstimestring = strdup(atimes[0]);
2178
msFreeCharArray(atimes, numtimes);
2180
if (!tmpstimestring)
2183
timesresol = msTimeGetResolution((const char*)tmpstimestring);
2187
free(tmpstimestring);
2191
case (TIME_RESOLUTION_SECOND):
2192
timeresolution = strdup("second");
2195
case (TIME_RESOLUTION_MINUTE):
2196
timeresolution = strdup("minute");
2199
case (TIME_RESOLUTION_HOUR):
2200
timeresolution = strdup("hour");
2203
case (TIME_RESOLUTION_DAY):
2204
timeresolution = strdup("day");
2207
case (TIME_RESOLUTION_MONTH):
2208
timeresolution = strdup("month");
2211
case (TIME_RESOLUTION_YEAR):
2212
timeresolution = strdup("year");
2219
if (!timeresolution)
2222
/* where date_trunc('month', _cwctstamp) = '2004-08-01' */
2223
if (strstr(timestring, ",") == NULL &&
2224
strstr(timestring, "/") == NULL) /* discrete time */
2226
if(lp->filteritem) free(lp->filteritem);
2227
lp->filteritem = strdup(timefield);
2230
/* if the filter is set and it's a string type, concatenate it with
2231
the time. If not just free it */
2232
if (lp->filter.type == MS_EXPRESSION)
2234
strcat(buffer, "(");
2235
strcat(buffer, lp->filter.string);
2236
strcat(buffer, ") and ");
2239
freeExpression(&lp->filter);
2243
strcat(buffer, "(");
2245
strcat(buffer, "date_trunc('");
2246
strcat(buffer, timeresolution);
2247
strcat(buffer, "', ");
2248
strcat(buffer, timefield);
2249
strcat(buffer, ")");
2252
strcat(buffer, " = ");
2253
strcat(buffer, "'");
2254
strcat(buffer, timestring);
2255
/* make sure that the timestring is complete and acceptable */
2256
/* to the date_trunc function : */
2257
/* - if the resolution is year (2004) or month (2004-01), */
2258
/* a complete string for time would be 2004-01-01 */
2259
/* - if the resolluion is hour or minute (2004-01-01 15), a */
2260
/* complete time is 2004-01-01 15:00:00 */
2261
if (strcasecmp(timeresolution, "year")==0)
2263
nlength = strlen(timestring);
2264
if (timestring[nlength-1] != '-')
2265
strcat(buffer,"-01-01");
2267
strcat(buffer,"01-01");
2269
else if (strcasecmp(timeresolution, "month")==0)
2271
nlength = strlen(timestring);
2272
if (timestring[nlength-1] != '-')
2273
strcat(buffer,"-01");
2275
strcat(buffer,"01");
2277
else if (strcasecmp(timeresolution, "hour")==0)
2279
nlength = strlen(timestring);
2280
if (timestring[nlength-1] != ':')
2281
strcat(buffer,":00:00");
2283
strcat(buffer,"00:00");
2285
else if (strcasecmp(timeresolution, "minute")==0)
2287
nlength = strlen(timestring);
2288
if (timestring[nlength-1] != ':')
2289
strcat(buffer,":00");
2291
strcat(buffer,"00");
2295
strcat(buffer, "'");
2297
strcat(buffer, ")");
2299
/* loadExpressionString(&lp->filter, (char *)timestring); */
2300
loadExpressionString(&lp->filter, buffer);
2302
free(timeresolution);
2306
atimes = msStringSplit (timestring, ',', &numtimes);
2307
if (atimes == NULL || numtimes < 1)
2312
/* check to see if we have ranges by parsing the first entry */
2313
tokens = msStringSplit(atimes[0], '/', &ntmp);
2314
if (ntmp == 2) /* ranges */
2316
msFreeCharArray(tokens, ntmp);
2317
for (i=0; i<numtimes; i++)
2319
tokens = msStringSplit(atimes[i], '/', &ntmp);
2322
if (strlen(buffer) > 0)
2323
strcat(buffer, " OR ");
2325
strcat(buffer, "(");
2327
strcat(buffer, "(");
2329
strcat(buffer, "date_trunc('");
2330
strcat(buffer, timeresolution);
2331
strcat(buffer, "', ");
2332
strcat(buffer, timefield);
2333
strcat(buffer, ")");
2335
strcat(buffer, " >= ");
2337
strcat(buffer, "'");
2339
strcat(buffer, tokens[0]);
2340
/* - if the resolution is year (2004) or month (2004-01), */
2341
/* a complete string for time would be 2004-01-01 */
2342
/* - if the resolluion is hour or minute (2004-01-01 15), a */
2343
/* complete time is 2004-01-01 15:00:00 */
2344
if (strcasecmp(timeresolution, "year")==0)
2346
nlength = strlen(tokens[0]);
2347
if (tokens[0][nlength-1] != '-')
2348
strcat(buffer,"-01-01");
2350
strcat(buffer,"01-01");
2352
else if (strcasecmp(timeresolution, "month")==0)
2354
nlength = strlen(tokens[0]);
2355
if (tokens[0][nlength-1] != '-')
2356
strcat(buffer,"-01");
2358
strcat(buffer,"01");
2360
else if (strcasecmp(timeresolution, "hour")==0)
2362
nlength = strlen(tokens[0]);
2363
if (tokens[0][nlength-1] != ':')
2364
strcat(buffer,":00:00");
2366
strcat(buffer,"00:00");
2368
else if (strcasecmp(timeresolution, "minute")==0)
2370
nlength = strlen(tokens[0]);
2371
if (tokens[0][nlength-1] != ':')
2372
strcat(buffer,":00");
2374
strcat(buffer,"00");
2377
strcat(buffer, "'");
2378
strcat(buffer, " AND ");
2381
strcat(buffer, "date_trunc('");
2382
strcat(buffer, timeresolution);
2383
strcat(buffer, "', ");
2384
strcat(buffer, timefield);
2385
strcat(buffer, ")");
2387
strcat(buffer, " <= ");
2389
strcat(buffer, "'");
2390
strcat(buffer, tokens[1]);
2392
/* - if the resolution is year (2004) or month (2004-01), */
2393
/* a complete string for time would be 2004-01-01 */
2394
/* - if the resolluion is hour or minute (2004-01-01 15), a */
2395
/* complete time is 2004-01-01 15:00:00 */
2396
if (strcasecmp(timeresolution, "year")==0)
2398
nlength = strlen(tokens[1]);
2399
if (tokens[1][nlength-1] != '-')
2400
strcat(buffer,"-01-01");
2402
strcat(buffer,"01-01");
2404
else if (strcasecmp(timeresolution, "month")==0)
2406
nlength = strlen(tokens[1]);
2407
if (tokens[1][nlength-1] != '-')
2408
strcat(buffer,"-01");
2410
strcat(buffer,"01");
2412
else if (strcasecmp(timeresolution, "hour")==0)
2414
nlength = strlen(tokens[1]);
2415
if (tokens[1][nlength-1] != ':')
2416
strcat(buffer,":00:00");
2418
strcat(buffer,"00:00");
2420
else if (strcasecmp(timeresolution, "minute")==0)
2422
nlength = strlen(tokens[1]);
2423
if (tokens[1][nlength-1] != ':')
2424
strcat(buffer,":00");
2426
strcat(buffer,"00");
2429
strcat(buffer, "'");
2430
strcat(buffer, ")");
2433
msFreeCharArray(tokens, ntmp);
2435
if (strlen(buffer) > 0)
2436
strcat(buffer, ")");
2438
else if (ntmp == 1) /* multiple times */
2440
msFreeCharArray(tokens, ntmp);
2441
strcat(buffer, "(");
2442
for (i=0; i<numtimes; i++)
2445
strcat(buffer, " OR ");
2447
strcat(buffer, "(");
2449
strcat(buffer, "date_trunc('");
2450
strcat(buffer, timeresolution);
2451
strcat(buffer, "', ");
2452
strcat(buffer, timefield);
2453
strcat(buffer, ")");
2455
strcat(buffer, " = ");
2457
strcat(buffer, "'");
2459
strcat(buffer, atimes[i]);
2461
/* make sure that the timestring is complete and acceptable */
2462
/* to the date_trunc function : */
2463
/* - if the resolution is year (2004) or month (2004-01), */
2464
/* a complete string for time would be 2004-01-01 */
2465
/* - if the resolluion is hour or minute (2004-01-01 15), a */
2466
/* complete time is 2004-01-01 15:00:00 */
2467
if (strcasecmp(timeresolution, "year")==0)
2469
nlength = strlen(atimes[i]);
2470
if (atimes[i][nlength-1] != '-')
2471
strcat(buffer,"-01-01");
2473
strcat(buffer,"01-01");
2475
else if (strcasecmp(timeresolution, "month")==0)
2477
nlength = strlen(atimes[i]);
2478
if (atimes[i][nlength-1] != '-')
2479
strcat(buffer,"-01");
2481
strcat(buffer,"01");
2483
else if (strcasecmp(timeresolution, "hour")==0)
2485
nlength = strlen(atimes[i]);
2486
if (atimes[i][nlength-1] != ':')
2487
strcat(buffer,":00:00");
2489
strcat(buffer,"00:00");
2491
else if (strcasecmp(timeresolution, "minute")==0)
2493
nlength = strlen(atimes[i]);
2494
if (atimes[i][nlength-1] != ':')
2495
strcat(buffer,":00");
2497
strcat(buffer,"00");
2500
strcat(buffer, "'");
2501
strcat(buffer, ")");
2503
strcat(buffer, ")");
2507
msFreeCharArray(atimes, numtimes);
2511
msFreeCharArray(atimes, numtimes);
2513
/* load the string to the filter */
2514
if (strlen(buffer) > 0)
2517
free(lp->filteritem);
2518
lp->filteritem = strdup(timefield);
2521
if (lp->filter.type == MS_EXPRESSION)
2523
strcat(buffer, "(");
2524
strcat(buffer, lp->filter.string);
2525
strcat(buffer, ") and ");
2528
freeExpression(&lp->filter);
2530
loadExpressionString(&lp->filter, buffer);
2533
free(timeresolution);
2541
int msPostGISLayerInitializeVirtualTable(layerObj *layer) {
2542
assert(layer != NULL);
2543
assert(layer->vtable != NULL);
2545
layer->vtable->LayerInitItemInfo = msPostGISLayerInitItemInfo;
2546
layer->vtable->LayerFreeItemInfo = msPostGISLayerFreeItemInfo;
2547
layer->vtable->LayerOpen = msPostGISLayerOpen;
2548
layer->vtable->LayerIsOpen = msPostGISLayerIsOpen;
2549
layer->vtable->LayerWhichShapes = msPostGISLayerWhichShapes;
2550
layer->vtable->LayerNextShape = msPostGISLayerNextShape;
2551
layer->vtable->LayerResultsGetShape = msPostGISLayerResultsGetShape;
2552
layer->vtable->LayerGetShape = msPostGISLayerGetShape;
2553
layer->vtable->LayerClose = msPostGISLayerClose;
2554
layer->vtable->LayerGetItems = msPostGISLayerGetItems;
2555
layer->vtable->LayerGetExtent = msPostGISLayerGetExtent;
2556
layer->vtable->LayerApplyFilterToLayer = msLayerApplyCondSQLFilterToLayer;
2557
/* layer->vtable->LayerGetAutoStyle, not supported for this layer */
2558
layer->vtable->LayerCloseConnection = msPostGISLayerClose;
2559
layer->vtable->LayerSetTimeFilter = msPostGISLayerSetTimeFilter;
2560
/* layer->vtable->LayerCreateItems, use default */
2561
/* layer->vtable->LayerGetNumFeatures, use default */