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