1 /**********************************************************************
2 *
3 * PostGIS - Spatial Types for PostgreSQL
4 * http://postgis.net
5 *
6 * Copyright (C) 2001-2003 Refractions Research Inc.
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU General Public Licence. See the COPYING file.
10 *
11 **********************************************************************
12 *
13 * PostGIS to Shapefile converter
14 *
15 * Original Author: Jeff Lounsbury <jeffloun@refractions.net>
16 * Contributions by: Sandro Santilli <strk@keybit.bet>
17 * Enhanced by: Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
18 * Enhanced by: Regina Obe <lr@pcorp.us>
19 *
20 **********************************************************************/
21
22 #include "../postgis_config.h"
23
24 #include "pgsql2shp-core.h"
25
26 /* Solaris9 does not provide stdint.h */
27 /* #include <stdint.h> */
28 #include <inttypes.h>
29
30 #ifdef HAVE_UNISTD_H /* for getpid() and getopt */
31 #include <unistd.h>
32 #endif
33
34 #ifdef __CYGWIN__
35 #include <sys/param.h>
36 #endif
37
38 #include "../liblwgeom/liblwgeom.h" /* for LWGEOM struct and funx */
39 #include "../liblwgeom/lwgeom_log.h" /* for LWDEBUG macros */
40
41 /* Maximum DBF field width (according to ARCGIS) */
42 #define MAX_DBF_FIELD_SIZE 254
43
44
45 /* Prototypes */
46 static int reverse_points(int num_points, double *x, double *y, double *z, double *m);
47 static int is_clockwise(int num_points,double *x,double *y,double *z);
48 static int is_bigendian(void);
49 static SHPObject *create_point(SHPDUMPERSTATE *state, LWPOINT *lwpoint);
50 static SHPObject *create_multipoint(SHPDUMPERSTATE *state, LWMPOINT *lwmultipoint);
51 static SHPObject *create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon);
52 static SHPObject *create_multipolygon(SHPDUMPERSTATE *state, LWMPOLY *lwmultipolygon);
53 static SHPObject *create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring);
54 static SHPObject *create_multilinestring(SHPDUMPERSTATE *state, LWMLINE *lwmultilinestring);
55 static char *nullDBFValue(char fieldType);
56 static int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname);
57 static int getTableInfo(SHPDUMPERSTATE *state);
58 static int projFileCreate(SHPDUMPERSTATE *state);
59
60 /**
61 * @brief Make appropriate formatting of a DBF value based on type.
62 * Might return untouched input or pointer to static private
63 * buffer: use return value right away.
64 */
65 static char * goodDBFValue(char *in, char fieldType);
66
67 /** @brief Binary to hexewkb conversion function */
68 char *convert_bytes_to_hex(uint8_t *ewkb, size_t size);
69
70 static SHPObject *
create_point_empty(SHPDUMPERSTATE * state,LWPOINT * lwpoint)71 create_point_empty(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
72 {
73 SHPObject *obj;
74 const uint8_t ndr_nan[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f};
75 double double_nan;
76
77 memcpy(&double_nan, ndr_nan, 8);
78 obj = SHPCreateObject(state->outshptype, -1, 0, NULL, NULL, 1, &double_nan, &double_nan, &double_nan, &double_nan);
79 return obj;
80 }
81
82 static SHPObject *
create_point(SHPDUMPERSTATE * state,LWPOINT * lwpoint)83 create_point(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
84 {
85 SHPObject *obj;
86 POINT4D p4d;
87
88 double *xpts, *ypts, *zpts, *mpts;
89
90 /* Allocate storage for points */
91 xpts = malloc(sizeof(double));
92 ypts = malloc(sizeof(double));
93 zpts = malloc(sizeof(double));
94 mpts = malloc(sizeof(double));
95
96 /* Grab the point: note getPoint4d will correctly handle
97 the case where the POINTs don't contain Z or M coordinates */
98 p4d = getPoint4d(lwpoint->point, 0);
99
100 xpts[0] = p4d.x;
101 ypts[0] = p4d.y;
102 zpts[0] = p4d.z;
103 mpts[0] = p4d.m;
104
105 LWDEBUGF(4, "Point: %g %g %g %g", xpts[0], ypts[0], zpts[0], mpts[0]);
106
107 obj = SHPCreateObject(state->outshptype, -1, 0, NULL, NULL, 1, xpts, ypts, zpts, mpts);
108
109 free(xpts);
110 free(ypts);
111 free(zpts);
112 free(mpts);
113
114 return obj;
115 }
116
117 static SHPObject *
create_multipoint(SHPDUMPERSTATE * state,LWMPOINT * lwmultipoint)118 create_multipoint(SHPDUMPERSTATE *state, LWMPOINT *lwmultipoint)
119 {
120 SHPObject *obj;
121 POINT4D p4d;
122 uint32_t i;
123
124 double *xpts, *ypts, *zpts, *mpts;
125
126 /* Allocate storage for points */
127 xpts = malloc(sizeof(double) * lwmultipoint->ngeoms);
128 ypts = malloc(sizeof(double) * lwmultipoint->ngeoms);
129 zpts = malloc(sizeof(double) * lwmultipoint->ngeoms);
130 mpts = malloc(sizeof(double) * lwmultipoint->ngeoms);
131
132 /* Grab the points: note getPoint4d will correctly handle
133 the case where the POINTs don't contain Z or M coordinates */
134 for (i = 0; i < lwmultipoint->ngeoms; i++)
135 {
136 p4d = getPoint4d(lwmultipoint->geoms[i]->point, 0);
137
138 xpts[i] = p4d.x;
139 ypts[i] = p4d.y;
140 zpts[i] = p4d.z;
141 mpts[i] = p4d.m;
142
143 LWDEBUGF(4, "MultiPoint %d - Point: %g %g %g %g", i, xpts[i], ypts[i], zpts[i], mpts[i]);
144 }
145
146 obj = SHPCreateObject(state->outshptype, -1, 0, NULL, NULL, lwmultipoint->ngeoms, xpts, ypts, zpts, mpts);
147
148 free(xpts);
149 free(ypts);
150 free(zpts);
151 free(mpts);
152
153 return obj;
154 }
155
156 static SHPObject *
create_polygon(SHPDUMPERSTATE * state,LWPOLY * lwpolygon)157 create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon)
158 {
159 SHPObject *obj;
160 POINT4D p4d;
161 uint32_t i, j;
162
163 double *xpts, *ypts, *zpts, *mpts;
164
165 int *shpparts, shppointtotal = 0, shppoint = 0;
166
167 /* Allocate storage for ring pointers */
168 shpparts = malloc(sizeof(int) * lwpolygon->nrings);
169
170 /* First count through all the points in each ring so we now how much memory is required */
171 for (i = 0; i < lwpolygon->nrings; i++)
172 shppointtotal += lwpolygon->rings[i]->npoints;
173
174 /* Allocate storage for points */
175 xpts = malloc(sizeof(double) * shppointtotal);
176 ypts = malloc(sizeof(double) * shppointtotal);
177 zpts = malloc(sizeof(double) * shppointtotal);
178 mpts = malloc(sizeof(double) * shppointtotal);
179
180 LWDEBUGF(4, "Total number of points: %d", shppointtotal);
181
182 /* Iterate through each ring setting up shpparts to point to the beginning of each ring */
183 for (i = 0; i < lwpolygon->nrings; i++)
184 {
185 /* For each ring, store the integer coordinate offset for the start of each ring */
186 shpparts[i] = shppoint;
187
188 for (j = 0; j < lwpolygon->rings[i]->npoints; j++)
189 {
190 p4d = getPoint4d(lwpolygon->rings[i], j);
191
192 xpts[shppoint] = p4d.x;
193 ypts[shppoint] = p4d.y;
194 zpts[shppoint] = p4d.z;
195 mpts[shppoint] = p4d.m;
196
197 LWDEBUGF(4, "Polygon Ring %d - Point: %g %g %g %g", i, xpts[shppoint], ypts[shppoint], zpts[shppoint], mpts[shppoint]);
198
199 /* Increment the point counter */
200 shppoint++;
201 }
202
203 /*
204 * First ring should be clockwise,
205 * other rings should be counter-clockwise
206 */
207 if ( i == 0 )
208 {
209 if ( ! is_clockwise(lwpolygon->rings[i]->npoints,
210 &xpts[shpparts[i]], &ypts[shpparts[i]], NULL) )
211 {
212 LWDEBUG(4, "Outer ring not clockwise, forcing clockwise\n");
213
214 reverse_points(lwpolygon->rings[i]->npoints,
215 &xpts[shpparts[i]], &ypts[shpparts[i]],
216 &zpts[shpparts[i]], &mpts[shpparts[i]]);
217 }
218 }
219 else
220 {
221 if ( is_clockwise(lwpolygon->rings[i]->npoints,
222 &xpts[shpparts[i]], &ypts[shpparts[i]], NULL) )
223 {
224 LWDEBUGF(4, "Inner ring %d not counter-clockwise, forcing counter-clockwise\n", i);
225
226 reverse_points(lwpolygon->rings[i]->npoints,
227 &xpts[shpparts[i]], &ypts[shpparts[i]],
228 &zpts[shpparts[i]], &mpts[shpparts[i]]);
229 }
230 }
231 }
232
233 obj = SHPCreateObject(state->outshptype, -1, lwpolygon->nrings, shpparts, NULL, shppointtotal, xpts, ypts, zpts, mpts);
234
235 free(xpts);
236 free(ypts);
237 free(zpts);
238 free(mpts);
239 free(shpparts);
240
241 return obj;
242 }
243
244 static SHPObject *
create_multipolygon(SHPDUMPERSTATE * state,LWMPOLY * lwmultipolygon)245 create_multipolygon(SHPDUMPERSTATE *state, LWMPOLY *lwmultipolygon)
246 {
247 SHPObject *obj;
248 POINT4D p4d;
249 uint32_t i, j, k;
250
251 double *xpts, *ypts, *zpts, *mpts;
252
253 int *shpparts, shppointtotal = 0, shppoint = 0, shpringtotal = 0, shpring = 0;
254
255 /* NOTE: Multipolygons are stored in shapefiles as Polygon* shapes with multiple outer rings */
256
257 /* First count through each ring of each polygon so we now know much memory is required */
258 for (i = 0; i < lwmultipolygon->ngeoms; i++)
259 {
260 for (j = 0; j < lwmultipolygon->geoms[i]->nrings; j++)
261 {
262 shpringtotal++;
263 shppointtotal += lwmultipolygon->geoms[i]->rings[j]->npoints;
264 }
265 }
266
267 /* Allocate storage for ring pointers */
268 shpparts = malloc(sizeof(int) * shpringtotal);
269
270 /* Allocate storage for points */
271 xpts = malloc(sizeof(double) * shppointtotal);
272 ypts = malloc(sizeof(double) * shppointtotal);
273 zpts = malloc(sizeof(double) * shppointtotal);
274 mpts = malloc(sizeof(double) * shppointtotal);
275
276 LWDEBUGF(4, "Total number of rings: %d Total number of points: %d", shpringtotal, shppointtotal);
277
278 /* Iterate through each ring of each polygon in turn */
279 for (i = 0; i < lwmultipolygon->ngeoms; i++)
280 {
281 for (j = 0; j < lwmultipolygon->geoms[i]->nrings; j++)
282 {
283 /* For each ring, store the integer coordinate offset for the start of each ring */
284 shpparts[shpring] = shppoint;
285
286 LWDEBUGF(4, "Ring offset: %d", shpring);
287
288 for (k = 0; k < lwmultipolygon->geoms[i]->rings[j]->npoints; k++)
289 {
290 p4d = getPoint4d(lwmultipolygon->geoms[i]->rings[j], k);
291
292 xpts[shppoint] = p4d.x;
293 ypts[shppoint] = p4d.y;
294 zpts[shppoint] = p4d.z;
295 mpts[shppoint] = p4d.m;
296
297 LWDEBUGF(4, "MultiPolygon %d Polygon Ring %d - Point: %g %g %g %g", i, j, xpts[shppoint], ypts[shppoint], zpts[shppoint], mpts[shppoint]);
298
299 /* Increment the point counter */
300 shppoint++;
301 }
302
303 /*
304 * First ring should be clockwise,
305 * other rings should be counter-clockwise
306 */
307 if ( j == 0 )
308 {
309 if ( ! is_clockwise(lwmultipolygon->geoms[i]->rings[j]->npoints,
310 &xpts[shpparts[shpring]], &ypts[shpparts[shpring]], NULL) )
311 {
312 LWDEBUG(4, "Outer ring not clockwise, forcing clockwise\n");
313
314 reverse_points(lwmultipolygon->geoms[i]->rings[j]->npoints,
315 &xpts[shpparts[shpring]], &ypts[shpparts[shpring]],
316 &zpts[shpparts[shpring]], &mpts[shpparts[shpring]]);
317 }
318 }
319 else
320 {
321 if ( is_clockwise(lwmultipolygon->geoms[i]->rings[j]->npoints,
322 &xpts[shpparts[shpring]], &ypts[shpparts[shpring]], NULL) )
323 {
324 LWDEBUGF(4, "Inner ring %d not counter-clockwise, forcing counter-clockwise\n", i);
325
326 reverse_points(lwmultipolygon->geoms[i]->rings[j]->npoints,
327 &xpts[shpparts[shpring]], &ypts[shpparts[shpring]],
328 &zpts[shpparts[shpring]], &mpts[shpparts[shpring]]);
329 }
330 }
331
332 /* Increment the ring counter */
333 shpring++;
334 }
335 }
336
337 obj = SHPCreateObject(state->outshptype, -1, shpringtotal, shpparts, NULL, shppointtotal, xpts, ypts, zpts, mpts);
338
339 free(xpts);
340 free(ypts);
341 free(zpts);
342 free(mpts);
343 free(shpparts);
344
345 return obj;
346 }
347
348 static SHPObject *
create_linestring(SHPDUMPERSTATE * state,LWLINE * lwlinestring)349 create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring)
350 {
351 SHPObject *obj;
352 POINT4D p4d;
353 uint32_t i;
354
355 double *xpts, *ypts, *zpts, *mpts;
356
357 /* Allocate storage for points */
358 xpts = malloc(sizeof(double) * lwlinestring->points->npoints);
359 ypts = malloc(sizeof(double) * lwlinestring->points->npoints);
360 zpts = malloc(sizeof(double) * lwlinestring->points->npoints);
361 mpts = malloc(sizeof(double) * lwlinestring->points->npoints);
362
363 /* Grab the points: note getPoint4d will correctly handle
364 the case where the POINTs don't contain Z or M coordinates */
365 for (i = 0; i < lwlinestring->points->npoints; i++)
366 {
367 p4d = getPoint4d(lwlinestring->points, i);
368
369 xpts[i] = p4d.x;
370 ypts[i] = p4d.y;
371 zpts[i] = p4d.z;
372 mpts[i] = p4d.m;
373
374 LWDEBUGF(4, "Linestring - Point: %g %g %g %g", i, xpts[i], ypts[i], zpts[i], mpts[i]);
375 }
376
377 obj = SHPCreateObject(state->outshptype, -1, 0, NULL, NULL, lwlinestring->points->npoints, xpts, ypts, zpts, mpts);
378
379 free(xpts);
380 free(ypts);
381 free(zpts);
382 free(mpts);
383
384 return obj;
385 }
386
387 static SHPObject *
create_multilinestring(SHPDUMPERSTATE * state,LWMLINE * lwmultilinestring)388 create_multilinestring(SHPDUMPERSTATE *state, LWMLINE *lwmultilinestring)
389 {
390 SHPObject *obj;
391 POINT4D p4d;
392 uint32_t i, j;
393
394 double *xpts, *ypts, *zpts, *mpts;
395
396 int *shpparts, shppointtotal = 0, shppoint = 0;
397
398 /* Allocate storage for ring pointers */
399 shpparts = malloc(sizeof(int) * lwmultilinestring->ngeoms);
400
401 /* First count through all the points in each linestring so we now how much memory is required */
402 for (i = 0; i < lwmultilinestring->ngeoms; i++)
403 shppointtotal += lwmultilinestring->geoms[i]->points->npoints;
404
405 LWDEBUGF(3, "Total number of points: %d", shppointtotal);
406
407 /* Allocate storage for points */
408 xpts = malloc(sizeof(double) * shppointtotal);
409 ypts = malloc(sizeof(double) * shppointtotal);
410 zpts = malloc(sizeof(double) * shppointtotal);
411 mpts = malloc(sizeof(double) * shppointtotal);
412
413 /* Iterate through each linestring setting up shpparts to point to the beginning of each line */
414 for (i = 0; i < lwmultilinestring->ngeoms; i++)
415 {
416 /* For each linestring, store the integer coordinate offset for the start of each line */
417 shpparts[i] = shppoint;
418
419 for (j = 0; j < lwmultilinestring->geoms[i]->points->npoints; j++)
420 {
421 p4d = getPoint4d(lwmultilinestring->geoms[i]->points, j);
422
423 xpts[shppoint] = p4d.x;
424 ypts[shppoint] = p4d.y;
425 zpts[shppoint] = p4d.z;
426 mpts[shppoint] = p4d.m;
427
428 LWDEBUGF(4, "Linestring %d - Point: %g %g %g %g", i, xpts[shppoint], ypts[shppoint], zpts[shppoint], mpts[shppoint]);
429
430 /* Increment the point counter */
431 shppoint++;
432 }
433 }
434
435 obj = SHPCreateObject(state->outshptype, -1, lwmultilinestring->ngeoms, shpparts, NULL, shppoint, xpts, ypts, zpts, mpts);
436
437 free(xpts);
438 free(ypts);
439 free(zpts);
440 free(mpts);
441
442 return obj;
443 }
444
445
446
447 /*Reverse the clockwise-ness of the point list... */
448 static int
reverse_points(int num_points,double * x,double * y,double * z,double * m)449 reverse_points(int num_points, double *x, double *y, double *z, double *m)
450 {
451
452 int i,j;
453 double temp;
454 j = num_points -1;
455 for (i=0; i <num_points; i++)
456 {
457 if (j <= i)
458 {
459 break;
460 }
461 temp = x[j];
462 x[j] = x[i];
463 x[i] = temp;
464
465 temp = y[j];
466 y[j] = y[i];
467 y[i] = temp;
468
469 if ( z )
470 {
471 temp = z[j];
472 z[j] = z[i];
473 z[i] = temp;
474 }
475
476 if ( m )
477 {
478 temp = m[j];
479 m[j] = m[i];
480 m[i] = temp;
481 }
482
483 j--;
484 }
485 return 1;
486 }
487
488 /* Return 1 if the points are in clockwise order */
489 static int
is_clockwise(int num_points,double * x,double * y,double * z)490 is_clockwise(int num_points, double *x, double *y, double *z)
491 {
492 int i;
493 double x_change,y_change,area;
494 double *x_new, *y_new; /* the points, translated to the origin
495 * for safer accuracy */
496
497 x_new = (double *)malloc(sizeof(double) * num_points);
498 y_new = (double *)malloc(sizeof(double) * num_points);
499 area=0.0;
500 x_change = x[0];
501 y_change = y[0];
502
503 for (i=0; i < num_points ; i++)
504 {
505 x_new[i] = x[i] - x_change;
506 y_new[i] = y[i] - y_change;
507 }
508
509 for (i=0; i < num_points - 1; i++)
510 {
511 /* calculate the area */
512 area += (x[i] * y[i+1]) - (y[i] * x[i+1]);
513 }
514 if (area > 0 )
515 {
516 free(x_new);
517 free(y_new);
518 return 0; /*counter-clockwise */
519 }
520 else
521 {
522 free(x_new);
523 free(y_new);
524 return 1; /*clockwise */
525 }
526 }
527
528
529 /*
530 * Return the maximum octet_length from given table.
531 * Return -1 on error.
532 */
533 static int
getMaxFieldSize(PGconn * conn,char * schema,char * table,char * fname)534 getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname)
535 {
536 int size;
537 char *query;
538 PGresult *res;
539
540 /*( this is ugly: don't forget counting the length */
541 /* when changing the fixed query strings ) */
542
543 if ( schema )
544 {
545 query = (char *)malloc(strlen(fname)+strlen(table)+
546 strlen(schema)+46);
547 sprintf(query,
548 "select max(octet_length(\"%s\"::text)) from \"%s\".\"%s\"",
549 fname, schema, table);
550 }
551 else
552 {
553 query = (char *)malloc(strlen(fname)+strlen(table)+46);
554 sprintf(query,
555 "select max(octet_length(\"%s\"::text)) from \"%s\"",
556 fname, table);
557 }
558
559 LWDEBUGF(4, "maxFieldLenQuery: %s\n", query);
560
561 res = PQexec(conn, query);
562 free(query);
563 if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK )
564 {
565 printf( _("Querying for maximum field length: %s"),
566 PQerrorMessage(conn));
567 return -1;
568 }
569
570 if (PQntuples(res) <= 0 )
571 {
572 PQclear(res);
573 return -1;
574 }
575 size = atoi(PQgetvalue(res, 0, 0));
576 PQclear(res);
577 return size;
578 }
579
580 static int
is_bigendian(void)581 is_bigendian(void)
582 {
583 int test = 1;
584
585 if ( (((char *)(&test))[0]) == 1)
586 {
587 return 0; /*NDR (little_endian) */
588 }
589 else
590 {
591 return 1; /*XDR (big_endian) */
592 }
593 }
594
595 char *
shapetypename(int num)596 shapetypename(int num)
597 {
598 switch (num)
599 {
600 case SHPT_NULL:
601 return "Null Shape";
602 case SHPT_POINT:
603 return "Point";
604 case SHPT_ARC:
605 return "PolyLine";
606 case SHPT_POLYGON:
607 return "Polygon";
608 case SHPT_MULTIPOINT:
609 return "MultiPoint";
610 case SHPT_POINTZ:
611 return "PointZ";
612 case SHPT_ARCZ:
613 return "PolyLineZ";
614 case SHPT_POLYGONZ:
615 return "PolygonZ";
616 case SHPT_MULTIPOINTZ:
617 return "MultiPointZ";
618 case SHPT_POINTM:
619 return "PointM";
620 case SHPT_ARCM:
621 return "PolyLineM";
622 case SHPT_POLYGONM:
623 return "PolygonM";
624 case SHPT_MULTIPOINTM:
625 return "MultiPointM";
626 case SHPT_MULTIPATCH:
627 return "MultiPatch";
628 default:
629 return "Unknown";
630 }
631 }
632
633
634 /* This is taken and adapted from dbfopen.c of shapelib */
635 static char *
nullDBFValue(char fieldType)636 nullDBFValue(char fieldType)
637 {
638 switch (fieldType)
639 {
640 case FTInteger:
641 case FTDouble:
642 /* NULL numeric fields have value "****************" */
643 return "****************";
644
645 case FTDate:
646 /* NULL date fields have value "00000000" */
647 return " ";
648
649 case FTLogical:
650 /* NULL boolean fields have value "?" */
651 return "?";
652
653 default:
654 /* empty string fields are considered NULL */
655 return "";
656 }
657 }
658
659 /**
660 * @brief Make appropriate formatting of a DBF value based on type.
661 * Might return untouched input or pointer to static private
662 * buffer: use return value right away.
663 */
664 static char *
goodDBFValue(char * in,char fieldType)665 goodDBFValue(char *in, char fieldType)
666 {
667 /*
668 * We only work on FTLogical and FTDate.
669 * FTLogical is 1 byte, FTDate is 8 byte (YYYYMMDD)
670 * We allocate space for 9 bytes to take
671 * terminating null into account
672 */
673 static char buf[9];
674
675 switch (fieldType)
676 {
677 case FTLogical:
678 buf[0] = toupper(in[0]);
679 buf[1]='\0';
680 return buf;
681 case FTDate:
682 buf[0]=in[0]; /* Y */
683 buf[1]=in[1]; /* Y */
684 buf[2]=in[2]; /* Y */
685 buf[3]=in[3]; /* Y */
686 buf[4]=in[5]; /* M */
687 buf[5]=in[6]; /* M */
688 buf[6]=in[8]; /* D */
689 buf[7]=in[9]; /* D */
690 buf[8]='\0';
691 return buf;
692 default:
693 return in;
694 }
695 }
696
convert_bytes_to_hex(uint8_t * ewkb,size_t size)697 char *convert_bytes_to_hex(uint8_t *ewkb, size_t size)
698 {
699 size_t i;
700 char *hexewkb;
701
702 /* Convert the byte stream to a hex string using liblwgeom's deparse_hex function */
703 hexewkb = malloc(size * 2 + 1);
704 for (i=0; i<size; ++i) deparse_hex(ewkb[i], &hexewkb[i * 2]);
705 hexewkb[size * 2] = '\0';
706
707 return hexewkb;
708 }
709
710 /**
711 * @brief Creates ESRI .prj file for this shp output
712 * It looks in the spatial_ref_sys table and outputs the srtext field for this data
713 * If data is a table will use geometry_columns, if a query or view will read SRID from query output.
714 * @warning Will give warning and not output a .prj file if SRID is -1, Unknown, mixed SRIDS or not found in spatial_ref_sys. The dbf and shp will still be output.
715 */
716 static int
projFileCreate(SHPDUMPERSTATE * state)717 projFileCreate(SHPDUMPERSTATE *state)
718 {
719 FILE *fp;
720 char *pszFullname, *pszBasename;
721 int i;
722
723 char *pszFilename = state->shp_file;
724 char *schema = state->schema;
725 char *table = state->table;
726 char *geo_col_name = state->geo_col_name;
727
728 char *srtext;
729 char *query;
730 char *esc_schema;
731 char *esc_table;
732 char *esc_geo_col_name;
733
734 int error, result;
735 PGresult *res;
736 int size;
737
738 /***********
739 *** I'm multiplying by 2 instead of 3 because I am too lazy to figure out how many characters to add
740 *** after escaping if any **/
741 size = 1000;
742 if ( schema )
743 {
744 size += 3 * strlen(schema);
745 }
746 size += 1000;
747 esc_table = (char *) malloc(3 * strlen(table) + 1);
748 esc_geo_col_name = (char *) malloc(3 * strlen(geo_col_name) + 1);
749 PQescapeStringConn(state->conn, esc_table, table, strlen(table), &error);
750 PQescapeStringConn(state->conn, esc_geo_col_name, geo_col_name, strlen(geo_col_name), &error);
751
752 /** make our address space large enough to hold query with table/schema **/
753 query = (char *) malloc(size);
754 if ( ! query ) return 0; /* out of virtual memory */
755
756 /**************************************************
757 * Get what kind of spatial ref is the selected geometry field
758 * We first check the geometry_columns table for a match and then if no match do a distinct against the table
759 * NOTE: COALESCE does a short-circuit check returning the faster query result and skipping the second if first returns something
760 * Escaping quotes in the schema and table in query may not be necessary except to prevent malicious attacks
761 * or should someone be crazy enough to have quotes or other weird character in their table, column or schema names
762 **************************************************/
763 if ( schema )
764 {
765 esc_schema = (char *) malloc(2 * strlen(schema) + 1);
766 PQescapeStringConn(state->conn, esc_schema, schema, strlen(schema), &error);
767 sprintf(query, "SELECT COALESCE((SELECT sr.srtext "
768 " FROM geometry_columns As gc INNER JOIN spatial_ref_sys sr ON sr.srid = gc.srid "
769 " WHERE gc.f_table_schema = '%s' AND gc.f_table_name = '%s' AND gc.f_geometry_column = '%s' LIMIT 1), "
770 " (SELECT CASE WHEN COUNT(DISTINCT sr.srid) > 1 THEN 'm' ELSE MAX(sr.srtext) END As srtext "
771 " FROM \"%s\".\"%s\" As g INNER JOIN spatial_ref_sys sr ON sr.srid = ST_SRID((g.\"%s\")::geometry)) , ' ') As srtext ",
772 esc_schema, esc_table,esc_geo_col_name, schema, table, geo_col_name);
773 free(esc_schema);
774 }
775 else
776 {
777 sprintf(query, "SELECT COALESCE((SELECT sr.srtext "
778 " FROM geometry_columns As gc INNER JOIN spatial_ref_sys sr ON sr.srid = gc.srid "
779 " WHERE gc.f_table_name = '%s' AND gc.f_geometry_column = '%s' AND pg_table_is_visible((gc.f_table_schema || '.' || gc.f_table_name)::regclass) LIMIT 1), "
780 " (SELECT CASE WHEN COUNT(DISTINCT sr.srid) > 1 THEN 'm' ELSE MAX(sr.srtext) END as srtext "
781 " FROM \"%s\" As g INNER JOIN spatial_ref_sys sr ON sr.srid = ST_SRID((g.\"%s\")::geometry)), ' ') As srtext ",
782 esc_table, esc_geo_col_name, table, geo_col_name);
783 }
784
785 LWDEBUGF(3,"%s\n",query);
786 free(esc_table);
787 free(esc_geo_col_name);
788
789 res = PQexec(state->conn, query);
790
791 if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK )
792 {
793 snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Could not execute prj query: %s"), PQresultErrorMessage(res));
794 PQclear(res);
795 free(query);
796 return SHPDUMPERWARN;
797 }
798
799 for (i=0; i < PQntuples(res); i++)
800 {
801 srtext = PQgetvalue(res, i, 0);
802 if (strcmp(srtext,"m") == 0)
803 {
804 snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Mixed set of spatial references. No prj file will be generated"));
805 PQclear(res);
806 free(query);
807 return SHPDUMPERWARN;
808 }
809 else
810 {
811 if (srtext[0] == ' ')
812 {
813 snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Cannot determine spatial reference (empty table or unknown spatial ref). No prj file will be generated."));
814 PQclear(res);
815 free(query);
816 return SHPDUMPERWARN;
817 }
818 else
819 {
820 /* -------------------------------------------------------------------- */
821 /* Compute the base (layer) name. If there is any extension */
822 /* on the passed in filename we will strip it off. */
823 /* -------------------------------------------------------------------- */
824 pszBasename = (char *) malloc(strlen(pszFilename)+5);
825 strcpy( pszBasename, pszFilename );
826 for ( i = strlen(pszBasename)-1;
827 i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
828 && pszBasename[i] != '\\';
829 i-- ) {}
830
831 if ( pszBasename[i] == '.' )
832 pszBasename[i] = '\0';
833
834 pszFullname = (char *) malloc(strlen(pszBasename) + 5);
835 sprintf( pszFullname, "%s.prj", pszBasename );
836 free( pszBasename );
837
838
839 /* -------------------------------------------------------------------- */
840 /* Create the file. */
841 /* -------------------------------------------------------------------- */
842 fp = fopen( pszFullname, "wb" );
843 if ( fp == NULL )
844 {
845 free(pszFullname);
846 free(query);
847 return 0;
848 }
849 else
850 {
851 result = fputs (srtext,fp);
852 LWDEBUGF(3, "\n result %d proj SRText is %s .\n", result, srtext);
853 if (result == EOF)
854 {
855 fclose( fp );
856 free( pszFullname );
857 PQclear(res);
858 free(query);
859 return 0;
860 }
861 }
862 fclose( fp );
863 free( pszFullname );
864 }
865 }
866 }
867 PQclear(res);
868 free(query);
869 return SHPDUMPEROK;
870 }
871
872
873 static int
getTableInfo(SHPDUMPERSTATE * state)874 getTableInfo(SHPDUMPERSTATE *state)
875 {
876
877 /* Get some more information from the table:
878 - count = total number of geometries/geographies in the table
879
880 and if we have found a suitable geometry column:
881
882 - max = maximum number of dimensions within the geometry/geography column
883 - geometrytype = string representing the geometry/geography type, e.g. POINT
884
885 Since max/geometrytype already require a sequential scan of the table, we may as
886 well get the row count too.
887 */
888
889 PGresult *res;
890 char *query;
891 int tmpint;
892
893
894 if (state->geo_col_name)
895 {
896 /* Include geometry information */
897 if (state->schema)
898 {
899 query = malloc(150 + 4 * strlen(state->geo_col_name) + strlen(state->schema) + strlen(state->table));
900
901 sprintf(query, "SELECT count(1), max(ST_zmflag(\"%s\"::geometry)), geometrytype(\"%s\"::geometry) FROM \"%s\".\"%s\" GROUP BY 3",
902 state->geo_col_name, state->geo_col_name, state->schema, state->table);
903 }
904 else
905 {
906 query = malloc(150 + 4 * strlen(state->geo_col_name) + strlen(state->table));
907
908 sprintf(query, "SELECT count(1), max(ST_zmflag(\"%s\"::geometry)), geometrytype(\"%s\"::geometry) FROM \"%s\" GROUP BY 3",
909 state->geo_col_name, state->geo_col_name, state->table);
910 }
911 }
912 else
913 {
914 /* Otherwise... just a row count will do */
915 if (state->schema)
916 {
917 query = malloc(40 + strlen(state->schema) + strlen(state->table));
918
919 sprintf(query, "SELECT count(1) FROM \"%s\".\"%s\"", state->schema, state->table);
920 }
921 else
922 {
923 query = malloc(40 + strlen(state->table));
924
925 sprintf(query, "SELECT count(1) FROM \"%s\"", state->table);
926 }
927 }
928
929 LWDEBUGF(3, "Table metadata query: %s\n", query);
930
931 res = PQexec(state->conn, query);
932 free(query);
933
934 if (PQresultStatus(res) != PGRES_TUPLES_OK)
935 {
936 snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Could not execute table metadata query: %s"), PQresultErrorMessage(res));
937 PQclear(res);
938 return SHPDUMPERERR;
939 }
940
941 /* Make sure we error if the table is empty */
942 if (PQntuples(res) == 0)
943 {
944 snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Could not determine table metadata (empty table)"));
945 PQclear(res);
946 return SHPDUMPERERR;
947 }
948
949 /* If we have a geo* column, get the dimension, type and count information */
950 if (state->geo_col_name)
951 {
952 /* If a table has a geometry column containing mixed types then
953 the metadata query will return multiple rows. We need to cycle
954 through all rows to determine if the type combinations are valid.
955
956 Note that if we find a combination of a MULTI and non-MULTI geometry
957 of the same type, we always choose MULTI to ensure that everything
958 gets output correctly. The create_* conversion functions are clever
959 enough to up-convert the non-MULTI geometry to a MULTI in this case. */
960
961 int dummy, i;
962 uint8_t type = 0;
963 int typefound = 0, typemismatch = 0;
964
965 state->rowcount = 0;
966
967 for (i = 0; i < PQntuples(res); i++)
968 {
969 /* skip null geometries */
970 if (PQgetisnull(res, i, 2))
971 {
972 state->rowcount += atoi(PQgetvalue(res, i, 0));
973 continue;
974 }
975
976 geometry_type_from_string(PQgetvalue(res, i, 2), &type, &dummy, &dummy);
977
978 /* We can always set typefound to that of the first column found */
979 if (!typefound)
980 typefound = type;
981
982 switch (type)
983 {
984 case MULTIPOINTTYPE:
985 if (typefound != MULTIPOINTTYPE && typefound != POINTTYPE)
986 typemismatch = 1;
987 else
988 typefound = MULTIPOINTTYPE;
989 break;
990
991 case MULTILINETYPE:
992 if (typefound != MULTILINETYPE && typefound != LINETYPE)
993 typemismatch = 1;
994 else
995 typefound = MULTILINETYPE;
996 break;
997
998 case MULTIPOLYGONTYPE:
999 if (typefound != MULTIPOLYGONTYPE && typefound != POLYGONTYPE)
1000 typemismatch = 1;
1001 else
1002 typefound = MULTIPOLYGONTYPE;
1003 break;
1004
1005 case POINTTYPE:
1006 if (typefound != POINTTYPE && typefound != MULTIPOINTTYPE)
1007 typemismatch = 1;
1008 else if (!lwtype_is_collection(type))
1009 typefound = POINTTYPE;
1010 break;
1011
1012 case LINETYPE:
1013 if (typefound != LINETYPE && typefound != MULTILINETYPE)
1014 typemismatch = 1;
1015 else if (!lwtype_is_collection(type))
1016 typefound = LINETYPE;
1017 break;
1018
1019 case POLYGONTYPE:
1020 if (typefound != POLYGONTYPE && typefound != MULTIPOLYGONTYPE)
1021 typemismatch = 1;
1022 else if (!lwtype_is_collection(type))
1023 typefound = POLYGONTYPE;
1024 break;
1025 }
1026
1027 /* Update the rowcount for each type */
1028 state->rowcount += atoi(PQgetvalue(res, i, 0));
1029
1030 /* Set up the dimension output type (note: regardless of how many rows
1031 the table metadata query returns, this value will be the same. But
1032 we'll choose to use the first value anyway) */
1033 tmpint = atoi(PQgetvalue(res, i, 1));
1034 switch (tmpint)
1035 {
1036 case 0:
1037 state->outtype = 's';
1038 break;
1039 case 1:
1040 state->outtype = 'm';
1041 break;
1042 default:
1043 state->outtype = 'z';
1044 break;
1045 }
1046
1047 }
1048
1049 /* Flag an error if the table contains incompatible geometry combinations */
1050 if (typemismatch)
1051 {
1052 snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Incompatible mixed geometry types in table"));
1053 PQclear(res);
1054 return SHPDUMPERERR;
1055 }
1056
1057 /* Set up the shapefile output type based upon the dimension information */
1058 switch (typefound)
1059 {
1060 case POINTTYPE:
1061 switch(state->outtype)
1062 {
1063 case 'z':
1064 state->outshptype = SHPT_POINTZ;
1065 break;
1066
1067 case 'm':
1068 state->outshptype = SHPT_POINTM;
1069 break;
1070
1071 default:
1072 state->outshptype = SHPT_POINT;
1073 }
1074 break;
1075
1076 case MULTIPOINTTYPE:
1077 switch(state->outtype)
1078 {
1079 case 'z':
1080 state->outshptype = SHPT_MULTIPOINTZ;
1081 break;
1082
1083 case 'm':
1084 state->outshptype = SHPT_MULTIPOINTM;
1085 break;
1086
1087 default:
1088 state->outshptype = SHPT_MULTIPOINT;
1089 }
1090 break;
1091
1092 case LINETYPE:
1093 case MULTILINETYPE:
1094 switch(state->outtype)
1095 {
1096 case 'z':
1097 state->outshptype = SHPT_ARCZ;
1098 break;
1099
1100 case 'm':
1101 state->outshptype = SHPT_ARCM;
1102 break;
1103
1104 default:
1105 state->outshptype = SHPT_ARC;
1106 }
1107 break;
1108
1109 case POLYGONTYPE:
1110 case MULTIPOLYGONTYPE:
1111 switch(state->outtype)
1112 {
1113 case 'z':
1114 state->outshptype = SHPT_POLYGONZ;
1115 break;
1116
1117 case 'm':
1118 state->outshptype = SHPT_POLYGONM;
1119 break;
1120
1121 default:
1122 state->outshptype = SHPT_POLYGON;
1123 }
1124 break;
1125 }
1126 }
1127 else
1128 {
1129 /* Without a geo* column the total is simply the first (COUNT) column */
1130 state->rowcount = atoi(PQgetvalue(res, 0, 0));
1131 }
1132
1133 /* Dispose of the result set */
1134 PQclear(res);
1135
1136 return SHPDUMPEROK;
1137 }
1138
1139
1140 /* Default configuration settings */
1141 void
set_dumper_config_defaults(SHPDUMPERCONFIG * config)1142 set_dumper_config_defaults(SHPDUMPERCONFIG *config)
1143 {
1144 config->conn = malloc(sizeof(SHPCONNECTIONCONFIG));
1145 config->conn->host = NULL;
1146 config->conn->port = NULL;
1147 config->conn->database = NULL;
1148 config->conn->username = NULL;
1149 config->conn->password = NULL;
1150
1151 config->table = NULL;
1152 config->schema = NULL;
1153 config->usrquery = NULL;
1154 config->binary = 0;
1155 config->shp_file = NULL;
1156 config->dswitchprovided = 0;
1157 config->includegid = 0;
1158 config->unescapedattrs = 0;
1159 config->geo_col_name = NULL;
1160 config->keep_fieldname_case = 0;
1161 config->fetchsize = 100;
1162 config->column_map_filename = NULL;
1163 }
1164
1165 /* Create a new shapefile state object */
1166 SHPDUMPERSTATE *
ShpDumperCreate(SHPDUMPERCONFIG * config)1167 ShpDumperCreate(SHPDUMPERCONFIG *config)
1168 {
1169 SHPDUMPERSTATE *state;
1170
1171 /* Create a new state object and assign the config to it */
1172 state = malloc(sizeof(SHPDUMPERSTATE));
1173 state->config = config;
1174
1175 /* Set any state defaults */
1176 state->conn = NULL;
1177 state->outtype = 's';
1178 state->outshptype = 0;
1179 state->geom_oid = 0;
1180 state->geog_oid = 0;
1181 state->schema = NULL;
1182 state->table = NULL;
1183 state->geo_col_name = NULL;
1184 state->fetch_query = NULL;
1185 state->main_scan_query = NULL;
1186 state->dbffieldnames = NULL;
1187 state->dbffieldtypes = NULL;
1188 state->pgfieldnames = NULL;
1189 state->big_endian = is_bigendian();
1190 state->message[0] = '\0';
1191 colmap_init(&state->column_map);
1192
1193 return state;
1194 }
1195
1196 /* Generate the database connection string used by a state */
1197 char *
ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG * conn)1198 ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
1199 {
1200 char *connstring;
1201 int connlen;
1202
1203 connlen = 64 +
1204 (conn->host ? strlen(conn->host) : 0) + (conn->port ? strlen(conn->port) : 0) +
1205 (conn->username ? strlen(conn->username) : 0) + (conn->password ? strlen(conn->password) : 0) +
1206 (conn->database ? strlen(conn->database) : 0);
1207
1208 connstring = malloc(connlen);
1209 memset(connstring, 0, connlen);
1210
1211 if (conn->host)
1212 {
1213 strcat(connstring, " host=");
1214 strcat(connstring, conn->host);
1215 }
1216
1217 if (conn->port)
1218 {
1219 strcat(connstring, " port=");
1220 strcat(connstring, conn->port);
1221 }
1222
1223 if (conn->username)
1224 {
1225 strcat(connstring, " user=");
1226 strcat(connstring, conn->username);
1227 }
1228
1229 if (conn->password)
1230 {
1231 strcat(connstring, " password='");
1232 strcat(connstring, conn->password);
1233 strcat(connstring, "'");
1234 }
1235
1236 if (conn->database)
1237 {
1238 strcat(connstring, " dbname=");
1239 strcat(connstring, conn->database);
1240 }
1241
1242 if ( ! getenv("PGCLIENTENCODING") )
1243 {
1244 strcat(connstring, " client_encoding=UTF8");
1245 }
1246
1247 return connstring;
1248 }
1249
1250 /* Connect to the database and identify the version of PostGIS (and any other
1251 capabilities required) */
1252 int
ShpDumperConnectDatabase(SHPDUMPERSTATE * state)1253 ShpDumperConnectDatabase(SHPDUMPERSTATE *state)
1254 {
1255 PGresult *res;
1256
1257 char *connstring, *tmpvalue;
1258
1259 /* Generate the PostgreSQL connection string */
1260 connstring = ShpDumperGetConnectionStringFromConn(state->config->conn);
1261
1262 /* Connect to the database */
1263 state->conn = PQconnectdb(connstring);
1264 if (PQstatus(state->conn) == CONNECTION_BAD)
1265 {
1266 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQerrorMessage(state->conn));
1267 free(connstring);
1268 return SHPDUMPERERR;
1269 }
1270
1271 /* Set datestyle to ISO */
1272 res = PQexec(state->conn, "SET DATESTYLE='ISO'");
1273 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1274 {
1275 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1276 PQclear(res);
1277 free(connstring);
1278 return SHPDUMPERERR;
1279 }
1280
1281 PQclear(res);
1282
1283 /* Retrieve PostGIS major version */
1284 res = PQexec(state->conn, "SELECT postgis_version()");
1285 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1286 {
1287 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1288 PQclear(res);
1289 free(connstring);
1290 return SHPDUMPERERR;
1291 }
1292
1293 tmpvalue = PQgetvalue(res, 0, 0);
1294 state->pgis_major_version = atoi(tmpvalue);
1295
1296 PQclear(res);
1297
1298 /* Find the OID for the geometry type */
1299 res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1300 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1301 {
1302 snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geometry oid: %s"), PQresultErrorMessage(res));
1303 PQclear(res);
1304 free(connstring);
1305 return SHPDUMPERERR;
1306 }
1307
1308 if (PQntuples(res) > 0)
1309 {
1310 tmpvalue = PQgetvalue(res, 0, 0);
1311 state->geom_oid = atoi(tmpvalue);
1312 }
1313 else
1314 {
1315 snprintf(state->message, SHPDUMPERMSGLEN, _("Geometry type unknown (have you enabled postgis?)"));
1316 PQclear(res);
1317 free(connstring);
1318 return SHPDUMPERERR;
1319 }
1320
1321 PQclear(res);
1322
1323 /* Find the OID for the geography type */
1324 res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geography'");
1325 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1326 {
1327 snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geography oid: %s"), PQresultErrorMessage(res));
1328 PQclear(res);
1329 free(connstring);
1330 return SHPDUMPERERR;
1331 }
1332
1333 if (PQntuples(res) > 0)
1334 {
1335 /* Old databases don't have a geography type, so don't fail if we don't find it */
1336 tmpvalue = PQgetvalue(res, 0, 0);
1337 state->geog_oid = atoi(tmpvalue);
1338 }
1339
1340 PQclear(res);
1341
1342 free(connstring);
1343
1344 return SHPDUMPEROK;
1345 }
1346
1347
1348 /* Open the specified table in preparation for extracting rows */
1349 int
ShpDumperOpenTable(SHPDUMPERSTATE * state)1350 ShpDumperOpenTable(SHPDUMPERSTATE *state)
1351 {
1352 PGresult *res;
1353
1354 char buf[256];
1355 char *query;
1356 int gidfound = 0, i, j, ret, status;
1357
1358
1359 /* Open the column map if one was specified */
1360 if (state->config->column_map_filename)
1361 {
1362 ret = colmap_read(state->config->column_map_filename,
1363 &state->column_map, state->message, SHPDUMPERMSGLEN);
1364 if (!ret) return SHPDUMPERERR;
1365 }
1366
1367 /* If a user-defined query has been specified, create and point the state to our new table */
1368 if (state->config->usrquery)
1369 {
1370 state->table = malloc(20 + 20); /* string + max long precision */
1371 sprintf(state->table, "__pgsql2shp%lu_tmp_table", (long)getpid());
1372
1373 query = malloc(32 + strlen(state->table) + strlen(state->config->usrquery));
1374
1375 sprintf(query, "CREATE TEMP TABLE \"%s\" AS %s", state->table, state->config->usrquery);
1376 res = PQexec(state->conn, query);
1377 free(query);
1378
1379 /* Execute the code to create the table */
1380 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1381 {
1382 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing user query: %s"), PQresultErrorMessage(res));
1383 PQclear(res);
1384 return SHPDUMPERERR;
1385 }
1386 }
1387 else
1388 {
1389 /* Simply point the state to copies of the supplied schema and table */
1390 state->table = strdup(state->config->table);
1391 if (state->config->schema)
1392 state->schema = strdup(state->config->schema);
1393 }
1394
1395
1396 /* Get the list of columns and their types for the selected table */
1397 if (state->schema)
1398 {
1399 query = malloc(250 + strlen(state->schema) + strlen(state->table));
1400
1401 sprintf(query, "SELECT a.attname, a.atttypid, "
1402 "a.atttypmod, a.attlen FROM "
1403 "pg_attribute a, pg_class c, pg_namespace n WHERE "
1404 "n.nspname = '%s' AND a.attrelid = c.oid AND "
1405 "n.oid = c.relnamespace AND "
1406 "a.atttypid != 0 AND "
1407 "a.attnum > 0 AND c.relname = '%s'", state->schema, state->table);
1408 }
1409 else
1410 {
1411 query = malloc(250 + strlen(state->table));
1412
1413 sprintf(query, "SELECT a.attname, a.atttypid, "
1414 "a.atttypmod, a.attlen FROM "
1415 "pg_attribute a, pg_class c WHERE "
1416 "a.attrelid = c.oid and a.attnum > 0 AND "
1417 "a.atttypid != 0 AND "
1418 "c.relname = '%s' AND "
1419 "pg_catalog.pg_table_is_visible(c.oid)", state->table);
1420 }
1421
1422 LWDEBUGF(3, "query is: %s\n", query);
1423
1424 res = PQexec(state->conn, query);
1425 free(query);
1426
1427 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1428 {
1429 snprintf(state->message, SHPDUMPERMSGLEN, _("Error querying for attributes: %s"), PQresultErrorMessage(res));
1430 PQclear(res);
1431 return SHPDUMPERERR;
1432 }
1433
1434 if (!PQntuples(res))
1435 {
1436 snprintf(state->message, SHPDUMPERMSGLEN, _("Table %s does not exist"), state->table);
1437 PQclear(res);
1438 return SHPDUMPERERR;
1439 }
1440
1441 /* If a shapefile name was specified, use it. Otherwise simply use the table name. */
1442 if (state->config->shp_file != NULL)
1443 state->shp_file = state->config->shp_file;
1444 else
1445 state->shp_file = state->table;
1446
1447 /* Create the dbf file: */
1448 /* If there's a user-specified encoding hanging around, try and use that. */
1449 /* Otherwise, just use UTF-8 encoding, since that's usually our client encoding. */
1450 if ( getenv("PGCLIENTENCODING") )
1451 {
1452 char *codepage = encoding2codepage(getenv("PGCLIENTENCODING"));
1453 state->dbf = DBFCreateEx(state->shp_file, codepage);
1454 }
1455 else
1456 {
1457 state->dbf = DBFCreateEx(state->shp_file, "UTF-8");
1458 }
1459
1460 if (!state->dbf)
1461 {
1462 snprintf(state->message, SHPDUMPERMSGLEN, _("Could not create dbf file %s"), state->shp_file);
1463 return SHPDUMPERERR;
1464 }
1465
1466 /*
1467 * Scan the result setting fields to be returned in mainscan
1468 * query, filling the type_ary, and creating .dbf and .shp files.
1469 */
1470 state->dbffieldnames = malloc(sizeof(char *) * PQntuples(res));
1471 state->dbffieldtypes = malloc(sizeof(int) * PQntuples(res));
1472 state->pgfieldnames = malloc(sizeof(char *) * PQntuples(res));
1473 state->pgfieldlens = malloc(sizeof(int) * PQntuples(res));
1474 state->pgfieldtypmods = malloc(sizeof(int) * PQntuples(res));
1475 state->fieldcount = 0;
1476 int tmpint = 1;
1477
1478 for (i = 0; i < PQntuples(res); i++)
1479 {
1480 char *ptr;
1481
1482 int pgfieldtype, pgtypmod, pgfieldlen;
1483 char *pgfieldname;
1484
1485 int dbffieldtype, dbffieldsize, dbffielddecs;
1486 char *dbffieldname;
1487
1488 pgfieldname = PQgetvalue(res, i, 0);
1489 pgfieldtype = atoi(PQgetvalue(res, i, 1));
1490 pgtypmod = atoi(PQgetvalue(res, i, 2));
1491 pgfieldlen = atoi(PQgetvalue(res, i, 3));
1492 dbffieldtype = -1;
1493 dbffieldsize = 0;
1494 dbffielddecs = 0;
1495
1496 /*
1497 * This is a geometry/geography column
1498 */
1499 if (pgfieldtype == state->geom_oid || pgfieldtype == state->geog_oid)
1500 {
1501 /* If no geometry/geography column has been found yet... */
1502 if (!state->geo_col_name)
1503 {
1504 /* If either no geo* column name was provided (in which case this is
1505 the first match) or we match the provided column name, we have
1506 found our geo* column */
1507 if (!state->config->geo_col_name || !strcmp(state->config->geo_col_name, pgfieldname))
1508 {
1509 dbffieldtype = 9;
1510
1511 state->geo_col_name = strdup(pgfieldname);
1512 }
1513 }
1514 }
1515
1516 /*
1517 * Everything else (non geometries) will be
1518 * a DBF attribute.
1519 */
1520
1521 /* Skip gid (if not asked to do otherwise */
1522 if (!strcmp(pgfieldname, "gid") )
1523 {
1524 gidfound = 1;
1525
1526 if (!state->config->includegid)
1527 continue;
1528 }
1529
1530 /* Unescape any reserved column names */
1531 ptr = pgfieldname;
1532 if (!state->config->unescapedattrs)
1533 {
1534 if (*ptr == '_')
1535 ptr += 2;
1536 }
1537
1538 /*
1539 * This needs special handling since both xmin and _xmin
1540 * becomes __xmin when escaped
1541 */
1542
1543 /* Limit dbf field name to 10-digits */
1544 dbffieldname = malloc(11);
1545 strncpy(dbffieldname, ptr, 10);
1546 dbffieldname[10] = '\0';
1547
1548 /* If a column map file has been passed in,
1549 * use this to create the dbf field name from
1550 * the PostgreSQL column name */
1551 {
1552 const char *mapped = colmap_dbf_by_pg(&state->column_map, dbffieldname);
1553 if (mapped)
1554 {
1555 strncpy(dbffieldname, mapped, 10);
1556 dbffieldname[10] = '\0';
1557 }
1558 }
1559
1560 /*
1561 * make sure the fields all have unique names,
1562 */
1563 tmpint = 1;
1564 for (j = 0; j < state->fieldcount; j++)
1565 {
1566 if (!strncasecmp(dbffieldname, state->dbffieldnames[j], 10))
1567 {
1568 sprintf(dbffieldname, "%.7s_%.2d", ptr, abs(tmpint) % 100);
1569 tmpint++;
1570 continue;
1571 }
1572 }
1573
1574 /* make UPPERCASE if keep_fieldname_case = 0 */
1575 if (!state->config->keep_fieldname_case)
1576 {
1577 size_t nameit;
1578 for (nameit = 0; nameit < strlen(dbffieldname); nameit++)
1579 dbffieldname[nameit] = toupper(dbffieldname[nameit]);
1580 }
1581
1582 /* Issue warning if column has been renamed */
1583 if (strcasecmp(dbffieldname, pgfieldname))
1584 {
1585 if ( snprintf(buf, 256, _("Warning, field %s renamed to %s\n"),
1586 pgfieldname, dbffieldname) >= 256 )
1587 {
1588 buf[255] = '\0';
1589 }
1590 /* Note: we concatenate all warnings from the main loop as this is useful information */
1591 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message) - 1);
1592
1593 ret = SHPDUMPERWARN;
1594 }
1595
1596
1597 /*
1598 * Find appropriate type of dbf attributes
1599 */
1600
1601 /* int2 type */
1602 if (pgfieldtype == 21)
1603 {
1604 /*
1605 * Longest text representation for
1606 * an int2 type (16bit) is 6 bytes
1607 * (-32768)
1608 */
1609 dbffieldtype = FTInteger;
1610 dbffieldsize = 6;
1611 dbffielddecs = 0;
1612 }
1613
1614 /* int4 type */
1615 else if (pgfieldtype == 23)
1616 {
1617 /*
1618 * Longest text representation for
1619 * an int4 type (32bit) is 11 bytes
1620 * (-2147483648)
1621 */
1622 dbffieldtype = FTInteger;
1623 dbffieldsize = 11;
1624 dbffielddecs = 0;
1625 }
1626
1627 /* int8 type */
1628 else if (pgfieldtype == 20)
1629 {
1630 /*
1631 * Longest text representation for
1632 * an int8 type (64bit) is 20 bytes
1633 * (-9223372036854775808)
1634 */
1635 dbffieldtype = FTInteger;
1636 dbffieldsize = 19;
1637 dbffielddecs = 0;
1638 }
1639
1640 /*
1641 * double or numeric types:
1642 * 700: float4
1643 * 701: float8
1644 * 1700: numeric
1645 *
1646 *
1647 * TODO: stricter handling of sizes
1648 */
1649 else if (pgfieldtype == 700 || pgfieldtype == 701 || pgfieldtype == 1700)
1650 {
1651 dbffieldtype = FTDouble;
1652 dbffieldsize = 32;
1653 dbffielddecs = 10;
1654 }
1655
1656 /*
1657 * Boolean field, we use FTLogical
1658 */
1659 else if (pgfieldtype == 16)
1660 {
1661 dbffieldtype = FTLogical;
1662 dbffieldsize = 1;
1663 dbffielddecs = 0;
1664 }
1665
1666 /*
1667 * Date field
1668 */
1669 else if (pgfieldtype == 1082)
1670 {
1671 dbffieldtype = FTDate;
1672 dbffieldsize = 8;
1673 dbffielddecs = 0;
1674 }
1675
1676 /*
1677 * time, timetz, timestamp, or timestamptz field.
1678 */
1679 else if (pgfieldtype == 1083 || pgfieldtype == 1266 || pgfieldtype == 1114 || pgfieldtype == 1184)
1680 {
1681 int secondsize;
1682
1683 switch (pgtypmod)
1684 {
1685 case -1:
1686 secondsize = 6 + 1;
1687 break;
1688 case 0:
1689 secondsize = 0;
1690 break;
1691 default:
1692 secondsize = pgtypmod + 1;
1693 break;
1694 }
1695
1696 /* We assume the worst case scenario for all of these:
1697 * date = '5874897-12-31' = 13
1698 * date = '294276-11-20' = 12 (with --enable-interger-datetimes)
1699 * time = '00:00:00' = 8
1700 * zone = '+01:39:52' = 9 (see Europe/Helsinki around 1915)
1701 */
1702
1703 /* time */
1704 if (pgfieldtype == 1083)
1705 {
1706 dbffieldsize = 8 + secondsize;
1707 }
1708 /* timetz */
1709 else if (pgfieldtype == 1266)
1710 {
1711 dbffieldsize = 8 + secondsize + 9;
1712 }
1713 /* timestamp */
1714 else if (pgfieldtype == 1114)
1715 {
1716 dbffieldsize = 13 + 1 + 8 + secondsize;
1717 }
1718 /* timestamptz */
1719 else if (pgfieldtype == 1184)
1720 {
1721 dbffieldsize = 13 + 1 + 8 + secondsize + 9;
1722 }
1723
1724 dbffieldtype = FTString;
1725 dbffielddecs = 0;
1726 }
1727
1728 /*
1729 * uuid type 36 bytes (12345678-9012-3456-7890-123456789012)
1730 */
1731 else if (pgfieldtype == 2950)
1732 {
1733 dbffieldtype = FTString;
1734 dbffieldsize = 36;
1735 dbffielddecs = 0;
1736 }
1737
1738 /*
1739 * For variable-sized fields we know about, we use
1740 * the maximum allowed size.
1741 * 1042 is bpchar, 1043 is varchar
1742 */
1743 else if ((pgfieldtype == 1042 || pgfieldtype == 1043) && pgtypmod != -1)
1744 {
1745 /*
1746 * mod is maximum allowed size, including
1747 * header which contains *real* size.
1748 */
1749 dbffieldtype = FTString;
1750 dbffieldsize = pgtypmod - 4; /* 4 is header size */
1751 dbffielddecs = 0;
1752 }
1753
1754 /* For all other valid non-geometry/geography fields... */
1755 else if (dbffieldtype == -1)
1756 {
1757 /*
1758 * For types we don't know anything about, all
1759 * we can do is query the table for the maximum field
1760 * size.
1761 */
1762 dbffieldsize = getMaxFieldSize(state->conn, state->schema, state->table, pgfieldname);
1763 if (dbffieldsize == -1)
1764 {
1765 free(dbffieldname);
1766 return 0;
1767 }
1768
1769 if (!dbffieldsize)
1770 dbffieldsize = 32;
1771
1772 /* might 0 be a good size ? */
1773
1774 dbffieldtype = FTString;
1775 dbffielddecs = 0;
1776
1777 /* Check to make sure the final field size isn't too large */
1778 if (dbffieldsize > MAX_DBF_FIELD_SIZE)
1779 {
1780 /* Note: we concatenate all warnings from the main loop as this is useful information */
1781 snprintf(buf, 256, _("Warning: values of field '%s' exceeding maximum dbf field width (%d) "
1782 "will be truncated.\n"), dbffieldname, MAX_DBF_FIELD_SIZE);
1783 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1784 dbffieldsize = MAX_DBF_FIELD_SIZE;
1785
1786 ret = SHPDUMPERWARN;
1787 }
1788 }
1789
1790 LWDEBUGF(3, "DBF FIELD_NAME: %s, SIZE: %d\n", dbffieldname, dbffieldsize);
1791
1792 if (dbffieldtype != 9)
1793 {
1794 /* Add the field to the DBF file */
1795 if (DBFAddField(state->dbf, dbffieldname, dbffieldtype, dbffieldsize, dbffielddecs) == -1)
1796 {
1797 snprintf(state->message, SHPDUMPERMSGLEN, _("Error: field %s of type %d could not be created."), dbffieldname, dbffieldtype);
1798
1799 return SHPDUMPERERR;
1800 }
1801
1802 /* Add the field information to our field arrays */
1803 state->dbffieldnames[state->fieldcount] = dbffieldname;
1804 state->dbffieldtypes[state->fieldcount] = dbffieldtype;
1805 state->pgfieldnames[state->fieldcount] = pgfieldname;
1806 state->pgfieldlens[state->fieldcount] = pgfieldlen;
1807 state->pgfieldtypmods[state->fieldcount] = pgtypmod;
1808
1809 state->fieldcount++;
1810 }
1811 }
1812
1813 /* Now we have generated the field lists, grab some info about the table */
1814 status = getTableInfo(state);
1815 if (status == SHPDUMPERERR)
1816 return SHPDUMPERERR;
1817
1818 LWDEBUGF(3, "rows: %d\n", state->rowcount);
1819 LWDEBUGF(3, "shptype: %c\n", state->outtype);
1820 LWDEBUGF(3, "shpouttype: %d\n", state->outshptype);
1821
1822 /* If we didn't find a geometry/geography column... */
1823 if (!state->geo_col_name)
1824 {
1825 if (state->config->geo_col_name)
1826 {
1827 /* A geo* column was specified, but not found */
1828 snprintf(state->message, SHPDUMPERMSGLEN, _("%s: no such attribute in table %s"), state->config->geo_col_name, state->table);
1829
1830 return SHPDUMPERERR;
1831 }
1832 else
1833 {
1834 /* No geo* column specified so we can only create the DBF section -
1835 but let's issue a warning... */
1836 snprintf(buf, 256, _("No geometry column found.\nThe DBF file will be created but not the shx or shp files.\n"));
1837 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1838
1839 state->shp = NULL;
1840
1841 ret = SHPDUMPERWARN;
1842 }
1843 }
1844 else
1845 {
1846 /* Since we have found a geo* column, open the shapefile */
1847 state->shp = SHPCreate(state->shp_file, state->outshptype);
1848 if (!state->shp)
1849 {
1850 snprintf(state->message, SHPDUMPERMSGLEN, _("Could not open shapefile %s!"), state->shp_file);
1851
1852 return SHPDUMPERERR;
1853 }
1854 }
1855
1856
1857 /* Now we have the complete list of fieldnames, let's generate the SQL query. First let's make sure
1858 we reserve enough space for tables with lots of columns */
1859 j = 0;
1860 /*TODO: this really should be rewritten to use stringbuffer */
1861 for (i = 0; i < state->fieldcount; i++)
1862 j += strlen( state->pgfieldnames[i]) + 10; /*add extra space for the quotes to quote identify and any embedded quotes that may need escaping */
1863
1864 state->main_scan_query = malloc(1024 + j);
1865
1866 sprintf(state->main_scan_query, "DECLARE cur ");
1867 if (state->config->binary)
1868 strcat(state->main_scan_query, "BINARY ");
1869
1870 strcat(state->main_scan_query, "CURSOR FOR SELECT ");
1871
1872 for (i = 0; i < state->fieldcount; i++)
1873 {
1874 /* Comma-separated column names */
1875 if (i > 0)
1876 strcat(state->main_scan_query, ",");
1877
1878 if (state->config->binary)
1879 sprintf(buf, "%s::text", quote_identifier(state->pgfieldnames[i]) ) ;
1880 else
1881 sprintf(buf, "%s", quote_identifier(state->pgfieldnames[i]) );
1882
1883 strcat(state->main_scan_query, buf);
1884 }
1885
1886 /* If we found a valid geometry/geography column then use it */
1887 if (state->geo_col_name)
1888 {
1889 /* If this is the (only) column, no need for the initial comma */
1890 if (state->fieldcount > 0)
1891 strcat(state->main_scan_query, ",");
1892
1893 if (state->big_endian)
1894 {
1895 if (state->pgis_major_version > 0)
1896 {
1897 sprintf(buf, "ST_asEWKB(ST_SetSRID(%s::geometry, 0), 'XDR') AS _geoX", quote_identifier(state->geo_col_name) );
1898 }
1899 else
1900 {
1901 sprintf(buf, "asbinary(%s::geometry, 'XDR') AS _geoX",
1902 quote_identifier(state->geo_col_name) );
1903 }
1904 }
1905 else /* little_endian */
1906 {
1907 if (state->pgis_major_version > 0)
1908 {
1909 sprintf(buf, "ST_AsEWKB(ST_SetSRID(%s::geometry, 0), 'NDR') AS _geoX", quote_identifier(state->geo_col_name) ) ;
1910 }
1911 else
1912 {
1913 sprintf(buf, "asbinary(%s::geometry, 'NDR') AS _geoX",
1914 quote_identifier(state->geo_col_name) );
1915 }
1916 }
1917
1918 strcat(state->main_scan_query, buf);
1919 }
1920
1921 if (state->schema)
1922 {
1923 sprintf(buf, " FROM \"%s\".\"%s\"", state->schema, state->table);
1924 }
1925 else
1926 {
1927 sprintf(buf, " FROM \"%s\"", state->table);
1928 }
1929
1930 strcat(state->main_scan_query, buf);
1931
1932 /* Order by 'gid' (if found) */
1933 if (gidfound)
1934 {
1935 sprintf(buf, " ORDER BY \"gid\"");
1936 strcat(state->main_scan_query, buf);
1937 }
1938
1939 /* Now we've finished with the result set, we can dispose of it */
1940 PQclear(res);
1941
1942 LWDEBUGF(3, "FINAL QUERY: %s\n", state->main_scan_query);
1943
1944 /*
1945 * Begin the transaction
1946 * (a cursor can only be defined inside a transaction block)
1947 */
1948 res = PQexec(state->conn, "BEGIN");
1949 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1950 {
1951 snprintf(state->message, SHPDUMPERMSGLEN, _("Error starting transaction: %s"), PQresultErrorMessage(res));
1952 PQclear(res);
1953 return SHPDUMPERERR;
1954 }
1955
1956 PQclear(res);
1957
1958 /* Execute the main scan query */
1959 res = PQexec(state->conn, state->main_scan_query);
1960 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1961 {
1962 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing main scan query: %s"), PQresultErrorMessage(res));
1963 PQclear(res);
1964 return SHPDUMPERERR;
1965 }
1966
1967 PQclear(res);
1968
1969 /* Setup initial scan state */
1970 state->currow = 0;
1971 state->curresrow = 0;
1972 state->currescount = 0;
1973 state->fetchres = NULL;
1974
1975 /* Generate the fetch query */
1976 state->fetch_query = malloc(256);
1977 sprintf(state->fetch_query, "FETCH %d FROM cur", state->config->fetchsize);
1978
1979 return SHPDUMPEROK;
1980 }
1981
1982
1983 /* Append the next row to the output shapefile */
ShpLoaderGenerateShapeRow(SHPDUMPERSTATE * state)1984 int ShpLoaderGenerateShapeRow(SHPDUMPERSTATE *state)
1985 {
1986 char *hexewkb = NULL;
1987 unsigned char *hexewkb_binary = NULL;
1988 size_t hexewkb_len;
1989 char *val;
1990 SHPObject *obj = NULL;
1991 LWGEOM *lwgeom;
1992
1993 int i, geocolnum = 0;
1994
1995 /* If we try to go pass the end of the table, fail immediately */
1996 if (state->currow > state->rowcount)
1997 {
1998 snprintf(state->message, SHPDUMPERMSGLEN, _("Tried to read past end of table!"));
1999 PQclear(state->fetchres);
2000 return SHPDUMPERERR;
2001 }
2002
2003 /* If we have reached the end of the current batch, fetch a new one */
2004 if (state->curresrow == state->currescount && state->currow < state->rowcount)
2005 {
2006 /* Clear the previous batch results */
2007 if (state->fetchres)
2008 PQclear(state->fetchres);
2009
2010 state->fetchres = PQexec(state->conn, state->fetch_query);
2011 if (PQresultStatus(state->fetchres) != PGRES_TUPLES_OK)
2012 {
2013 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing fetch query: %s"), PQresultErrorMessage(state->fetchres));
2014 PQclear(state->fetchres);
2015 return SHPDUMPERERR;
2016 }
2017
2018 state->curresrow = 0;
2019 state->currescount = PQntuples(state->fetchres);
2020 }
2021
2022 /* Grab the id of the geo column if we have one */
2023 if (state->geo_col_name)
2024 geocolnum = PQfnumber(state->fetchres, "_geoX");
2025
2026 /* Process the next record within the batch. First write out all of
2027 the non-geo fields */
2028 for (i = 0; i < state->fieldcount; i++)
2029 {
2030 /*
2031 * Transform NULL numbers to '0'
2032 * This is because the shapelib
2033 * won't easly take care of setting
2034 * nulls unless paying the acquisition
2035 * of a bug in long integer values
2036 */
2037 if (PQgetisnull(state->fetchres, state->curresrow, i))
2038 {
2039 val = nullDBFValue(state->dbffieldtypes[i]);
2040 }
2041 else
2042 {
2043 val = PQgetvalue(state->fetchres, state->curresrow, i);
2044 val = goodDBFValue(val, state->dbffieldtypes[i]);
2045 }
2046
2047 /* Write it to the DBF file */
2048 if (!DBFWriteAttributeDirectly(state->dbf, state->currow, i, val))
2049 {
2050 snprintf(state->message, SHPDUMPERMSGLEN, _("Error: record %d could not be created"), state->currow);
2051 PQclear(state->fetchres);
2052 return SHPDUMPERERR;
2053 }
2054 }
2055
2056 /* Now process the geo field, if present */
2057 if (state->geo_col_name)
2058 {
2059 /* Handle NULL shapes */
2060 if (PQgetisnull(state->fetchres, state->curresrow, geocolnum))
2061 {
2062 obj = SHPCreateSimpleObject(SHPT_NULL, 0, NULL, NULL, NULL);
2063 if (SHPWriteObject(state->shp, -1, obj) == -1)
2064 {
2065 snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing NULL shape for record %d"), state->currow);
2066 PQclear(state->fetchres);
2067 SHPDestroyObject(obj);
2068 return SHPDUMPERERR;
2069 }
2070 SHPDestroyObject(obj);
2071 }
2072 else
2073 {
2074 /* Get the value from the result set */
2075 val = PQgetvalue(state->fetchres, state->curresrow, geocolnum);
2076
2077 if (!state->config->binary)
2078 {
2079 if (state->pgis_major_version > 0)
2080 {
2081 LWDEBUG(4, "PostGIS >= 1.0, non-binary cursor");
2082
2083 /* Input is bytea encoded text field, so it must be unescaped and
2084 then converted to hexewkb string */
2085 hexewkb_binary = PQunescapeBytea((unsigned char *)val, &hexewkb_len);
2086 hexewkb = convert_bytes_to_hex(hexewkb_binary, hexewkb_len);
2087 }
2088 else
2089 {
2090 LWDEBUG(4, "PostGIS < 1.0, non-binary cursor");
2091
2092 /* Input is already hexewkb string, so we can just
2093 copy directly to hexewkb */
2094 hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2095 hexewkb = malloc(hexewkb_len + 1);
2096 strncpy(hexewkb, val, hexewkb_len + 1);
2097 }
2098 }
2099 else /* binary */
2100 {
2101 LWDEBUG(4, "PostGIS (any version) using binary cursor");
2102
2103 /* Input is binary field - must convert to hexewkb string */
2104 hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2105 hexewkb = convert_bytes_to_hex((unsigned char *)val, hexewkb_len);
2106 }
2107
2108 LWDEBUGF(4, "HexEWKB - length: %d value: %s", strlen(hexewkb), hexewkb);
2109
2110 /* Deserialize the LWGEOM */
2111 lwgeom = lwgeom_from_hexwkb(hexewkb, LW_PARSER_CHECK_NONE);
2112 if (!lwgeom)
2113 {
2114 snprintf(state->message, SHPDUMPERMSGLEN, _("Error parsing HEXEWKB for record %d"), state->currow);
2115 PQclear(state->fetchres);
2116 free(hexewkb);
2117 return SHPDUMPERERR;
2118 }
2119
2120 /* Call the relevant method depending upon the geometry type */
2121 LWDEBUGF(4, "geomtype: %s\n", lwtype_name(lwgeom->type));
2122
2123 switch (lwgeom->type)
2124 {
2125 case POINTTYPE:
2126 if (lwgeom_is_empty(lwgeom))
2127 {
2128 obj = create_point_empty(state, lwgeom_as_lwpoint(lwgeom));
2129 }
2130 else
2131 {
2132 obj = create_point(state, lwgeom_as_lwpoint(lwgeom));
2133 }
2134 break;
2135
2136 case MULTIPOINTTYPE:
2137 obj = create_multipoint(state, lwgeom_as_lwmpoint(lwgeom));
2138 break;
2139
2140 case POLYGONTYPE:
2141 obj = create_polygon(state, lwgeom_as_lwpoly(lwgeom));
2142 break;
2143
2144 case MULTIPOLYGONTYPE:
2145 obj = create_multipolygon(state, lwgeom_as_lwmpoly(lwgeom));
2146 break;
2147
2148 case LINETYPE:
2149 obj = create_linestring(state, lwgeom_as_lwline(lwgeom));
2150 break;
2151
2152 case MULTILINETYPE:
2153 obj = create_multilinestring(state, lwgeom_as_lwmline(lwgeom));
2154 break;
2155
2156 default:
2157 snprintf(state->message, SHPDUMPERMSGLEN, _("Unknown WKB type (%d) for record %d"), lwgeom->type, state->currow);
2158 PQclear(state->fetchres);
2159 SHPDestroyObject(obj);
2160 return SHPDUMPERERR;
2161 }
2162
2163 /* Free both the original and geometries */
2164 lwgeom_free(lwgeom);
2165
2166 /* Write the shape out to the file */
2167 if (SHPWriteObject(state->shp, -1, obj) == -1)
2168 {
2169 snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing shape %d"), state->currow);
2170 PQclear(state->fetchres);
2171 SHPDestroyObject(obj);
2172 return SHPDUMPERERR;
2173 }
2174
2175 SHPDestroyObject(obj);
2176
2177 /* Free the hexewkb (and temporary bytea unescaped string if used) */
2178 if (hexewkb) free(hexewkb);
2179 if (hexewkb_binary) PQfreemem(hexewkb_binary);
2180 }
2181 }
2182
2183 /* Increment ready for next time */
2184 state->curresrow++;
2185 state->currow++;
2186
2187 return SHPDUMPEROK;
2188 }
2189
2190
2191 /* Return a count of the number of rows in the table being dumped */
2192 int
ShpDumperGetRecordCount(SHPDUMPERSTATE * state)2193 ShpDumperGetRecordCount(SHPDUMPERSTATE *state)
2194 {
2195 return state->rowcount;
2196 }
2197
2198
2199 /* Close the specified table and flush all files to disk */
2200 int
ShpDumperCloseTable(SHPDUMPERSTATE * state)2201 ShpDumperCloseTable(SHPDUMPERSTATE *state)
2202 {
2203 int ret = SHPDUMPEROK;
2204
2205 /* Clear the current batch fetch resource */
2206 PQclear(state->fetchres);
2207
2208 /* If a geo column is present, generate the projection file */
2209 if (state->geo_col_name)
2210 ret = projFileCreate(state);
2211
2212 /* Close the DBF and SHP files */
2213 if (state->dbf)
2214 DBFClose(state->dbf);
2215 if (state->shp)
2216 SHPClose(state->shp);
2217
2218 return ret;
2219 }
2220
2221
2222 void
ShpDumperDestroy(SHPDUMPERSTATE * state)2223 ShpDumperDestroy(SHPDUMPERSTATE *state)
2224 {
2225 /* Destroy a state object created with ShpDumperConnect */
2226 int i;
2227
2228 if (state != NULL)
2229 {
2230 /* Disconnect from the database */
2231 if (state->conn)
2232 PQfinish(state->conn);
2233
2234 /* Free the query strings */
2235 if (state->fetch_query)
2236 free(state->fetch_query);
2237 if (state->main_scan_query)
2238 free(state->main_scan_query);
2239
2240 /* Free the DBF information fields */
2241 if (state->dbffieldnames)
2242 {
2243 for (i = 0; i < state->fieldcount; i++)
2244 free(state->dbffieldnames[i]);
2245 free(state->dbffieldnames);
2246 }
2247
2248 if (state->dbffieldtypes)
2249 free(state->dbffieldtypes);
2250
2251 if (state->pgfieldnames)
2252 free(state->pgfieldnames);
2253
2254 /* Free any column map fieldnames if specified */
2255 colmap_clean(&state->column_map);
2256
2257 /* Free other names */
2258 if (state->table)
2259 free(state->table);
2260 if (state->schema)
2261 free(state->schema);
2262 if (state->geo_col_name)
2263 free(state->geo_col_name);
2264
2265 /* Free the state itself */
2266 free(state);
2267 }
2268 }
2269
2270 /*
2271 * quote_identifier()
2272 * Properly double-quote a SQL identifier.
2273 * Copied from PostgreSQL pg_upgrade/util.c
2274 */
2275 char *
quote_identifier(const char * s)2276 quote_identifier(const char *s)
2277 {
2278 char *result = malloc(strlen(s) * 2 + 3);
2279 char *r = result;
2280
2281 *r++ = '"';
2282 while (*s)
2283 {
2284 if (*s == '"')
2285 *r++ = *s;
2286 *r++ = *s;
2287 s++;
2288 }
2289 *r++ = '"';
2290 *r++ = '\0';
2291
2292 return result;
2293 }
2294