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