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