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