PostGIS  2.3.8dev-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  int 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  int 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  int 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  int 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  int 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  int 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++);
1553  continue;
1554  }
1555  }
1556 
1557  /* make UPPERCASE if keep_fieldname_case = 0 */
1558  if (!state->config->keep_fieldname_case)
1559  for (j = 0; j < strlen(dbffieldname); j++)
1560  dbffieldname[j] = toupper(dbffieldname[j]);
1561 
1562  /* Issue warning if column has been renamed */
1563  if (strcasecmp(dbffieldname, pgfieldname))
1564  {
1565  if ( snprintf(buf, 256, _("Warning, field %s renamed to %s\n"),
1566  pgfieldname, dbffieldname) >= 256 )
1567  {
1568  buf[255] = '\0';
1569  }
1570  /* Note: we concatenate all warnings from the main loop as this is useful information */
1571  strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message) - 1);
1572 
1573  ret = SHPDUMPERWARN;
1574  }
1575 
1576 
1577  /*
1578  * Find appropriate type of dbf attributes
1579  */
1580 
1581  /* int2 type */
1582  if (pgfieldtype == 21)
1583  {
1584  /*
1585  * Longest text representation for
1586  * an int2 type (16bit) is 6 bytes
1587  * (-32768)
1588  */
1589  dbffieldtype = FTInteger;
1590  dbffieldsize = 6;
1591  dbffielddecs = 0;
1592  }
1593 
1594  /* int4 type */
1595  else if (pgfieldtype == 23)
1596  {
1597  /*
1598  * Longest text representation for
1599  * an int4 type (32bit) is 11 bytes
1600  * (-2147483648)
1601  */
1602  dbffieldtype = FTInteger;
1603  dbffieldsize = 11;
1604  dbffielddecs = 0;
1605  }
1606 
1607  /* int8 type */
1608  else if (pgfieldtype == 20)
1609  {
1610  /*
1611  * Longest text representation for
1612  * an int8 type (64bit) is 20 bytes
1613  * (-9223372036854775808)
1614  */
1615  dbffieldtype = FTInteger;
1616  dbffieldsize = 19;
1617  dbffielddecs = 0;
1618  }
1619 
1620  /*
1621  * double or numeric types:
1622  * 700: float4
1623  * 701: float8
1624  * 1700: numeric
1625  *
1626  *
1627  * TODO: stricter handling of sizes
1628  */
1629  else if (pgfieldtype == 700 || pgfieldtype == 701 || pgfieldtype == 1700)
1630  {
1631  dbffieldtype = FTDouble;
1632  dbffieldsize = 32;
1633  dbffielddecs = 10;
1634  }
1635 
1636  /*
1637  * Boolean field, we use FTLogical
1638  */
1639  else if (pgfieldtype == 16)
1640  {
1641  dbffieldtype = FTLogical;
1642  dbffieldsize = 1;
1643  dbffielddecs = 0;
1644  }
1645 
1646  /*
1647  * Date field
1648  */
1649  else if (pgfieldtype == 1082)
1650  {
1651  dbffieldtype = FTDate;
1652  dbffieldsize = 8;
1653  dbffielddecs = 0;
1654  }
1655 
1656  /*
1657  * time, timetz, timestamp, or timestamptz field.
1658  */
1659  else if (pgfieldtype == 1083 || pgfieldtype == 1266 || pgfieldtype == 1114 || pgfieldtype == 1184)
1660  {
1661  int secondsize;
1662 
1663  switch (pgtypmod)
1664  {
1665  case -1:
1666  secondsize = 6 + 1;
1667  break;
1668  case 0:
1669  secondsize = 0;
1670  break;
1671  default:
1672  secondsize = pgtypmod + 1;
1673  break;
1674  }
1675 
1676  /* We assume the worst case scenario for all of these:
1677  * date = '5874897-12-31' = 13
1678  * date = '294276-11-20' = 12 (with --enable-interger-datetimes)
1679  * time = '00:00:00' = 8
1680  * zone = '+01:39:52' = 9 (see Europe/Helsinki around 1915)
1681  */
1682 
1683  /* time */
1684  if (pgfieldtype == 1083)
1685  {
1686  dbffieldsize = 8 + secondsize;
1687  }
1688  /* timetz */
1689  else if (pgfieldtype == 1266)
1690  {
1691  dbffieldsize = 8 + secondsize + 9;
1692  }
1693  /* timestamp */
1694  else if (pgfieldtype == 1114)
1695  {
1696  dbffieldsize = 13 + 1 + 8 + secondsize;
1697  }
1698  /* timestamptz */
1699  else if (pgfieldtype == 1184)
1700  {
1701  dbffieldsize = 13 + 1 + 8 + secondsize + 9;
1702  }
1703 
1704  dbffieldtype = FTString;
1705  dbffielddecs = 0;
1706  }
1707 
1708  /*
1709  * uuid type 36 bytes (12345678-9012-3456-7890-123456789012)
1710  */
1711  else if (pgfieldtype == 2950)
1712  {
1713  dbffieldtype = FTString;
1714  dbffieldsize = 36;
1715  dbffielddecs = 0;
1716  }
1717 
1718  /*
1719  * For variable-sized fields we know about, we use
1720  * the maximum allowed size.
1721  * 1042 is bpchar, 1043 is varchar
1722  */
1723  else if ((pgfieldtype == 1042 || pgfieldtype == 1043) && pgtypmod != -1)
1724  {
1725  /*
1726  * mod is maximum allowed size, including
1727  * header which contains *real* size.
1728  */
1729  dbffieldtype = FTString;
1730  dbffieldsize = pgtypmod - 4; /* 4 is header size */
1731  dbffielddecs = 0;
1732  }
1733 
1734  /* For all other valid non-geometry/geography fields... */
1735  else if (dbffieldtype == -1)
1736  {
1737  /*
1738  * For types we don't know anything about, all
1739  * we can do is query the table for the maximum field
1740  * size.
1741  */
1742  dbffieldsize = getMaxFieldSize(state->conn, state->schema, state->table, pgfieldname);
1743  if (dbffieldsize == -1)
1744  {
1745  free(dbffieldname);
1746  return 0;
1747  }
1748 
1749  if (!dbffieldsize)
1750  dbffieldsize = 32;
1751 
1752  /* might 0 be a good size ? */
1753 
1754  dbffieldtype = FTString;
1755  dbffielddecs = 0;
1756 
1757  /* Check to make sure the final field size isn't too large */
1758  if (dbffieldsize > MAX_DBF_FIELD_SIZE)
1759  {
1760  /* Note: we concatenate all warnings from the main loop as this is useful information */
1761  snprintf(buf, 256, _("Warning: values of field '%s' exceeding maximum dbf field width (%d) "
1762  "will be truncated.\n"), dbffieldname, MAX_DBF_FIELD_SIZE);
1763  strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1764  dbffieldsize = MAX_DBF_FIELD_SIZE;
1765 
1766  ret = SHPDUMPERWARN;
1767  }
1768  }
1769 
1770  LWDEBUGF(3, "DBF FIELD_NAME: %s, SIZE: %d\n", dbffieldname, dbffieldsize);
1771 
1772  if (dbffieldtype != 9)
1773  {
1774  /* Add the field to the DBF file */
1775  if (DBFAddField(state->dbf, dbffieldname, dbffieldtype, dbffieldsize, dbffielddecs) == -1)
1776  {
1777  snprintf(state->message, SHPDUMPERMSGLEN, _("Error: field %s of type %d could not be created."), dbffieldname, dbffieldtype);
1778 
1779  return SHPDUMPERERR;
1780  }
1781 
1782  /* Add the field information to our field arrays */
1783  state->dbffieldnames[state->fieldcount] = dbffieldname;
1784  state->dbffieldtypes[state->fieldcount] = dbffieldtype;
1785  state->pgfieldnames[state->fieldcount] = pgfieldname;
1786  state->pgfieldlens[state->fieldcount] = pgfieldlen;
1787  state->pgfieldtypmods[state->fieldcount] = pgtypmod;
1788 
1789  state->fieldcount++;
1790  }
1791  }
1792 
1793  /* Now we have generated the field lists, grab some info about the table */
1794  status = getTableInfo(state);
1795  if (status == SHPDUMPERERR)
1796  return SHPDUMPERERR;
1797 
1798  LWDEBUGF(3, "rows: %d\n", state->rowcount);
1799  LWDEBUGF(3, "shptype: %c\n", state->outtype);
1800  LWDEBUGF(3, "shpouttype: %d\n", state->outshptype);
1801 
1802  /* If we didn't find a geometry/geography column... */
1803  if (!state->geo_col_name)
1804  {
1805  if (state->config->geo_col_name)
1806  {
1807  /* A geo* column was specified, but not found */
1808  snprintf(state->message, SHPDUMPERMSGLEN, _("%s: no such attribute in table %s"), state->config->geo_col_name, state->table);
1809 
1810  return SHPDUMPERERR;
1811  }
1812  else
1813  {
1814  /* No geo* column specified so we can only create the DBF section -
1815  but let's issue a warning... */
1816  snprintf(buf, 256, _("No geometry column found.\nThe DBF file will be created but not the shx or shp files.\n"));
1817  strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1818 
1819  state->shp = NULL;
1820 
1821  ret = SHPDUMPERWARN;
1822  }
1823  }
1824  else
1825  {
1826  /* Since we have found a geo* column, open the shapefile */
1827  state->shp = SHPCreate(state->shp_file, state->outshptype);
1828  if (!state->shp)
1829  {
1830  snprintf(state->message, SHPDUMPERMSGLEN, _("Could not open shapefile %s!"), state->shp_file);
1831 
1832  return SHPDUMPERERR;
1833  }
1834  }
1835 
1836 
1837  /* Now we have the complete list of fieldnames, let's generate the SQL query. First let's make sure
1838  we reserve enough space for tables with lots of columns */
1839  j = 0;
1840  /*TODO: this really should be rewritten to use stringbuffer */
1841  for (i = 0; i < state->fieldcount; i++)
1842  j += strlen( state->pgfieldnames[i]) + 10; /*add extra space for the quotes to quote identify and any embedded quotes that may need escaping */
1843 
1844  state->main_scan_query = malloc(1024 + j);
1845 
1846  sprintf(state->main_scan_query, "DECLARE cur ");
1847  if (state->config->binary)
1848  strcat(state->main_scan_query, "BINARY ");
1849 
1850  strcat(state->main_scan_query, "CURSOR FOR SELECT ");
1851 
1852  for (i = 0; i < state->fieldcount; i++)
1853  {
1854  /* Comma-separated column names */
1855  if (i > 0)
1856  strcat(state->main_scan_query, ",");
1857 
1858  if (state->config->binary)
1859  sprintf(buf, "%s::text", quote_identifier(state->pgfieldnames[i]) ) ;
1860  else
1861  sprintf(buf, "%s", quote_identifier(state->pgfieldnames[i]) );
1862 
1863  strcat(state->main_scan_query, buf);
1864  }
1865 
1866  /* If we found a valid geometry/geography column then use it */
1867  if (state->geo_col_name)
1868  {
1869  /* If this is the (only) column, no need for the initial comma */
1870  if (state->fieldcount > 0)
1871  strcat(state->main_scan_query, ",");
1872 
1873  if (state->big_endian)
1874  {
1875  if (state->pgis_major_version > 0)
1876  {
1877  sprintf(buf, "ST_asEWKB(ST_SetSRID(%s::geometry, 0), 'XDR') AS _geoX", quote_identifier(state->geo_col_name) );
1878  }
1879  else
1880  {
1881  sprintf(buf, "asbinary(%s::geometry, 'XDR') AS _geoX",
1882  quote_identifier(state->geo_col_name) );
1883  }
1884  }
1885  else /* little_endian */
1886  {
1887  if (state->pgis_major_version > 0)
1888  {
1889  sprintf(buf, "ST_AsEWKB(ST_SetSRID(%s::geometry, 0), 'NDR') AS _geoX", quote_identifier(state->geo_col_name) ) ;
1890  }
1891  else
1892  {
1893  sprintf(buf, "asbinary(%s::geometry, 'NDR') AS _geoX",
1894  quote_identifier(state->geo_col_name) );
1895  }
1896  }
1897 
1898  strcat(state->main_scan_query, buf);
1899  }
1900 
1901  if (state->schema)
1902  {
1903  sprintf(buf, " FROM \"%s\".\"%s\"", state->schema, state->table);
1904  }
1905  else
1906  {
1907  sprintf(buf, " FROM \"%s\"", state->table);
1908  }
1909 
1910  strcat(state->main_scan_query, buf);
1911 
1912  /* Order by 'gid' (if found) */
1913  if (gidfound)
1914  {
1915  sprintf(buf, " ORDER BY \"gid\"");
1916  strcat(state->main_scan_query, buf);
1917  }
1918 
1919  /* Now we've finished with the result set, we can dispose of it */
1920  PQclear(res);
1921 
1922  LWDEBUGF(3, "FINAL QUERY: %s\n", state->main_scan_query);
1923 
1924  /*
1925  * Begin the transaction
1926  * (a cursor can only be defined inside a transaction block)
1927  */
1928  res = PQexec(state->conn, "BEGIN");
1929  if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1930  {
1931  snprintf(state->message, SHPDUMPERMSGLEN, _("Error starting transaction: %s"), PQresultErrorMessage(res));
1932  PQclear(res);
1933  return SHPDUMPERERR;
1934  }
1935 
1936  PQclear(res);
1937 
1938  /* Execute the main scan query */
1939  res = PQexec(state->conn, state->main_scan_query);
1940  if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1941  {
1942  snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing main scan query: %s"), PQresultErrorMessage(res));
1943  PQclear(res);
1944  return SHPDUMPERERR;
1945  }
1946 
1947  PQclear(res);
1948 
1949  /* Setup initial scan state */
1950  state->currow = 0;
1951  state->curresrow = 0;
1952  state->currescount = 0;
1953  state->fetchres = NULL;
1954 
1955  /* Generate the fetch query */
1956  state->fetch_query = malloc(256);
1957  sprintf(state->fetch_query, "FETCH %d FROM cur", state->config->fetchsize);
1958 
1959  return SHPDUMPEROK;
1960 }
1961 
1962 
1963 /* Append the next row to the output shapefile */
1965 {
1966  char *hexewkb = NULL;
1967  unsigned char *hexewkb_binary = NULL;
1968  size_t hexewkb_len;
1969  char *val;
1970  SHPObject *obj = NULL;
1971  LWGEOM *lwgeom;
1972 
1973  int i, geocolnum = 0;
1974 
1975  /* If we try to go pass the end of the table, fail immediately */
1976  if (state->currow > state->rowcount)
1977  {
1978  snprintf(state->message, SHPDUMPERMSGLEN, _("Tried to read past end of table!"));
1979  PQclear(state->fetchres);
1980  return SHPDUMPERERR;
1981  }
1982 
1983  /* If we have reached the end of the current batch, fetch a new one */
1984  if (state->curresrow == state->currescount && state->currow < state->rowcount)
1985  {
1986  /* Clear the previous batch results */
1987  if (state->fetchres)
1988  PQclear(state->fetchres);
1989 
1990  state->fetchres = PQexec(state->conn, state->fetch_query);
1991  if (PQresultStatus(state->fetchres) != PGRES_TUPLES_OK)
1992  {
1993  snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing fetch query: %s"), PQresultErrorMessage(state->fetchres));
1994  PQclear(state->fetchres);
1995  return SHPDUMPERERR;
1996  }
1997 
1998  state->curresrow = 0;
1999  state->currescount = PQntuples(state->fetchres);
2000  }
2001 
2002  /* Grab the id of the geo column if we have one */
2003  if (state->geo_col_name)
2004  geocolnum = PQfnumber(state->fetchres, "_geoX");
2005 
2006  /* Process the next record within the batch. First write out all of
2007  the non-geo fields */
2008  for (i = 0; i < state->fieldcount; i++)
2009  {
2010  /*
2011  * Transform NULL numbers to '0'
2012  * This is because the shapelib
2013  * won't easly take care of setting
2014  * nulls unless paying the acquisition
2015  * of a bug in long integer values
2016  */
2017  if (PQgetisnull(state->fetchres, state->curresrow, i))
2018  {
2019  val = nullDBFValue(state->dbffieldtypes[i]);
2020  }
2021  else
2022  {
2023  val = PQgetvalue(state->fetchres, state->curresrow, i);
2024  val = goodDBFValue(val, state->dbffieldtypes[i]);
2025  }
2026 
2027  /* Write it to the DBF file */
2028  if (!DBFWriteAttributeDirectly(state->dbf, state->currow, i, val))
2029  {
2030  snprintf(state->message, SHPDUMPERMSGLEN, _("Error: record %d could not be created"), state->currow);
2031  PQclear(state->fetchres);
2032  return SHPDUMPERERR;
2033  }
2034  }
2035 
2036  /* Now process the geo field, if present */
2037  if (state->geo_col_name)
2038  {
2039  /* Handle NULL shapes */
2040  if (PQgetisnull(state->fetchres, state->curresrow, geocolnum))
2041  {
2042  obj = SHPCreateSimpleObject(SHPT_NULL, 0, NULL, NULL, NULL);
2043  if (SHPWriteObject(state->shp, -1, obj) == -1)
2044  {
2045  snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing NULL shape for record %d"), state->currow);
2046  PQclear(state->fetchres);
2047  SHPDestroyObject(obj);
2048  return SHPDUMPERERR;
2049  }
2050  SHPDestroyObject(obj);
2051  }
2052  else
2053  {
2054  /* Get the value from the result set */
2055  val = PQgetvalue(state->fetchres, state->curresrow, geocolnum);
2056 
2057  if (!state->config->binary)
2058  {
2059  if (state->pgis_major_version > 0)
2060  {
2061  LWDEBUG(4, "PostGIS >= 1.0, non-binary cursor");
2062 
2063  /* Input is bytea encoded text field, so it must be unescaped and
2064  then converted to hexewkb string */
2065  hexewkb_binary = PQunescapeBytea((unsigned char *)val, &hexewkb_len);
2066  hexewkb = convert_bytes_to_hex(hexewkb_binary, hexewkb_len);
2067  }
2068  else
2069  {
2070  LWDEBUG(4, "PostGIS < 1.0, non-binary cursor");
2071 
2072  /* Input is already hexewkb string, so we can just
2073  copy directly to hexewkb */
2074  hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2075  hexewkb = malloc(hexewkb_len + 1);
2076  strncpy(hexewkb, val, hexewkb_len + 1);
2077  }
2078  }
2079  else /* binary */
2080  {
2081  LWDEBUG(4, "PostGIS (any version) using binary cursor");
2082 
2083  /* Input is binary field - must convert to hexewkb string */
2084  hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2085  hexewkb = convert_bytes_to_hex((unsigned char *)val, hexewkb_len);
2086  }
2087 
2088  LWDEBUGF(4, "HexEWKB - length: %d value: %s", strlen(hexewkb), hexewkb);
2089 
2090  /* Deserialize the LWGEOM */
2091  lwgeom = lwgeom_from_hexwkb(hexewkb, LW_PARSER_CHECK_NONE);
2092  if (!lwgeom)
2093  {
2094  snprintf(state->message, SHPDUMPERMSGLEN, _("Error parsing HEXEWKB for record %d"), state->currow);
2095  PQclear(state->fetchres);
2096  free(hexewkb);
2097  return SHPDUMPERERR;
2098  }
2099 
2100  /* Call the relevant method depending upon the geometry type */
2101  LWDEBUGF(4, "geomtype: %s\n", lwtype_name(lwgeom->type));
2102 
2103  switch (lwgeom->type)
2104  {
2105  case POINTTYPE:
2106  obj = create_point(state, lwgeom_as_lwpoint(lwgeom));
2107  break;
2108 
2109  case MULTIPOINTTYPE:
2110  obj = create_multipoint(state, lwgeom_as_lwmpoint(lwgeom));
2111  break;
2112 
2113  case POLYGONTYPE:
2114  obj = create_polygon(state, lwgeom_as_lwpoly(lwgeom));
2115  break;
2116 
2117  case MULTIPOLYGONTYPE:
2118  obj = create_multipolygon(state, lwgeom_as_lwmpoly(lwgeom));
2119  break;
2120 
2121  case LINETYPE:
2122  obj = create_linestring(state, lwgeom_as_lwline(lwgeom));
2123  break;
2124 
2125  case MULTILINETYPE:
2126  obj = create_multilinestring(state, lwgeom_as_lwmline(lwgeom));
2127  break;
2128 
2129  default:
2130  snprintf(state->message, SHPDUMPERMSGLEN, _("Unknown WKB type (%d) for record %d"), lwgeom->type, state->currow);
2131  PQclear(state->fetchres);
2132  SHPDestroyObject(obj);
2133  return SHPDUMPERERR;
2134  }
2135 
2136  /* Free both the original and geometries */
2137  lwgeom_free(lwgeom);
2138 
2139  /* Write the shape out to the file */
2140  if (SHPWriteObject(state->shp, -1, obj) == -1)
2141  {
2142  snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing shape %d"), state->currow);
2143  PQclear(state->fetchres);
2144  SHPDestroyObject(obj);
2145  return SHPDUMPERERR;
2146  }
2147 
2148  SHPDestroyObject(obj);
2149 
2150  /* Free the hexewkb (and temporary bytea unescaped string if used) */
2151  if (hexewkb) free(hexewkb);
2152  if (hexewkb_binary) PQfreemem(hexewkb_binary);
2153  }
2154  }
2155 
2156  /* Increment ready for next time */
2157  state->curresrow++;
2158  state->currow++;
2159 
2160  return SHPDUMPEROK;
2161 }
2162 
2163 
2164 /* Return a count of the number of rows in the table being dumped */
2165 int
2167 {
2168  return state->rowcount;
2169 }
2170 
2171 
2172 /* Close the specified table and flush all files to disk */
2173 int
2175 {
2176  int ret = SHPDUMPEROK;
2177 
2178  /* Clear the current batch fetch resource */
2179  PQclear(state->fetchres);
2180 
2181  /* If a geo column is present, generate the projection file */
2182  if (state->geo_col_name)
2183  ret = projFileCreate(state);
2184 
2185  /* Close the DBF and SHP files */
2186  if (state->dbf)
2187  DBFClose(state->dbf);
2188  if (state->shp)
2189  SHPClose(state->shp);
2190 
2191  return ret;
2192 }
2193 
2194 
2195 void
2197 {
2198  /* Destroy a state object created with ShpDumperConnect */
2199  int i;
2200 
2201  if (state != NULL)
2202  {
2203  /* Disconnect from the database */
2204  if (state->conn)
2205  PQfinish(state->conn);
2206 
2207  /* Free the query strings */
2208  if (state->fetch_query)
2209  free(state->fetch_query);
2210  if (state->main_scan_query)
2211  free(state->main_scan_query);
2212 
2213  /* Free the DBF information fields */
2214  if (state->dbffieldnames)
2215  {
2216  for (i = 0; i < state->fieldcount; i++)
2217  free(state->dbffieldnames[i]);
2218  free(state->dbffieldnames);
2219  }
2220 
2221  if (state->dbffieldtypes)
2222  free(state->dbffieldtypes);
2223 
2224  if (state->pgfieldnames)
2225  free(state->pgfieldnames);
2226 
2227  /* Free any column map fieldnames if specified */
2228  colmap_clean(&state->column_map);
2229 
2230  /* Free other names */
2231  if (state->table)
2232  free(state->table);
2233  if (state->schema)
2234  free(state->schema);
2235  if (state->geo_col_name)
2236  free(state->geo_col_name);
2237 
2238  /* Free the state itself */
2239  free(state);
2240  }
2241 }
2242 
2243 /*
2244  * quote_identifier()
2245  * Properly double-quote a SQL identifier.
2246  * Copied from PostgreSQL pg_upgrade/util.c
2247  */
2248 char *
2249 quote_identifier(const char *s)
2250 {
2251  char *result = malloc(strlen(s) * 2 + 3);
2252  char *r = result;
2253 
2254  *r++ = '"';
2255  while (*s)
2256  {
2257  if (*s == '"')
2258  *r++ = *s;
2259  *r++ = *s;
2260  s++;
2261  }
2262  *r++ = '"';
2263  *r++ = '\0';
2264 
2265  return result;
2266 }
#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
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 * 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
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:211
int npoints
Definition: liblwgeom.h:370
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:787
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:1063
#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:138
static int getTableInfo(SHPDUMPERSTATE *state)
LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:93
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
int ngeoms
Definition: liblwgeom.h:480
#define SHPT_ARCZ
Definition: shapefil.h:312
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:183
void colmap_init(colmap *map)
Definition: shpcommon.c:159
#define LW_PARSER_CHECK_NONE
Definition: liblwgeom.h:1985
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:216
POINT4D getPoint4d(const POINTARRAY *pa, int n)
Definition: lwgeom_api.c:229
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:174
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:1012
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)
int nrings
Definition: liblwgeom.h:454
char * ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
char * s
Definition: cu_in_wkt.c:23
double z
Definition: liblwgeom.h:351
#define SHPT_MULTIPOINTM
Definition: shapefil.h:318
int ngeoms
Definition: liblwgeom.h:493
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:102
void ShpDumperDestroy(SHPDUMPERSTATE *state)
#define SHPDUMPERMSGLEN
LWGEOM * lwgeom_from_hexwkb(const char *hexwkb, const char check)
Definition: lwin_wkb.c:790
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
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:341
#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:165
static char * nullDBFValue(char fieldType)
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
Definition: dbfopen.c:578
uint8_t type
Definition: liblwgeom.h:395
type
Definition: ovdump.py:41
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
char message[SHPDUMPERMSGLEN]
double y
Definition: liblwgeom.h:351
#define MULTILINETYPE
Definition: liblwgeom.h:88
int ngeoms
Definition: liblwgeom.h:467
char * shapetypename(int num)
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
#define SHPT_ARC
Definition: shapefil.h:308
const char * colmap_dbf_by_pg(colmap *map, const char *pgname)
Definition: shpcommon.c:183
PGresult * fetchres
POINTARRAY * points
Definition: liblwgeom.h:421