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 config->quiet = 0;
1148 }
1149
1150 /* Create a new shapefile state object */
1151 SHPDUMPERSTATE *
ShpDumperCreate(SHPDUMPERCONFIG * config)1152 ShpDumperCreate(SHPDUMPERCONFIG *config)
1153 {
1154 SHPDUMPERSTATE *state;
1155
1156 /* Create a new state object and assign the config to it */
1157 state = malloc(sizeof(SHPDUMPERSTATE));
1158 state->config = config;
1159
1160 /* Set any state defaults */
1161 state->conn = NULL;
1162 state->outtype = 's';
1163 state->outshptype = 0;
1164 state->geom_oid = 0;
1165 state->geog_oid = 0;
1166 state->schema = NULL;
1167 state->table = NULL;
1168 state->geo_col_name = NULL;
1169 state->fetch_query = NULL;
1170 state->main_scan_query = NULL;
1171 state->dbffieldnames = NULL;
1172 state->dbffieldtypes = NULL;
1173 state->pgfieldnames = NULL;
1174 state->message[0] = '\0';
1175 colmap_init(&state->column_map);
1176
1177 return state;
1178 }
1179
1180 /* Generate the database connection string used by a state */
1181 char *
ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG * conn)1182 ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
1183 {
1184 char *connstring;
1185 int connlen;
1186
1187 connlen = 64 +
1188 (conn->host ? strlen(conn->host) : 0) + (conn->port ? strlen(conn->port) : 0) +
1189 (conn->username ? strlen(conn->username) : 0) + (conn->password ? strlen(conn->password) : 0) +
1190 (conn->database ? strlen(conn->database) : 0);
1191
1192 connstring = malloc(connlen);
1193 memset(connstring, 0, connlen);
1194
1195 if (conn->host)
1196 {
1197 strcat(connstring, " host=");
1198 strcat(connstring, conn->host);
1199 }
1200
1201 if (conn->port)
1202 {
1203 strcat(connstring, " port=");
1204 strcat(connstring, conn->port);
1205 }
1206
1207 if (conn->username)
1208 {
1209 strcat(connstring, " user=");
1210 strcat(connstring, conn->username);
1211 }
1212
1213 if (conn->password)
1214 {
1215 strcat(connstring, " password='");
1216 strcat(connstring, conn->password);
1217 strcat(connstring, "'");
1218 }
1219
1220 if (conn->database)
1221 {
1222 strcat(connstring, " dbname=");
1223 strcat(connstring, conn->database);
1224 }
1225
1226 if ( ! getenv("PGCLIENTENCODING") )
1227 {
1228 strcat(connstring, " client_encoding=UTF8");
1229 }
1230
1231 return connstring;
1232 }
1233
1234 /* Connect to the database and identify the version of PostGIS (and any other
1235 capabilities required) */
1236 int
ShpDumperConnectDatabase(SHPDUMPERSTATE * state)1237 ShpDumperConnectDatabase(SHPDUMPERSTATE *state)
1238 {
1239 PGresult *res;
1240
1241 char *connstring, *tmpvalue;
1242
1243 /* Generate the PostgreSQL connection string */
1244 connstring = ShpDumperGetConnectionStringFromConn(state->config->conn);
1245
1246 /* Connect to the database */
1247 state->conn = PQconnectdb(connstring);
1248 if (PQstatus(state->conn) == CONNECTION_BAD)
1249 {
1250 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQerrorMessage(state->conn));
1251 free(connstring);
1252 return SHPDUMPERERR;
1253 }
1254
1255 /* Set datestyle to ISO */
1256 res = PQexec(state->conn, "SET DATESTYLE='ISO'");
1257 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1258 {
1259 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1260 PQclear(res);
1261 free(connstring);
1262 return SHPDUMPERERR;
1263 }
1264
1265 PQclear(res);
1266
1267 /* Retrieve PostGIS major version */
1268 res = PQexec(state->conn, "SELECT postgis_version()");
1269 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1270 {
1271 snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1272 PQclear(res);
1273 free(connstring);
1274 return SHPDUMPERERR;
1275 }
1276
1277 tmpvalue = PQgetvalue(res, 0, 0);
1278 state->pgis_major_version = atoi(tmpvalue);
1279
1280 PQclear(res);
1281
1282 /* Find the OID for the geometry type */
1283 res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1284 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1285 {
1286 snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geometry oid: %s"), PQresultErrorMessage(res));
1287 PQclear(res);
1288 free(connstring);
1289 return SHPDUMPERERR;
1290 }
1291
1292 if (PQntuples(res) > 0)
1293 {
1294 tmpvalue = PQgetvalue(res, 0, 0);
1295 state->geom_oid = atoi(tmpvalue);
1296 }
1297 else
1298 {
1299 snprintf(state->message, SHPDUMPERMSGLEN, _("Geometry type unknown (have you enabled postgis?)"));
1300 PQclear(res);
1301 free(connstring);
1302 return SHPDUMPERERR;
1303 }
1304
1305 PQclear(res);
1306
1307 /* Find the OID for the geography type */
1308 res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geography'");
1309 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1310 {
1311 snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geography oid: %s"), PQresultErrorMessage(res));
1312 PQclear(res);
1313 free(connstring);
1314 return SHPDUMPERERR;
1315 }
1316
1317 if (PQntuples(res) > 0)
1318 {
1319 /* Old databases don't have a geography type, so don't fail if we don't find it */
1320 tmpvalue = PQgetvalue(res, 0, 0);
1321 state->geog_oid = atoi(tmpvalue);
1322 }
1323
1324 PQclear(res);
1325
1326 free(connstring);
1327
1328 return SHPDUMPEROK;
1329 }
1330
1331
1332 /* Open the specified table in preparation for extracting rows */
1333 int
ShpDumperOpenTable(SHPDUMPERSTATE * state)1334 ShpDumperOpenTable(SHPDUMPERSTATE *state)
1335 {
1336 PGresult *res;
1337
1338 char buf[256];
1339 char *query;
1340 int gidfound = 0, i, j, ret, status;
1341
1342
1343 /* Open the column map if one was specified */
1344 if (state->config->column_map_filename)
1345 {
1346 ret = colmap_read(state->config->column_map_filename,
1347 &state->column_map, state->message, SHPDUMPERMSGLEN);
1348 if (!ret) return SHPDUMPERERR;
1349 }
1350
1351 /* If a user-defined query has been specified, create and point the state to our new table */
1352 if (state->config->usrquery)
1353 {
1354 state->table = malloc(20 + 20); /* string + max long precision */
1355 sprintf(state->table, "__pgsql2shp%lu_tmp_table", (long)getpid());
1356
1357 query = malloc(32 + strlen(state->table) + strlen(state->config->usrquery));
1358
1359 sprintf(query, "CREATE TEMP TABLE \"%s\" AS %s", state->table, state->config->usrquery);
1360 res = PQexec(state->conn, query);
1361 free(query);
1362
1363 /* Execute the code to create the table */
1364 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1365 {
1366 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing user query: %s"), PQresultErrorMessage(res));
1367 PQclear(res);
1368 return SHPDUMPERERR;
1369 }
1370 }
1371 else
1372 {
1373 /* Simply point the state to copies of the supplied schema and table */
1374 state->table = strdup(state->config->table);
1375 if (state->config->schema)
1376 state->schema = strdup(state->config->schema);
1377 }
1378
1379
1380 /* Get the list of columns and their types for the selected table */
1381 if (state->schema)
1382 {
1383 query = malloc(250 + strlen(state->schema) + strlen(state->table));
1384
1385 sprintf(query, "SELECT a.attname, a.atttypid, "
1386 "a.atttypmod, a.attlen FROM "
1387 "pg_attribute a, pg_class c, pg_namespace n WHERE "
1388 "n.nspname = '%s' AND a.attrelid = c.oid AND "
1389 "n.oid = c.relnamespace AND "
1390 "a.atttypid != 0 AND "
1391 "a.attnum > 0 AND c.relname = '%s'", state->schema, state->table);
1392 }
1393 else
1394 {
1395 query = malloc(250 + strlen(state->table));
1396
1397 sprintf(query, "SELECT a.attname, a.atttypid, "
1398 "a.atttypmod, a.attlen FROM "
1399 "pg_attribute a, pg_class c WHERE "
1400 "a.attrelid = c.oid and a.attnum > 0 AND "
1401 "a.atttypid != 0 AND "
1402 "c.relname = '%s' AND "
1403 "pg_catalog.pg_table_is_visible(c.oid)", state->table);
1404 }
1405
1406 LWDEBUGF(3, "query is: %s\n", query);
1407
1408 res = PQexec(state->conn, query);
1409 free(query);
1410
1411 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1412 {
1413 snprintf(state->message, SHPDUMPERMSGLEN, _("Error querying for attributes: %s"), PQresultErrorMessage(res));
1414 PQclear(res);
1415 return SHPDUMPERERR;
1416 }
1417
1418 if (!PQntuples(res))
1419 {
1420 snprintf(state->message, SHPDUMPERMSGLEN, _("Table %s does not exist"), state->table);
1421 PQclear(res);
1422 return SHPDUMPERERR;
1423 }
1424
1425 /* If a shapefile name was specified, use it. Otherwise simply use the table name. */
1426 if (state->config->shp_file != NULL)
1427 state->shp_file = state->config->shp_file;
1428 else
1429 state->shp_file = state->table;
1430
1431 /* Create the dbf file: */
1432 /* If there's a user-specified encoding hanging around, try and use that. */
1433 /* Otherwise, just use UTF-8 encoding, since that's usually our client encoding. */
1434 if ( getenv("PGCLIENTENCODING") )
1435 {
1436 char *codepage = encoding2codepage(getenv("PGCLIENTENCODING"));
1437 state->dbf = DBFCreateEx(state->shp_file, codepage);
1438 }
1439 else
1440 {
1441 state->dbf = DBFCreateEx(state->shp_file, "UTF-8");
1442 }
1443
1444 if (!state->dbf)
1445 {
1446 snprintf(state->message, SHPDUMPERMSGLEN, _("Could not create dbf file %s"), state->shp_file);
1447 return SHPDUMPERERR;
1448 }
1449
1450 /* Mimic old behaviour and skip the EOF character (1A) */
1451 DBFSetWriteEndOfFileChar(state->dbf, 0);
1452
1453 /*
1454 * Scan the result setting fields to be returned in mainscan
1455 * query, filling the type_ary, and creating .dbf and .shp files.
1456 */
1457 state->dbffieldnames = malloc(sizeof(char *) * PQntuples(res));
1458 state->dbffieldtypes = malloc(sizeof(int) * PQntuples(res));
1459 state->pgfieldnames = malloc(sizeof(char *) * PQntuples(res));
1460 state->pgfieldlens = malloc(sizeof(int) * PQntuples(res));
1461 state->pgfieldtypmods = malloc(sizeof(int) * PQntuples(res));
1462 state->fieldcount = 0;
1463 int tmpint = 1;
1464
1465 for (i = 0; i < PQntuples(res); i++)
1466 {
1467 char *ptr;
1468
1469 int pgfieldtype, pgtypmod, pgfieldlen;
1470 char *pgfieldname;
1471
1472 int dbffieldtype, dbffieldsize, dbffielddecs;
1473 char *dbffieldname;
1474
1475 pgfieldname = PQgetvalue(res, i, 0);
1476 pgfieldtype = atoi(PQgetvalue(res, i, 1));
1477 pgtypmod = atoi(PQgetvalue(res, i, 2));
1478 pgfieldlen = atoi(PQgetvalue(res, i, 3));
1479 dbffieldtype = -1;
1480 dbffieldsize = 0;
1481 dbffielddecs = 0;
1482
1483 /*
1484 * This is a geometry/geography column
1485 */
1486 if (pgfieldtype == state->geom_oid || pgfieldtype == state->geog_oid)
1487 {
1488 /* If no geometry/geography column has been found yet... */
1489 if (!state->geo_col_name)
1490 {
1491 /* If either no geo* column name was provided (in which case this is
1492 the first match) or we match the provided column name, we have
1493 found our geo* column */
1494 if (!state->config->geo_col_name || !strcmp(state->config->geo_col_name, pgfieldname))
1495 {
1496 dbffieldtype = 9;
1497
1498 state->geo_col_name = strdup(pgfieldname);
1499 }
1500 }
1501 }
1502
1503 /*
1504 * Everything else (non geometries) will be
1505 * a DBF attribute.
1506 */
1507
1508 /* Skip gid (if not asked to do otherwise */
1509 if (!strcmp(pgfieldname, "gid") )
1510 {
1511 gidfound = 1;
1512
1513 if (!state->config->includegid)
1514 continue;
1515 }
1516
1517 /* Unescape any reserved column names */
1518 ptr = pgfieldname;
1519 if (!state->config->unescapedattrs)
1520 {
1521 if (*ptr == '_')
1522 ptr += 2;
1523 }
1524
1525 /*
1526 * This needs special handling since both xmin and _xmin
1527 * becomes __xmin when escaped
1528 */
1529
1530 /* Limit dbf field name to 10-digits */
1531 dbffieldname = malloc(11);
1532 strncpy(dbffieldname, ptr, 10);
1533 dbffieldname[10] = '\0';
1534
1535 /* If a column map file has been passed in,
1536 * use this to create the dbf field name from
1537 * the PostgreSQL column name */
1538 {
1539 const char *mapped = colmap_dbf_by_pg(&state->column_map, pgfieldname);
1540 if (mapped)
1541 {
1542 strncpy(dbffieldname, mapped, 10);
1543 dbffieldname[10] = '\0';
1544 }
1545 }
1546
1547 /*
1548 * make sure the fields all have unique names,
1549 */
1550 tmpint = 1;
1551 for (j = 0; j < state->fieldcount; j++)
1552 {
1553 if (!strncasecmp(dbffieldname, state->dbffieldnames[j], 10))
1554 {
1555 sprintf(dbffieldname, "%.7s_%.2d", ptr, abs(tmpint) % 100);
1556 tmpint++;
1557 continue;
1558 }
1559 }
1560
1561 /* make UPPERCASE if keep_fieldname_case = 0 */
1562 if (!state->config->keep_fieldname_case)
1563 {
1564 size_t nameit;
1565 for (nameit = 0; nameit < strlen(dbffieldname); nameit++)
1566 dbffieldname[nameit] = toupper(dbffieldname[nameit]);
1567 }
1568
1569 /* Issue warning if column has been renamed */
1570 if (strcasecmp(dbffieldname, pgfieldname))
1571 {
1572 if ( snprintf(buf, 256, _("Warning, field %s renamed to %s\n"),
1573 pgfieldname, dbffieldname) >= 256 )
1574 {
1575 buf[255] = '\0';
1576 }
1577 /* Note: we concatenate all warnings from the main loop as this is useful information */
1578 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message) - 1);
1579
1580 ret = SHPDUMPERWARN;
1581 }
1582
1583
1584 /*
1585 * Find appropriate type of dbf attributes
1586 */
1587
1588 /* int2 type */
1589 if (pgfieldtype == 21)
1590 {
1591 /*
1592 * Longest text representation for
1593 * an int2 type (16bit) is 6 bytes
1594 * (-32768)
1595 */
1596 dbffieldtype = FTInteger;
1597 dbffieldsize = 6;
1598 dbffielddecs = 0;
1599 }
1600
1601 /* int4 type */
1602 else if (pgfieldtype == 23)
1603 {
1604 /*
1605 * Longest text representation for
1606 * an int4 type (32bit) is 11 bytes
1607 * (-2147483648)
1608 */
1609 dbffieldtype = FTInteger;
1610 dbffieldsize = 11;
1611 dbffielddecs = 0;
1612 }
1613
1614 /* int8 type */
1615 else if (pgfieldtype == 20)
1616 {
1617 /*
1618 * Longest text representation for
1619 * an int8 type (64bit) is 20 bytes
1620 * (-9223372036854775808)
1621 */
1622 dbffieldtype = FTInteger;
1623 dbffieldsize = 19;
1624 dbffielddecs = 0;
1625 }
1626
1627 /*
1628 * double or numeric types:
1629 * 700: float4
1630 * 701: float8
1631 * 1700: numeric
1632 *
1633 *
1634 * TODO: stricter handling of sizes
1635 */
1636 else if (pgfieldtype == 700 || pgfieldtype == 701 || pgfieldtype == 1700)
1637 {
1638 dbffieldtype = FTDouble;
1639 dbffieldsize = 32;
1640 dbffielddecs = 10;
1641 }
1642
1643 /*
1644 * Boolean field, we use FTLogical
1645 */
1646 else if (pgfieldtype == 16)
1647 {
1648 dbffieldtype = FTLogical;
1649 dbffieldsize = 1;
1650 dbffielddecs = 0;
1651 }
1652
1653 /*
1654 * Date field
1655 */
1656 else if (pgfieldtype == 1082)
1657 {
1658 dbffieldtype = FTDate;
1659 dbffieldsize = 8;
1660 dbffielddecs = 0;
1661 }
1662
1663 /*
1664 * time, timetz, timestamp, or timestamptz field.
1665 */
1666 else if (pgfieldtype == 1083 || pgfieldtype == 1266 || pgfieldtype == 1114 || pgfieldtype == 1184)
1667 {
1668 int secondsize;
1669
1670 switch (pgtypmod)
1671 {
1672 case -1:
1673 secondsize = 6 + 1;
1674 break;
1675 case 0:
1676 secondsize = 0;
1677 break;
1678 default:
1679 secondsize = pgtypmod + 1;
1680 break;
1681 }
1682
1683 /* We assume the worst case scenario for all of these:
1684 * date = '5874897-12-31' = 13
1685 * date = '294276-11-20' = 12 (with --enable-interger-datetimes)
1686 * time = '00:00:00' = 8
1687 * zone = '+01:39:52' = 9 (see Europe/Helsinki around 1915)
1688 */
1689
1690 /* time */
1691 if (pgfieldtype == 1083)
1692 {
1693 dbffieldsize = 8 + secondsize;
1694 }
1695 /* timetz */
1696 else if (pgfieldtype == 1266)
1697 {
1698 dbffieldsize = 8 + secondsize + 9;
1699 }
1700 /* timestamp */
1701 else if (pgfieldtype == 1114)
1702 {
1703 dbffieldsize = 13 + 1 + 8 + secondsize;
1704 }
1705 /* timestamptz */
1706 else if (pgfieldtype == 1184)
1707 {
1708 dbffieldsize = 13 + 1 + 8 + secondsize + 9;
1709 }
1710
1711 dbffieldtype = FTString;
1712 dbffielddecs = 0;
1713 }
1714
1715 /*
1716 * uuid type 36 bytes (12345678-9012-3456-7890-123456789012)
1717 */
1718 else if (pgfieldtype == 2950)
1719 {
1720 dbffieldtype = FTString;
1721 dbffieldsize = 36;
1722 dbffielddecs = 0;
1723 }
1724
1725 /*
1726 * For variable-sized fields we know about, we use
1727 * the maximum allowed size.
1728 * 1042 is bpchar, 1043 is varchar
1729 */
1730 else if ((pgfieldtype == 1042 || pgfieldtype == 1043) && pgtypmod != -1)
1731 {
1732 /*
1733 * mod is maximum allowed size, including
1734 * header which contains *real* size.
1735 */
1736 dbffieldtype = FTString;
1737 dbffieldsize = pgtypmod - 4; /* 4 is header size */
1738 dbffielddecs = 0;
1739 }
1740
1741 /* For all other valid non-geometry/geography fields... */
1742 else if (dbffieldtype == -1)
1743 {
1744 /*
1745 * For types we don't know anything about, all
1746 * we can do is query the table for the maximum field
1747 * size.
1748 */
1749 dbffieldsize = getMaxFieldSize(state->conn, state->schema, state->table, pgfieldname);
1750 if (dbffieldsize == -1)
1751 {
1752 free(dbffieldname);
1753 return 0;
1754 }
1755
1756 if (!dbffieldsize)
1757 dbffieldsize = 32;
1758
1759 /* might 0 be a good size ? */
1760
1761 dbffieldtype = FTString;
1762 dbffielddecs = 0;
1763
1764 /* Check to make sure the final field size isn't too large */
1765 if (dbffieldsize > MAX_DBF_FIELD_SIZE)
1766 {
1767 /* Note: we concatenate all warnings from the main loop as this is useful information */
1768 snprintf(buf, 256, _("Warning: values of field '%s' exceeding maximum dbf field width (%d) "
1769 "will be truncated.\n"), dbffieldname, MAX_DBF_FIELD_SIZE);
1770 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1771 dbffieldsize = MAX_DBF_FIELD_SIZE;
1772
1773 ret = SHPDUMPERWARN;
1774 }
1775 }
1776
1777 LWDEBUGF(3, "DBF FIELD_NAME: %s, SIZE: %d\n", dbffieldname, dbffieldsize);
1778
1779 if (dbffieldtype != 9)
1780 {
1781 /* Add the field to the DBF file */
1782 if (DBFAddField(state->dbf, dbffieldname, dbffieldtype, dbffieldsize, dbffielddecs) == -1)
1783 {
1784 snprintf(state->message, SHPDUMPERMSGLEN, _("Error: field %s of type %d could not be created."), dbffieldname, dbffieldtype);
1785
1786 return SHPDUMPERERR;
1787 }
1788
1789 /* Add the field information to our field arrays */
1790 state->dbffieldnames[state->fieldcount] = dbffieldname;
1791 state->dbffieldtypes[state->fieldcount] = dbffieldtype;
1792 state->pgfieldnames[state->fieldcount] = pgfieldname;
1793 state->pgfieldlens[state->fieldcount] = pgfieldlen;
1794 state->pgfieldtypmods[state->fieldcount] = pgtypmod;
1795
1796 state->fieldcount++;
1797 }
1798 }
1799
1800 /* Now we have generated the field lists, grab some info about the table */
1801 status = getTableInfo(state);
1802 if (status == SHPDUMPERERR)
1803 return SHPDUMPERERR;
1804
1805 LWDEBUGF(3, "rows: %d\n", state->rowcount);
1806 LWDEBUGF(3, "shptype: %c\n", state->outtype);
1807 LWDEBUGF(3, "shpouttype: %d\n", state->outshptype);
1808
1809 /* If we didn't find a geometry/geography column... */
1810 if (!state->geo_col_name)
1811 {
1812 if (state->config->geo_col_name)
1813 {
1814 /* A geo* column was specified, but not found */
1815 snprintf(state->message, SHPDUMPERMSGLEN, _("%s: no such attribute in table %s"), state->config->geo_col_name, state->table);
1816
1817 return SHPDUMPERERR;
1818 }
1819 else
1820 {
1821 /* No geo* column specified so we can only create the DBF section -
1822 but let's issue a warning... */
1823 snprintf(buf, 256, _("No geometry column found.\nThe DBF file will be created but not the shx or shp files.\n"));
1824 strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1825
1826 state->shp = NULL;
1827
1828 ret = SHPDUMPERWARN;
1829 }
1830 }
1831 else
1832 {
1833 /* Since we have found a geo* column, open the shapefile */
1834 state->shp = SHPCreate(state->shp_file, state->outshptype);
1835 if (!state->shp)
1836 {
1837 snprintf(state->message, SHPDUMPERMSGLEN, _("Could not open shapefile %s!"), state->shp_file);
1838
1839 return SHPDUMPERERR;
1840 }
1841 }
1842
1843
1844 /* Now we have the complete list of fieldnames, let's generate the SQL query. First let's make sure
1845 we reserve enough space for tables with lots of columns */
1846 j = 0;
1847 /*TODO: this really should be rewritten to use stringbuffer */
1848 for (i = 0; i < state->fieldcount; i++)
1849 j += strlen( state->pgfieldnames[i]) + 10; /*add extra space for the quotes to quote identify and any embedded quotes that may need escaping */
1850
1851 state->main_scan_query = malloc(1024 + j);
1852
1853 sprintf(state->main_scan_query, "DECLARE cur ");
1854 if (state->config->binary)
1855 strcat(state->main_scan_query, "BINARY ");
1856
1857 strcat(state->main_scan_query, "CURSOR FOR SELECT ");
1858
1859 for (i = 0; i < state->fieldcount; i++)
1860 {
1861 /* Comma-separated column names */
1862 if (i > 0)
1863 strcat(state->main_scan_query, ",");
1864
1865 if (state->config->binary)
1866 sprintf(buf, "%s::text", quote_identifier(state->pgfieldnames[i]) ) ;
1867 else
1868 sprintf(buf, "%s", quote_identifier(state->pgfieldnames[i]) );
1869
1870 strcat(state->main_scan_query, buf);
1871 }
1872
1873 /* If we found a valid geometry/geography column then use it */
1874 if (state->geo_col_name)
1875 {
1876 /* If this is the (only) column, no need for the initial comma */
1877 if (state->fieldcount > 0)
1878 strcat(state->main_scan_query, ",");
1879
1880 #ifdef WORDS_BIGENDIAN
1881 if (state->pgis_major_version > 0)
1882 {
1883 sprintf(buf, "ST_asEWKB(ST_SetSRID(%s::geometry, 0), 'XDR') AS _geoX", quote_identifier(state->geo_col_name) );
1884 }
1885 else
1886 {
1887 sprintf(buf, "asbinary(%s::geometry, 'XDR') AS _geoX",
1888 quote_identifier(state->geo_col_name) );
1889 }
1890 #else
1891 if (state->pgis_major_version > 0)
1892 {
1893 sprintf(buf, "ST_AsEWKB(ST_SetSRID(%s::geometry, 0), 'NDR') AS _geoX", quote_identifier(state->geo_col_name) ) ;
1894 }
1895 else
1896 {
1897 sprintf(buf, "asbinary(%s::geometry, 'NDR') AS _geoX",
1898 quote_identifier(state->geo_col_name) );
1899 }
1900 #endif
1901
1902 strcat(state->main_scan_query, buf);
1903 }
1904
1905 if (state->schema)
1906 {
1907 sprintf(buf, " FROM \"%s\".\"%s\"", state->schema, state->table);
1908 }
1909 else
1910 {
1911 sprintf(buf, " FROM \"%s\"", state->table);
1912 }
1913
1914 strcat(state->main_scan_query, buf);
1915
1916 /* Order by 'gid' (if found) */
1917 if (gidfound)
1918 {
1919 sprintf(buf, " ORDER BY \"gid\"");
1920 strcat(state->main_scan_query, buf);
1921 }
1922
1923 /* Now we've finished with the result set, we can dispose of it */
1924 PQclear(res);
1925
1926 LWDEBUGF(3, "FINAL QUERY: %s\n", state->main_scan_query);
1927
1928 /*
1929 * Begin the transaction
1930 * (a cursor can only be defined inside a transaction block)
1931 */
1932 res = PQexec(state->conn, "BEGIN");
1933 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1934 {
1935 snprintf(state->message, SHPDUMPERMSGLEN, _("Error starting transaction: %s"), PQresultErrorMessage(res));
1936 PQclear(res);
1937 return SHPDUMPERERR;
1938 }
1939
1940 PQclear(res);
1941
1942 /* Execute the main scan query */
1943 res = PQexec(state->conn, state->main_scan_query);
1944 if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1945 {
1946 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing main scan query: %s"), PQresultErrorMessage(res));
1947 PQclear(res);
1948 return SHPDUMPERERR;
1949 }
1950
1951 PQclear(res);
1952
1953 /* Setup initial scan state */
1954 state->currow = 0;
1955 state->curresrow = 0;
1956 state->currescount = 0;
1957 state->fetchres = NULL;
1958
1959 /* Generate the fetch query */
1960 state->fetch_query = malloc(256);
1961 sprintf(state->fetch_query, "FETCH %d FROM cur", state->config->fetchsize);
1962
1963 return SHPDUMPEROK;
1964 }
1965
1966
1967 /* Append the next row to the output shapefile */
ShpLoaderGenerateShapeRow(SHPDUMPERSTATE * state)1968 int ShpLoaderGenerateShapeRow(SHPDUMPERSTATE *state)
1969 {
1970 char *hexewkb = NULL;
1971 unsigned char *hexewkb_binary = NULL;
1972 size_t hexewkb_len;
1973 char *val;
1974 SHPObject *obj = NULL;
1975 LWGEOM *lwgeom;
1976
1977 int i, geocolnum = 0;
1978
1979 /* If we try to go pass the end of the table, fail immediately */
1980 if (state->currow > state->rowcount)
1981 {
1982 snprintf(state->message, SHPDUMPERMSGLEN, _("Tried to read past end of table!"));
1983 PQclear(state->fetchres);
1984 return SHPDUMPERERR;
1985 }
1986
1987 /* If we have reached the end of the current batch, fetch a new one */
1988 if (state->curresrow == state->currescount && state->currow < state->rowcount)
1989 {
1990 /* Clear the previous batch results */
1991 if (state->fetchres)
1992 PQclear(state->fetchres);
1993
1994 state->fetchres = PQexec(state->conn, state->fetch_query);
1995 if (PQresultStatus(state->fetchres) != PGRES_TUPLES_OK)
1996 {
1997 snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing fetch query: %s"), PQresultErrorMessage(state->fetchres));
1998 PQclear(state->fetchres);
1999 return SHPDUMPERERR;
2000 }
2001
2002 state->curresrow = 0;
2003 state->currescount = PQntuples(state->fetchres);
2004 }
2005
2006 /* Grab the id of the geo column if we have one */
2007 if (state->geo_col_name)
2008 geocolnum = PQfnumber(state->fetchres, "_geoX");
2009
2010 /* Process the next record within the batch. First write out all of
2011 the non-geo fields */
2012 for (i = 0; i < state->fieldcount; i++)
2013 {
2014 /*
2015 * Transform NULL numbers to '0'
2016 * This is because the shapelib
2017 * won't easly take care of setting
2018 * nulls unless paying the acquisition
2019 * of a bug in long integer values
2020 */
2021 if (PQgetisnull(state->fetchres, state->curresrow, i))
2022 {
2023 val = nullDBFValue(state->dbffieldtypes[i]);
2024 }
2025 else
2026 {
2027 val = PQgetvalue(state->fetchres, state->curresrow, i);
2028 val = goodDBFValue(val, state->dbffieldtypes[i]);
2029 }
2030
2031 /* Write it to the DBF file */
2032 if (!DBFWriteAttributeDirectly(state->dbf, state->currow, i, val))
2033 {
2034 snprintf(state->message, SHPDUMPERMSGLEN, _("Error: record %d could not be created"), state->currow);
2035 PQclear(state->fetchres);
2036 return SHPDUMPERERR;
2037 }
2038 }
2039
2040 /* Now process the geo field, if present */
2041 if (state->geo_col_name)
2042 {
2043 /* Handle NULL shapes */
2044 if (PQgetisnull(state->fetchres, state->curresrow, geocolnum))
2045 {
2046 obj = SHPCreateSimpleObject(SHPT_NULL, 0, NULL, NULL, NULL);
2047 if (SHPWriteObject(state->shp, -1, obj) == -1)
2048 {
2049 snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing NULL shape for record %d"), state->currow);
2050 PQclear(state->fetchres);
2051 SHPDestroyObject(obj);
2052 return SHPDUMPERERR;
2053 }
2054 SHPDestroyObject(obj);
2055 }
2056 else
2057 {
2058 /* Get the value from the result set */
2059 val = PQgetvalue(state->fetchres, state->curresrow, geocolnum);
2060
2061 if (!state->config->binary)
2062 {
2063 if (state->pgis_major_version > 0)
2064 {
2065 LWDEBUG(4, "PostGIS >= 1.0, non-binary cursor");
2066
2067 /* Input is bytea encoded text field, so it must be unescaped and
2068 then converted to hexewkb string */
2069 hexewkb_binary = PQunescapeBytea((unsigned char *)val, &hexewkb_len);
2070 hexewkb = convert_bytes_to_hex(hexewkb_binary, hexewkb_len);
2071 }
2072 else
2073 {
2074 LWDEBUG(4, "PostGIS < 1.0, non-binary cursor");
2075
2076 /* Input is already hexewkb string, so we can just
2077 copy directly to hexewkb */
2078 hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2079 hexewkb = malloc(hexewkb_len + 1);
2080 strncpy(hexewkb, val, hexewkb_len + 1);
2081 }
2082 }
2083 else /* binary */
2084 {
2085 LWDEBUG(4, "PostGIS (any version) using binary cursor");
2086
2087 /* Input is binary field - must convert to hexewkb string */
2088 hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2089 hexewkb = convert_bytes_to_hex((unsigned char *)val, hexewkb_len);
2090 }
2091
2092 LWDEBUGF(4, "HexEWKB - length: %d value: %s", strlen(hexewkb), hexewkb);
2093
2094 /* Deserialize the LWGEOM */
2095 lwgeom = lwgeom_from_hexwkb(hexewkb, LW_PARSER_CHECK_NONE);
2096 if (!lwgeom)
2097 {
2098 snprintf(state->message, SHPDUMPERMSGLEN, _("Error parsing HEXEWKB for record %d"), state->currow);
2099 PQclear(state->fetchres);
2100 free(hexewkb);
2101 return SHPDUMPERERR;
2102 }
2103
2104 /* Call the relevant method depending upon the geometry type */
2105 LWDEBUGF(4, "geomtype: %s\n", lwtype_name(lwgeom->type));
2106
2107 switch (lwgeom->type)
2108 {
2109 case POINTTYPE:
2110 if (lwgeom_is_empty(lwgeom))
2111 {
2112 obj = create_point_empty(state, lwgeom_as_lwpoint(lwgeom));
2113 }
2114 else
2115 {
2116 obj = create_point(state, lwgeom_as_lwpoint(lwgeom));
2117 }
2118 break;
2119
2120 case MULTIPOINTTYPE:
2121 obj = create_multipoint(state, lwgeom_as_lwmpoint(lwgeom));
2122 break;
2123
2124 case POLYGONTYPE:
2125 obj = create_polygon(state, lwgeom_as_lwpoly(lwgeom));
2126 break;
2127
2128 case MULTIPOLYGONTYPE:
2129 obj = create_multipolygon(state, lwgeom_as_lwmpoly(lwgeom));
2130 break;
2131
2132 case LINETYPE:
2133 obj = create_linestring(state, lwgeom_as_lwline(lwgeom));
2134 break;
2135
2136 case MULTILINETYPE:
2137 obj = create_multilinestring(state, lwgeom_as_lwmline(lwgeom));
2138 break;
2139
2140 default:
2141 snprintf(state->message, SHPDUMPERMSGLEN, _("Unknown WKB type (%d) for record %d"), lwgeom->type, state->currow);
2142 PQclear(state->fetchres);
2143 SHPDestroyObject(obj);
2144 return SHPDUMPERERR;
2145 }
2146
2147 /* Free both the original and geometries */
2148 lwgeom_free(lwgeom);
2149
2150 /* Write the shape out to the file */
2151 if (SHPWriteObject(state->shp, -1, obj) == -1)
2152 {
2153 snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing shape %d"), state->currow);
2154 PQclear(state->fetchres);
2155 SHPDestroyObject(obj);
2156 return SHPDUMPERERR;
2157 }
2158
2159 SHPDestroyObject(obj);
2160
2161 /* Free the hexewkb (and temporary bytea unescaped string if used) */
2162 if (hexewkb) free(hexewkb);
2163 if (hexewkb_binary) PQfreemem(hexewkb_binary);
2164 }
2165 }
2166
2167 /* Increment ready for next time */
2168 state->curresrow++;
2169 state->currow++;
2170
2171 return SHPDUMPEROK;
2172 }
2173
2174
2175 /* Return a count of the number of rows in the table being dumped */
2176 int
ShpDumperGetRecordCount(SHPDUMPERSTATE * state)2177 ShpDumperGetRecordCount(SHPDUMPERSTATE *state)
2178 {
2179 return state->rowcount;
2180 }
2181
2182
2183 /* Close the specified table and flush all files to disk */
2184 int
ShpDumperCloseTable(SHPDUMPERSTATE * state)2185 ShpDumperCloseTable(SHPDUMPERSTATE *state)
2186 {
2187 int ret = SHPDUMPEROK;
2188
2189 /* Clear the current batch fetch resource */
2190 PQclear(state->fetchres);
2191
2192 /* If a geo column is present, generate the projection file */
2193 if (state->geo_col_name)
2194 ret = projFileCreate(state);
2195
2196 /* Close the DBF and SHP files */
2197 if (state->dbf)
2198 DBFClose(state->dbf);
2199 if (state->shp)
2200 SHPClose(state->shp);
2201
2202 return ret;
2203 }
2204
2205
2206 void
ShpDumperDestroy(SHPDUMPERSTATE * state)2207 ShpDumperDestroy(SHPDUMPERSTATE *state)
2208 {
2209 /* Destroy a state object created with ShpDumperConnect */
2210 int i;
2211
2212 if (state != NULL)
2213 {
2214 /* Disconnect from the database */
2215 if (state->conn)
2216 PQfinish(state->conn);
2217
2218 /* Free the query strings */
2219 if (state->fetch_query)
2220 free(state->fetch_query);
2221 if (state->main_scan_query)
2222 free(state->main_scan_query);
2223
2224 /* Free the DBF information fields */
2225 if (state->dbffieldnames)
2226 {
2227 for (i = 0; i < state->fieldcount; i++)
2228 free(state->dbffieldnames[i]);
2229 free(state->dbffieldnames);
2230 }
2231
2232 if (state->dbffieldtypes)
2233 free(state->dbffieldtypes);
2234
2235 if (state->pgfieldnames)
2236 free(state->pgfieldnames);
2237
2238 /* Free any column map fieldnames if specified */
2239 colmap_clean(&state->column_map);
2240
2241 /* Free other names */
2242 if (state->table)
2243 free(state->table);
2244 if (state->schema)
2245 free(state->schema);
2246 if (state->geo_col_name)
2247 free(state->geo_col_name);
2248
2249 /* Free the state itself */
2250 free(state);
2251 }
2252 }
2253
2254 /*
2255 * quote_identifier()
2256 * Properly double-quote a SQL identifier.
2257 * Copied from PostgreSQL pg_upgrade/util.c
2258 */
2259 char *
quote_identifier(const char * s)2260 quote_identifier(const char *s)
2261 {
2262 char *result = malloc(strlen(s) * 2 + 3);
2263 char *r = result;
2264
2265 *r++ = '"';
2266 while (*s)
2267 {
2268 if (*s == '"')
2269 *r++ = *s;
2270 *r++ = *s;
2271 s++;
2272 }
2273 *r++ = '"';
2274 *r++ = '\0';
2275
2276 return result;
2277 }
2278