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