PostGIS  3.7.0dev-r@@SVN_REVISION@@
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 */
50 static int reverse_points(int num_points, double *x, double *y, double *z, double *m);
51 static int is_clockwise(int num_points,double *x,double *y,double *z);
52 static SHPObject *create_point(SHPDUMPERSTATE *state, LWPOINT *lwpoint);
53 static SHPObject *create_multipoint(SHPDUMPERSTATE *state, LWMPOINT *lwmultipoint);
54 static SHPObject *create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon);
55 static SHPObject *create_multipolygon(SHPDUMPERSTATE *state, LWMPOLY *lwmultipolygon);
56 static SHPObject *create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring);
57 static SHPObject *create_multilinestring(SHPDUMPERSTATE *state, LWMLINE *lwmultilinestring);
58 static char *nullDBFValue(char fieldType);
59 static int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname);
60 static int getTableInfo(SHPDUMPERSTATE *state);
61 static int projFileCreate(SHPDUMPERSTATE *state);
62 
68 static char * goodDBFValue(char *in, char fieldType);
69 
71 char *convert_bytes_to_hex(uint8_t *ewkb, size_t size);
72 
73 static char*
74 core_asprintf(const char* format, ...) __attribute__ ((format (printf, 1, 2)));
75 
76 static char*
77 core_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 
90 static 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 
102 static 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 
137 static 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 
176 static 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 
264 static 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 
368 static 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 
407 static SHPObject *
408 create_multilinestring(SHPDUMPERSTATE *state, LWMLINE *lwmultilinestring)
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... */
469 static int
470 reverse_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 */
510 static int
511 is_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  */
554 static int
555 getMaxFieldSize(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 
601 char *
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 */
641 static char *
642 nullDBFValue(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 
670 static char *
671 goodDBFValue(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 
703 char *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 
722 static 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 
865 static 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':
1070  state->outshptype = SHPT_MULTIPOINTZ;
1071  break;
1072 
1073  case 'm':
1074  state->outshptype = SHPT_MULTIPOINTM;
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 */
1131 void
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 */
1187 char *
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
1215 capabilities required) */
1216 int
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 */
1312 int
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  {
1325  ret = colmap_read(state->config->column_map_filename,
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));
1339  stringbuffer_release(&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));
1389  stringbuffer_release(&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 */
1937  stringbuffer_release(&sb);
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 */
2170 int
2172 {
2173  return state->rowcount;
2174 }
2175 
2176 
2177 /* Close the specified table and flush all files to disk */
2178 int
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 
2200 void
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  */
2247 char *
2248 quote_identifier(const char *s)
2249 {
2250  char *result = malloc(strlen(s) * 2 + 3);
2251  char *r = result;
2252 
2253  *r++ = '"';
2254  while (*s)
2255  {
2256  if (*s == '"')
2257  *r++ = *s;
2258  *r++ = *s;
2259  s++;
2260  }
2261  *r++ = '"';
2262  *r++ = '\0';
2263 
2264  return result;
2265 }
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:788
DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename, const char *pszCodePage)
Definition: dbfopen.c:662
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
Definition: dbfopen.c:599
void SHPAPI_CALL DBFSetWriteEndOfFileChar(DBFHandle psDBF, int bWriteFlag)
Definition: dbfopen.c:2297
int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, void *pValue)
Definition: dbfopen.c:1437
void deparse_hex(uint8_t str, char *result)
Convert a char into a human readable hex digit.
Definition: lwgeom_api.c:614
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:179
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition: lwgeom_api.c:107
LWGEOM * lwgeom_from_hexwkb(const char *hexwkb, const char check)
Definition: lwin_wkb.c:866
LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:242
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1218
#define LW_PARSER_CHECK_NONE
Definition: liblwgeom.h:2146
#define MULTILINETYPE
Definition: liblwgeom.h:106
#define LINETYPE
Definition: liblwgeom.h:103
#define MULTIPOINTTYPE
Definition: liblwgeom.h:105
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:260
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1168
#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
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition: lwgeom.c:251
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:107
#define POLYGONTYPE
Definition: liblwgeom.h:104
#define __attribute__(x)
Definition: liblwgeom.h:228
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:215
#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 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
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:127
int value
Definition: genraster.py:62
type
Definition: ovdump.py:42
tuple res
Definition: window.py:79
void set_dumper_config_defaults(SHPDUMPERCONFIG *config)
static int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname)
static SHPObject * create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon)
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.
char * shapetypename(int num)
SHPDUMPERSTATE * ShpDumperCreate(SHPDUMPERCONFIG *config)
static SHPObject * create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring)
static int getTableInfo(SHPDUMPERSTATE *state)
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...
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)
int ShpDumperCloseTable(SHPDUMPERSTATE *state)
static int is_clockwise(int num_points, double *x, double *y, double *z)
char * quote_identifier(const char *s)
int ShpDumperOpenTable(SHPDUMPERSTATE *state)
static SHPObject * create_point_empty(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
static char * nullDBFValue(char fieldType)
char * ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
#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:979
@ 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:879
#define SHPT_POLYGON
Definition: shapefil.h:351
void SHPAPI_CALL SHPDestroyObject(SHPObject *psObject)
Definition: shpopen.c:2641
#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
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
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:1334
void stringbuffer_release(stringbuffer_t *s)
Definition: stringbuffer.c:48
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.
Definition: stringbuffer.c:254
void stringbuffer_init(stringbuffer_t *s)
Definition: stringbuffer.c:54
char * stringbuffer_getstringcopy(stringbuffer_t *s)
Returns a newly allocated string large enough to contain the current state of the string.
Definition: stringbuffer.c:136
const char * stringbuffer_getstring(stringbuffer_t *s)
Returns a reference to the internal string being managed by the stringbuffer.
Definition: stringbuffer.c:125
static void stringbuffer_append(stringbuffer_t *s, const char *a)
Append the specified string to the stringbuffer_t.
Definition: stringbuffer.h:105
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
char * column_map_filename
SHPCONNECTIONCONFIG * conn
SHPDUMPERCONFIG * config
PGresult * fetchres
char message[SHPDUMPERMSGLEN]
char * str_start
Definition: stringbuffer.h:43