PostGIS  3.1.6dev-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/stringbuffer.h"
39 #include "../liblwgeom/liblwgeom.h" /* for LWGEOM struct and funx */
40 #include "../liblwgeom/lwgeom_log.h" /* for LWDEBUG macros */
41 
42 /* Maximum DBF field width (according to ARCGIS) */
43 #define MAX_DBF_FIELD_SIZE 254
44 
45 
46 /* Prototypes */
47 static int reverse_points(int num_points, double *x, double *y, double *z, double *m);
48 static int is_clockwise(int num_points,double *x,double *y,double *z);
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 char *
582 {
583  switch (num)
584  {
585  case SHPT_NULL:
586  return "Null Shape";
587  case SHPT_POINT:
588  return "Point";
589  case SHPT_ARC:
590  return "PolyLine";
591  case SHPT_POLYGON:
592  return "Polygon";
593  case SHPT_MULTIPOINT:
594  return "MultiPoint";
595  case SHPT_POINTZ:
596  return "PointZ";
597  case SHPT_ARCZ:
598  return "PolyLineZ";
599  case SHPT_POLYGONZ:
600  return "PolygonZ";
601  case SHPT_MULTIPOINTZ:
602  return "MultiPointZ";
603  case SHPT_POINTM:
604  return "PointM";
605  case SHPT_ARCM:
606  return "PolyLineM";
607  case SHPT_POLYGONM:
608  return "PolygonM";
609  case SHPT_MULTIPOINTM:
610  return "MultiPointM";
611  case SHPT_MULTIPATCH:
612  return "MultiPatch";
613  default:
614  return "Unknown";
615  }
616 }
617 
618 
619 /* This is taken and adapted from dbfopen.c of shapelib */
620 static char *
621 nullDBFValue(char fieldType)
622 {
623  switch (fieldType)
624  {
625  case FTInteger:
626  case FTDouble:
627  /* NULL numeric fields have value "****************" */
628  return "****************";
629 
630  case FTDate:
631  /* NULL date fields have value "00000000" */
632  return " ";
633 
634  case FTLogical:
635  /* NULL boolean fields have value "?" */
636  return "?";
637 
638  default:
639  /* empty string fields are considered NULL */
640  return "";
641  }
642 }
643 
649 static char *
650 goodDBFValue(char *in, char fieldType)
651 {
652  /*
653  * We only work on FTLogical and FTDate.
654  * FTLogical is 1 byte, FTDate is 8 byte (YYYYMMDD)
655  * We allocate space for 9 bytes to take
656  * terminating null into account
657  */
658  static char buf[9];
659 
660  switch (fieldType)
661  {
662  case FTLogical:
663  buf[0] = toupper(in[0]);
664  buf[1]='\0';
665  return buf;
666  case FTDate:
667  buf[0]=in[0]; /* Y */
668  buf[1]=in[1]; /* Y */
669  buf[2]=in[2]; /* Y */
670  buf[3]=in[3]; /* Y */
671  buf[4]=in[5]; /* M */
672  buf[5]=in[6]; /* M */
673  buf[6]=in[8]; /* D */
674  buf[7]=in[9]; /* D */
675  buf[8]='\0';
676  return buf;
677  default:
678  return in;
679  }
680 }
681 
682 char *convert_bytes_to_hex(uint8_t *ewkb, size_t size)
683 {
684  size_t i;
685  char *hexewkb;
686 
687  /* Convert the byte stream to a hex string using liblwgeom's deparse_hex function */
688  hexewkb = malloc(size * 2 + 1);
689  for (i=0; i<size; ++i) deparse_hex(ewkb[i], &hexewkb[i * 2]);
690  hexewkb[size * 2] = '\0';
691 
692  return hexewkb;
693 }
694 
701 static int
703 {
704  FILE *fp;
705  char *pszFullname, *pszBasename;
706  int i;
707 
708  char *pszFilename = state->shp_file;
709  char *schema = state->schema;
710  char *table = state->table;
711  char *geo_col_name = state->geo_col_name;
712 
713  char *srtext;
714  char *query;
715  char *esc_schema;
716  char *esc_table;
717  char *esc_geo_col_name;
718 
719  int error, result;
720  PGresult *res;
721  int size;
722 
723  /***********
724  *** I'm multiplying by 2 instead of 3 because I am too lazy to figure out how many characters to add
725  *** after escaping if any **/
726  size = 1000;
727  if ( schema )
728  {
729  size += 3 * strlen(schema);
730  }
731  size += 1000;
732  esc_table = (char *) malloc(3 * strlen(table) + 1);
733  esc_geo_col_name = (char *) malloc(3 * strlen(geo_col_name) + 1);
734  PQescapeStringConn(state->conn, esc_table, table, strlen(table), &error);
735  PQescapeStringConn(state->conn, esc_geo_col_name, geo_col_name, strlen(geo_col_name), &error);
736 
738  query = (char *) malloc(size);
739  if ( ! query ) return 0; /* out of virtual memory */
740 
741  /**************************************************
742  * Get what kind of spatial ref is the selected geometry field
743  * We first check the geometry_columns table for a match and then if no match do a distinct against the table
744  * NOTE: COALESCE does a short-circuit check returning the faster query result and skipping the second if first returns something
745  * Escaping quotes in the schema and table in query may not be necessary except to prevent malicious attacks
746  * or should someone be crazy enough to have quotes or other weird character in their table, column or schema names
747  **************************************************/
748  if ( schema )
749  {
750  esc_schema = (char *) malloc(2 * strlen(schema) + 1);
751  PQescapeStringConn(state->conn, esc_schema, schema, strlen(schema), &error);
752  sprintf(query, "SELECT COALESCE((SELECT sr.srtext "
753  " FROM geometry_columns As gc INNER JOIN spatial_ref_sys sr ON sr.srid = gc.srid "
754  " WHERE gc.f_table_schema = '%s' AND gc.f_table_name = '%s' AND gc.f_geometry_column = '%s' LIMIT 1), "
755  " (SELECT CASE WHEN COUNT(DISTINCT sr.srid) > 1 THEN 'm' ELSE MAX(sr.srtext) END As srtext "
756  " FROM \"%s\".\"%s\" As g INNER JOIN spatial_ref_sys sr ON sr.srid = ST_SRID((g.\"%s\")::geometry)) , ' ') As srtext ",
757  esc_schema, esc_table,esc_geo_col_name, schema, table, geo_col_name);
758  free(esc_schema);
759  }
760  else
761  {
762  sprintf(query, "SELECT COALESCE((SELECT sr.srtext "
763  " FROM geometry_columns As gc INNER JOIN spatial_ref_sys sr ON sr.srid = gc.srid "
764  " WHERE gc.f_table_name = '%s' AND gc.f_geometry_column = '%s' AND pg_table_is_visible((gc.f_table_schema || '.' || gc.f_table_name)::regclass) LIMIT 1), "
765  " (SELECT CASE WHEN COUNT(DISTINCT sr.srid) > 1 THEN 'm' ELSE MAX(sr.srtext) END as srtext "
766  " FROM \"%s\" As g INNER JOIN spatial_ref_sys sr ON sr.srid = ST_SRID((g.\"%s\")::geometry)), ' ') As srtext ",
767  esc_table, esc_geo_col_name, table, geo_col_name);
768  }
769 
770  LWDEBUGF(3,"%s\n",query);
771  free(esc_table);
772  free(esc_geo_col_name);
773 
774  res = PQexec(state->conn, query);
775 
776  if ( ! res || PQresultStatus(res) != PGRES_TUPLES_OK )
777  {
778  snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Could not execute prj query: %s"), PQresultErrorMessage(res));
779  PQclear(res);
780  free(query);
781  return SHPDUMPERWARN;
782  }
783 
784  for (i=0; i < PQntuples(res); i++)
785  {
786  srtext = PQgetvalue(res, i, 0);
787  if (strcmp(srtext,"m") == 0)
788  {
789  snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Mixed set of spatial references. No prj file will be generated"));
790  PQclear(res);
791  free(query);
792  return SHPDUMPERWARN;
793  }
794  else
795  {
796  if (srtext[0] == ' ')
797  {
798  snprintf(state->message, SHPDUMPERMSGLEN, _("WARNING: Cannot determine spatial reference (empty table or unknown spatial ref). No prj file will be generated."));
799  PQclear(res);
800  free(query);
801  return SHPDUMPERWARN;
802  }
803  else
804  {
805  /* -------------------------------------------------------------------- */
806  /* Compute the base (layer) name. If there is any extension */
807  /* on the passed in filename we will strip it off. */
808  /* -------------------------------------------------------------------- */
809  pszBasename = (char *) malloc(strlen(pszFilename)+5);
810  strcpy( pszBasename, pszFilename );
811  for ( i = strlen(pszBasename)-1;
812  i > 0 && pszBasename[i] != '.' && pszBasename[i] != '/'
813  && pszBasename[i] != '\\';
814  i-- ) {}
815 
816  if ( pszBasename[i] == '.' )
817  pszBasename[i] = '\0';
818 
819  pszFullname = (char *) malloc(strlen(pszBasename) + 5);
820  sprintf( pszFullname, "%s.prj", pszBasename );
821  free( pszBasename );
822 
823 
824  /* -------------------------------------------------------------------- */
825  /* Create the file. */
826  /* -------------------------------------------------------------------- */
827  fp = fopen( pszFullname, "wb" );
828  if ( fp == NULL )
829  {
830  free(pszFullname);
831  free(query);
832  return 0;
833  }
834  else
835  {
836  result = fputs (srtext,fp);
837  LWDEBUGF(3, "\n result %d proj SRText is %s .\n", result, srtext);
838  if (result == EOF)
839  {
840  fclose( fp );
841  free( pszFullname );
842  PQclear(res);
843  free(query);
844  return 0;
845  }
846  }
847  fclose( fp );
848  free( pszFullname );
849  }
850  }
851  }
852  PQclear(res);
853  free(query);
854  return SHPDUMPEROK;
855 }
856 
857 
858 static int
860 {
861 
862  /* Get some more information from the table:
863  - count = total number of geometries/geographies in the table
864 
865  and if we have found a suitable geometry column:
866 
867  - max = maximum number of dimensions within the geometry/geography column
868  - geometrytype = string representing the geometry/geography type, e.g. POINT
869 
870  Since max/geometrytype already require a sequential scan of the table, we may as
871  well get the row count too.
872  */
873 
874  PGresult *res;
875  char *query;
876  int tmpint;
877 
878 
879  if (state->geo_col_name)
880  {
881  /* Include geometry information */
882  if (state->schema)
883  {
884  query = malloc(150 + 4 * strlen(state->geo_col_name) + strlen(state->schema) + strlen(state->table));
885 
886  sprintf(query, "SELECT count(1), max(ST_zmflag(\"%s\"::geometry)), geometrytype(\"%s\"::geometry) FROM \"%s\".\"%s\" GROUP BY 3",
887  state->geo_col_name, state->geo_col_name, state->schema, state->table);
888  }
889  else
890  {
891  query = malloc(150 + 4 * strlen(state->geo_col_name) + strlen(state->table));
892 
893  sprintf(query, "SELECT count(1), max(ST_zmflag(\"%s\"::geometry)), geometrytype(\"%s\"::geometry) FROM \"%s\" GROUP BY 3",
894  state->geo_col_name, state->geo_col_name, state->table);
895  }
896  }
897  else
898  {
899  /* Otherwise... just a row count will do */
900  if (state->schema)
901  {
902  query = malloc(40 + strlen(state->schema) + strlen(state->table));
903 
904  sprintf(query, "SELECT count(1) FROM \"%s\".\"%s\"", state->schema, state->table);
905  }
906  else
907  {
908  query = malloc(40 + strlen(state->table));
909 
910  sprintf(query, "SELECT count(1) FROM \"%s\"", state->table);
911  }
912  }
913 
914  LWDEBUGF(3, "Table metadata query: %s\n", query);
915 
916  res = PQexec(state->conn, query);
917  free(query);
918 
919  if (PQresultStatus(res) != PGRES_TUPLES_OK)
920  {
921  snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Could not execute table metadata query: %s"), PQresultErrorMessage(res));
922  PQclear(res);
923  return SHPDUMPERERR;
924  }
925 
926  /* Make sure we error if the table is empty */
927  if (PQntuples(res) == 0)
928  {
929  snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Could not determine table metadata (empty table)"));
930  PQclear(res);
931  return SHPDUMPERERR;
932  }
933 
934  /* If we have a geo* column, get the dimension, type and count information */
935  if (state->geo_col_name)
936  {
937  /* If a table has a geometry column containing mixed types then
938  the metadata query will return multiple rows. We need to cycle
939  through all rows to determine if the type combinations are valid.
940 
941  Note that if we find a combination of a MULTI and non-MULTI geometry
942  of the same type, we always choose MULTI to ensure that everything
943  gets output correctly. The create_* conversion functions are clever
944  enough to up-convert the non-MULTI geometry to a MULTI in this case. */
945 
946  int dummy, i;
947  uint8_t type = 0;
948  int typefound = 0, typemismatch = 0;
949 
950  state->rowcount = 0;
951 
952  for (i = 0; i < PQntuples(res); i++)
953  {
954  /* skip null geometries */
955  if (PQgetisnull(res, i, 2))
956  {
957  state->rowcount += atoi(PQgetvalue(res, i, 0));
958  continue;
959  }
960 
961  geometry_type_from_string(PQgetvalue(res, i, 2), &type, &dummy, &dummy);
962 
963  /* We can always set typefound to that of the first column found */
964  if (!typefound)
965  typefound = type;
966 
967  switch (type)
968  {
969  case MULTIPOINTTYPE:
970  if (typefound != MULTIPOINTTYPE && typefound != POINTTYPE)
971  typemismatch = 1;
972  else
973  typefound = MULTIPOINTTYPE;
974  break;
975 
976  case MULTILINETYPE:
977  if (typefound != MULTILINETYPE && typefound != LINETYPE)
978  typemismatch = 1;
979  else
980  typefound = MULTILINETYPE;
981  break;
982 
983  case MULTIPOLYGONTYPE:
984  if (typefound != MULTIPOLYGONTYPE && typefound != POLYGONTYPE)
985  typemismatch = 1;
986  else
987  typefound = MULTIPOLYGONTYPE;
988  break;
989 
990  case POINTTYPE:
991  if (typefound != POINTTYPE && typefound != MULTIPOINTTYPE)
992  typemismatch = 1;
993  else if (!lwtype_is_collection(type))
994  typefound = POINTTYPE;
995  break;
996 
997  case LINETYPE:
998  if (typefound != LINETYPE && typefound != MULTILINETYPE)
999  typemismatch = 1;
1000  else if (!lwtype_is_collection(type))
1001  typefound = LINETYPE;
1002  break;
1003 
1004  case POLYGONTYPE:
1005  if (typefound != POLYGONTYPE && typefound != MULTIPOLYGONTYPE)
1006  typemismatch = 1;
1007  else if (!lwtype_is_collection(type))
1008  typefound = POLYGONTYPE;
1009  break;
1010  }
1011 
1012  /* Update the rowcount for each type */
1013  state->rowcount += atoi(PQgetvalue(res, i, 0));
1014 
1015  /* Set up the dimension output type (note: regardless of how many rows
1016  the table metadata query returns, this value will be the same. But
1017  we'll choose to use the first value anyway) */
1018  tmpint = atoi(PQgetvalue(res, i, 1));
1019  switch (tmpint)
1020  {
1021  case 0:
1022  state->outtype = 's';
1023  break;
1024  case 1:
1025  state->outtype = 'm';
1026  break;
1027  default:
1028  state->outtype = 'z';
1029  break;
1030  }
1031 
1032  }
1033 
1034  /* Flag an error if the table contains incompatible geometry combinations */
1035  if (typemismatch)
1036  {
1037  snprintf(state->message, SHPDUMPERMSGLEN, _("ERROR: Incompatible mixed geometry types in table"));
1038  PQclear(res);
1039  return SHPDUMPERERR;
1040  }
1041 
1042  /* Set up the shapefile output type based upon the dimension information */
1043  switch (typefound)
1044  {
1045  case POINTTYPE:
1046  switch(state->outtype)
1047  {
1048  case 'z':
1049  state->outshptype = SHPT_POINTZ;
1050  break;
1051 
1052  case 'm':
1053  state->outshptype = SHPT_POINTM;
1054  break;
1055 
1056  default:
1057  state->outshptype = SHPT_POINT;
1058  }
1059  break;
1060 
1061  case MULTIPOINTTYPE:
1062  switch(state->outtype)
1063  {
1064  case 'z':
1065  state->outshptype = SHPT_MULTIPOINTZ;
1066  break;
1067 
1068  case 'm':
1069  state->outshptype = SHPT_MULTIPOINTM;
1070  break;
1071 
1072  default:
1073  state->outshptype = SHPT_MULTIPOINT;
1074  }
1075  break;
1076 
1077  case LINETYPE:
1078  case MULTILINETYPE:
1079  switch(state->outtype)
1080  {
1081  case 'z':
1082  state->outshptype = SHPT_ARCZ;
1083  break;
1084 
1085  case 'm':
1086  state->outshptype = SHPT_ARCM;
1087  break;
1088 
1089  default:
1090  state->outshptype = SHPT_ARC;
1091  }
1092  break;
1093 
1094  case POLYGONTYPE:
1095  case MULTIPOLYGONTYPE:
1096  switch(state->outtype)
1097  {
1098  case 'z':
1099  state->outshptype = SHPT_POLYGONZ;
1100  break;
1101 
1102  case 'm':
1103  state->outshptype = SHPT_POLYGONM;
1104  break;
1105 
1106  default:
1107  state->outshptype = SHPT_POLYGON;
1108  }
1109  break;
1110  }
1111  }
1112  else
1113  {
1114  /* Without a geo* column the total is simply the first (COUNT) column */
1115  state->rowcount = atoi(PQgetvalue(res, 0, 0));
1116  }
1117 
1118  /* Dispose of the result set */
1119  PQclear(res);
1120 
1121  return SHPDUMPEROK;
1122 }
1123 
1124 
1125 /* Default configuration settings */
1126 void
1128 {
1129  config->conn = malloc(sizeof(SHPCONNECTIONCONFIG));
1130  config->conn->host = NULL;
1131  config->conn->port = NULL;
1132  config->conn->database = NULL;
1133  config->conn->username = NULL;
1134  config->conn->password = NULL;
1135 
1136  config->table = NULL;
1137  config->schema = NULL;
1138  config->usrquery = NULL;
1139  config->binary = 0;
1140  config->shp_file = NULL;
1141  config->dswitchprovided = 0;
1142  config->includegid = 0;
1143  config->unescapedattrs = 0;
1144  config->geo_col_name = NULL;
1145  config->keep_fieldname_case = 0;
1146  config->fetchsize = 100;
1147  config->column_map_filename = NULL;
1148  config->quiet = 0;
1149 }
1150 
1151 /* Create a new shapefile state object */
1154 {
1155  SHPDUMPERSTATE *state;
1156 
1157  /* Create a new state object and assign the config to it */
1158  state = malloc(sizeof(SHPDUMPERSTATE));
1159  state->config = config;
1160 
1161  /* Set any state defaults */
1162  state->conn = NULL;
1163  state->outtype = 's';
1164  state->outshptype = 0;
1165  state->geom_oid = 0;
1166  state->geog_oid = 0;
1167  state->schema = NULL;
1168  state->table = NULL;
1169  state->geo_col_name = NULL;
1170  state->fetch_query = NULL;
1171  state->main_scan_query = NULL;
1172  state->dbffieldnames = NULL;
1173  state->dbffieldtypes = NULL;
1174  state->pgfieldnames = NULL;
1175  state->message[0] = '\0';
1176  colmap_init(&state->column_map);
1177 
1178  return state;
1179 }
1180 
1181 /* Generate the database connection string used by a state */
1182 char *
1184 {
1185  char *connstring;
1186  int connlen;
1187 
1188  connlen = 64 +
1189  (conn->host ? strlen(conn->host) : 0) + (conn->port ? strlen(conn->port) : 0) +
1190  (conn->username ? strlen(conn->username) : 0) + (conn->password ? strlen(conn->password) : 0) +
1191  (conn->database ? strlen(conn->database) : 0);
1192 
1193  connstring = malloc(connlen);
1194  memset(connstring, 0, connlen);
1195 
1196  if (conn->host)
1197  {
1198  strcat(connstring, " host=");
1199  strcat(connstring, conn->host);
1200  }
1201 
1202  if (conn->port)
1203  {
1204  strcat(connstring, " port=");
1205  strcat(connstring, conn->port);
1206  }
1207 
1208  if (conn->username)
1209  {
1210  strcat(connstring, " user=");
1211  strcat(connstring, conn->username);
1212  }
1213 
1214  if (conn->password)
1215  {
1216  strcat(connstring, " password='");
1217  strcat(connstring, conn->password);
1218  strcat(connstring, "'");
1219  }
1220 
1221  if (conn->database)
1222  {
1223  strcat(connstring, " dbname=");
1224  strcat(connstring, conn->database);
1225  }
1226 
1227  if ( ! getenv("PGCLIENTENCODING") )
1228  {
1229  strcat(connstring, " client_encoding=UTF8");
1230  }
1231 
1232  return connstring;
1233 }
1234 
1235 /* Connect to the database and identify the version of PostGIS (and any other
1236 capabilities required) */
1237 int
1239 {
1240  PGresult *res;
1241 
1242  char *connstring, *tmpvalue;
1243 
1244  /* Generate the PostgreSQL connection string */
1245  connstring = ShpDumperGetConnectionStringFromConn(state->config->conn);
1246 
1247  /* Connect to the database */
1248  state->conn = PQconnectdb(connstring);
1249  if (PQstatus(state->conn) == CONNECTION_BAD)
1250  {
1251  snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQerrorMessage(state->conn));
1252  free(connstring);
1253  return SHPDUMPERERR;
1254  }
1255 
1256  /* Set datestyle to ISO */
1257  res = PQexec(state->conn, "SET DATESTYLE='ISO'");
1258  if (PQresultStatus(res) != PGRES_COMMAND_OK)
1259  {
1260  snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1261  PQclear(res);
1262  free(connstring);
1263  return SHPDUMPERERR;
1264  }
1265 
1266  PQclear(res);
1267 
1268  /* Retrieve PostGIS major version */
1269  res = PQexec(state->conn, "SELECT postgis_version()");
1270  if (PQresultStatus(res) != PGRES_TUPLES_OK)
1271  {
1272  snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1273  PQclear(res);
1274  free(connstring);
1275  return SHPDUMPERERR;
1276  }
1277 
1278  tmpvalue = PQgetvalue(res, 0, 0);
1279  state->pgis_major_version = atoi(tmpvalue);
1280 
1281  PQclear(res);
1282 
1283  /* Find the OID for the geometry type */
1284  res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1285  if (PQresultStatus(res) != PGRES_TUPLES_OK)
1286  {
1287  snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geometry oid: %s"), PQresultErrorMessage(res));
1288  PQclear(res);
1289  free(connstring);
1290  return SHPDUMPERERR;
1291  }
1292 
1293  if (PQntuples(res) > 0)
1294  {
1295  tmpvalue = PQgetvalue(res, 0, 0);
1296  state->geom_oid = atoi(tmpvalue);
1297  }
1298  else
1299  {
1300  snprintf(state->message, SHPDUMPERMSGLEN, _("Geometry type unknown (have you enabled postgis?)"));
1301  PQclear(res);
1302  free(connstring);
1303  return SHPDUMPERERR;
1304  }
1305 
1306  PQclear(res);
1307 
1308  /* Find the OID for the geography type */
1309  res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geography'");
1310  if (PQresultStatus(res) != PGRES_TUPLES_OK)
1311  {
1312  snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geography oid: %s"), PQresultErrorMessage(res));
1313  PQclear(res);
1314  free(connstring);
1315  return SHPDUMPERERR;
1316  }
1317 
1318  if (PQntuples(res) > 0)
1319  {
1320  /* Old databases don't have a geography type, so don't fail if we don't find it */
1321  tmpvalue = PQgetvalue(res, 0, 0);
1322  state->geog_oid = atoi(tmpvalue);
1323  }
1324 
1325  PQclear(res);
1326 
1327  free(connstring);
1328 
1329  return SHPDUMPEROK;
1330 }
1331 
1332 
1333 /* Open the specified table in preparation for extracting rows */
1334 int
1336 {
1337  PGresult *res;
1338 
1339  char buf[256];
1340  char *query;
1341  int gidfound = 0, i, j, ret, status;
1342 
1343 
1344  /* Open the column map if one was specified */
1345  if (state->config->column_map_filename)
1346  {
1347  ret = colmap_read(state->config->column_map_filename,
1348  &state->column_map, state->message, SHPDUMPERMSGLEN);
1349  if (!ret) return SHPDUMPERERR;
1350  }
1351 
1352  /* If a user-defined query has been specified, create and point the state to our new table */
1353  if (state->config->usrquery)
1354  {
1355  int r = asprintf(&(state->table), "__pgsql2shp%lu_tmp_table", (long)getpid());
1356  (void)r;
1357  r = asprintf(&query, "CREATE TEMP TABLE \"%s\" AS %s", state->table, state->config->usrquery);
1358  (void)r;
1359  res = PQexec(state->conn, query);
1360  free(query);
1361 
1362  /* Execute the code to create the table */
1363  if (PQresultStatus(res) != PGRES_COMMAND_OK)
1364  {
1365  snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing user query: %s"), PQresultErrorMessage(res));
1366  PQclear(res);
1367  return SHPDUMPERERR;
1368  }
1369  }
1370  else
1371  {
1372  /* Simply point the state to copies of the supplied schema and table */
1373  state->table = strdup(state->config->table);
1374  if (state->config->schema)
1375  state->schema = strdup(state->config->schema);
1376  }
1377 
1378 
1379  /* Get the list of columns and their types for the selected table */
1380  if (state->schema)
1381  {
1382  int r = asprintf(&query, "SELECT a.attname, a.atttypid, "
1383  "a.atttypmod, a.attlen FROM "
1384  "pg_attribute a, pg_class c, pg_namespace n WHERE "
1385  "n.nspname = '%s' AND a.attrelid = c.oid AND "
1386  "n.oid = c.relnamespace AND "
1387  "a.atttypid != 0 AND "
1388  "a.attnum > 0 AND c.relname = '%s'", state->schema, state->table);
1389  (void)r;
1390  }
1391  else
1392  {
1393  int r = asprintf(&query, "SELECT a.attname, a.atttypid, "
1394  "a.atttypmod, a.attlen FROM "
1395  "pg_attribute a, pg_class c WHERE "
1396  "a.attrelid = c.oid and a.attnum > 0 AND "
1397  "a.atttypid != 0 AND "
1398  "c.relname = '%s' AND "
1399  "pg_catalog.pg_table_is_visible(c.oid)", state->table);
1400  (void)r;
1401  }
1402 
1403  LWDEBUGF(3, "query is: %s\n", query);
1404 
1405  res = PQexec(state->conn, query);
1406  free(query);
1407 
1408  if (PQresultStatus(res) != PGRES_TUPLES_OK)
1409  {
1410  snprintf(state->message, SHPDUMPERMSGLEN, _("Error querying for attributes: %s"), PQresultErrorMessage(res));
1411  PQclear(res);
1412  return SHPDUMPERERR;
1413  }
1414 
1415  if (!PQntuples(res))
1416  {
1417  snprintf(state->message, SHPDUMPERMSGLEN, _("Table %s does not exist"), state->table);
1418  PQclear(res);
1419  return SHPDUMPERERR;
1420  }
1421 
1422  /* If a shapefile name was specified, use it. Otherwise simply use the table name. */
1423  if (state->config->shp_file != NULL)
1424  state->shp_file = state->config->shp_file;
1425  else
1426  state->shp_file = state->table;
1427 
1428  /* Create the dbf file: */
1429  /* If there's a user-specified encoding hanging around, try and use that. */
1430  /* Otherwise, just use UTF-8 encoding, since that's usually our client encoding. */
1431  if ( getenv("PGCLIENTENCODING") )
1432  {
1433  char *codepage = encoding2codepage(getenv("PGCLIENTENCODING"));
1434  state->dbf = DBFCreateEx(state->shp_file, codepage);
1435  }
1436  else
1437  {
1438  state->dbf = DBFCreateEx(state->shp_file, "UTF-8");
1439  }
1440 
1441  if (!state->dbf)
1442  {
1443  snprintf(state->message, SHPDUMPERMSGLEN, _("Could not create dbf file %s"), state->shp_file);
1444  return SHPDUMPERERR;
1445  }
1446 
1447  /* Mimic old behaviour and skip the EOF character (1A) */
1448  DBFSetWriteEndOfFileChar(state->dbf, 0);
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, pgfieldname);
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, abs(tmpint) % 100);
1553  tmpint++;
1554  continue;
1555  }
1556  }
1557 
1558  /* make UPPERCASE if keep_fieldname_case = 0 */
1559  if (!state->config->keep_fieldname_case)
1560  {
1561  size_t nameit;
1562  for (nameit = 0; nameit < strlen(dbffieldname); nameit++)
1563  dbffieldname[nameit] = toupper(dbffieldname[nameit]);
1564  }
1565 
1566  /* Issue warning if column has been renamed */
1567  if (strcasecmp(dbffieldname, pgfieldname))
1568  {
1569  snprintf(buf, sizeof(buf), _("Warning, field %s renamed to %s\n"), pgfieldname, dbffieldname);
1570  /* Note: we concatenate all warnings from the main loop as this is useful information */
1571  if (SHPDUMPERMSGLEN > (strlen(state->message) + 1))
1572  strncat(state->message, buf, SHPDUMPERMSGLEN - (strlen(state->message) + 1));
1573 
1574  ret = SHPDUMPERWARN;
1575  }
1576 
1577 
1578  /*
1579  * Find appropriate type of dbf attributes
1580  */
1581 
1582  /* int2 type */
1583  if (pgfieldtype == 21)
1584  {
1585  /*
1586  * Longest text representation for
1587  * an int2 type (16bit) is 6 bytes
1588  * (-32768)
1589  */
1590  dbffieldtype = FTInteger;
1591  dbffieldsize = 6;
1592  dbffielddecs = 0;
1593  }
1594 
1595  /* int4 type */
1596  else if (pgfieldtype == 23)
1597  {
1598  /*
1599  * Longest text representation for
1600  * an int4 type (32bit) is 11 bytes
1601  * (-2147483648)
1602  */
1603  dbffieldtype = FTInteger;
1604  dbffieldsize = 11;
1605  dbffielddecs = 0;
1606  }
1607 
1608  /* int8 type */
1609  else if (pgfieldtype == 20)
1610  {
1611  /*
1612  * Longest text representation for
1613  * an int8 type (64bit) is 20 bytes
1614  * (-9223372036854775808)
1615  */
1616  dbffieldtype = FTInteger;
1617  dbffieldsize = 19;
1618  dbffielddecs = 0;
1619  }
1620 
1621  /*
1622  * double or numeric types:
1623  * 700: float4
1624  * 701: float8
1625  * 1700: numeric
1626  *
1627  *
1628  * TODO: stricter handling of sizes
1629  */
1630  else if (pgfieldtype == 700 || pgfieldtype == 701 || pgfieldtype == 1700)
1631  {
1632  dbffieldtype = FTDouble;
1633  dbffieldsize = 32;
1634  dbffielddecs = 10;
1635  }
1636 
1637  /*
1638  * Boolean field, we use FTLogical
1639  */
1640  else if (pgfieldtype == 16)
1641  {
1642  dbffieldtype = FTLogical;
1643  dbffieldsize = 1;
1644  dbffielddecs = 0;
1645  }
1646 
1647  /*
1648  * Date field
1649  */
1650  else if (pgfieldtype == 1082)
1651  {
1652  dbffieldtype = FTDate;
1653  dbffieldsize = 8;
1654  dbffielddecs = 0;
1655  }
1656 
1657  /*
1658  * time, timetz, timestamp, or timestamptz field.
1659  */
1660  else if (pgfieldtype == 1083 || pgfieldtype == 1266 || pgfieldtype == 1114 || pgfieldtype == 1184)
1661  {
1662  int secondsize;
1663 
1664  switch (pgtypmod)
1665  {
1666  case -1:
1667  secondsize = 6 + 1;
1668  break;
1669  case 0:
1670  secondsize = 0;
1671  break;
1672  default:
1673  secondsize = pgtypmod + 1;
1674  break;
1675  }
1676 
1677  /* We assume the worst case scenario for all of these:
1678  * date = '5874897-12-31' = 13
1679  * date = '294276-11-20' = 12 (with --enable-interger-datetimes)
1680  * time = '00:00:00' = 8
1681  * zone = '+01:39:52' = 9 (see Europe/Helsinki around 1915)
1682  */
1683 
1684  /* time */
1685  if (pgfieldtype == 1083)
1686  {
1687  dbffieldsize = 8 + secondsize;
1688  }
1689  /* timetz */
1690  else if (pgfieldtype == 1266)
1691  {
1692  dbffieldsize = 8 + secondsize + 9;
1693  }
1694  /* timestamp */
1695  else if (pgfieldtype == 1114)
1696  {
1697  dbffieldsize = 13 + 1 + 8 + secondsize;
1698  }
1699  /* timestamptz */
1700  else if (pgfieldtype == 1184)
1701  {
1702  dbffieldsize = 13 + 1 + 8 + secondsize + 9;
1703  }
1704 
1705  dbffieldtype = FTString;
1706  dbffielddecs = 0;
1707  }
1708 
1709  /*
1710  * uuid type 36 bytes (12345678-9012-3456-7890-123456789012)
1711  */
1712  else if (pgfieldtype == 2950)
1713  {
1714  dbffieldtype = FTString;
1715  dbffieldsize = 36;
1716  dbffielddecs = 0;
1717  }
1718 
1719  /*
1720  * For variable-sized fields we know about, we use
1721  * the maximum allowed size.
1722  * 1042 is bpchar, 1043 is varchar
1723  */
1724  else if ((pgfieldtype == 1042 || pgfieldtype == 1043) && pgtypmod != -1)
1725  {
1726  /*
1727  * mod is maximum allowed size, including
1728  * header which contains *real* size.
1729  */
1730  dbffieldtype = FTString;
1731  dbffieldsize = pgtypmod - 4; /* 4 is header size */
1732  dbffielddecs = 0;
1733  }
1734 
1735  /* For all other valid non-geometry/geography fields... */
1736  else if (dbffieldtype == -1)
1737  {
1738  /*
1739  * For types we don't know anything about, all
1740  * we can do is query the table for the maximum field
1741  * size.
1742  */
1743  dbffieldsize = getMaxFieldSize(state->conn, state->schema, state->table, pgfieldname);
1744  if (dbffieldsize == -1)
1745  {
1746  free(dbffieldname);
1747  return 0;
1748  }
1749 
1750  if (!dbffieldsize)
1751  dbffieldsize = 32;
1752 
1753  /* might 0 be a good size ? */
1754 
1755  dbffieldtype = FTString;
1756  dbffielddecs = 0;
1757 
1758  /* Check to make sure the final field size isn't too large */
1759  if (dbffieldsize > MAX_DBF_FIELD_SIZE)
1760  {
1761  /* Note: we concatenate all warnings from the main loop as this is useful information */
1762  snprintf(buf, sizeof(buf), _("Warning: values of field '%s' exceeding maximum dbf field width (%d) "
1763  "will be truncated.\n"), dbffieldname, MAX_DBF_FIELD_SIZE);
1764 
1765  if (SHPDUMPERMSGLEN > (strlen(state->message) + 1))
1766  strncat(state->message, buf, SHPDUMPERMSGLEN - (strlen(state->message)+1));
1767 
1768  dbffieldsize = MAX_DBF_FIELD_SIZE;
1769 
1770  ret = SHPDUMPERWARN;
1771  }
1772  }
1773 
1774  LWDEBUGF(3, "DBF FIELD_NAME: %s, SIZE: %d\n", dbffieldname, dbffieldsize);
1775 
1776  if (dbffieldtype != 9)
1777  {
1778  /* Add the field to the DBF file */
1779  if (DBFAddField(state->dbf, dbffieldname, dbffieldtype, dbffieldsize, dbffielddecs) == -1)
1780  {
1781  snprintf(state->message, SHPDUMPERMSGLEN, _("Error: field %s of type %d could not be created."), dbffieldname, dbffieldtype);
1782 
1783  return SHPDUMPERERR;
1784  }
1785 
1786  /* Add the field information to our field arrays */
1787  state->dbffieldnames[state->fieldcount] = dbffieldname;
1788  state->dbffieldtypes[state->fieldcount] = dbffieldtype;
1789  state->pgfieldnames[state->fieldcount] = pgfieldname;
1790  state->pgfieldlens[state->fieldcount] = pgfieldlen;
1791  state->pgfieldtypmods[state->fieldcount] = pgtypmod;
1792 
1793  state->fieldcount++;
1794  }
1795  }
1796 
1797  /* Now we have generated the field lists, grab some info about the table */
1798  status = getTableInfo(state);
1799  if (status == SHPDUMPERERR)
1800  return SHPDUMPERERR;
1801 
1802  LWDEBUGF(3, "rows: %d\n", state->rowcount);
1803  LWDEBUGF(3, "shptype: %c\n", state->outtype);
1804  LWDEBUGF(3, "shpouttype: %d\n", state->outshptype);
1805 
1806  /* If we didn't find a geometry/geography column... */
1807  if (!state->geo_col_name)
1808  {
1809  if (state->config->geo_col_name)
1810  {
1811  /* A geo* column was specified, but not found */
1812  snprintf(state->message, SHPDUMPERMSGLEN, _("%s: no such attribute in table %s"), state->config->geo_col_name, state->table);
1813 
1814  return SHPDUMPERERR;
1815  }
1816  else
1817  {
1818  /* No geo* column specified so we can only create the DBF section -
1819  but let's issue a warning... */
1820  snprintf(buf, sizeof(buf), _("No geometry column found.\nThe DBF file will be created but not the shx or shp files.\n"));
1821  if (SHPDUMPERMSGLEN > (strlen(state->message) + 1))
1822  strncat(state->message, buf, SHPDUMPERMSGLEN - (strlen(state->message)+1));
1823 
1824  state->shp = NULL;
1825 
1826  ret = SHPDUMPERWARN;
1827  }
1828  }
1829  else
1830  {
1831  /* Since we have found a geo* column, open the shapefile */
1832  state->shp = SHPCreate(state->shp_file, state->outshptype);
1833  if (!state->shp)
1834  {
1835  snprintf(state->message, SHPDUMPERMSGLEN, _("Could not open shapefile %s!"), state->shp_file);
1836 
1837  return SHPDUMPERERR;
1838  }
1839  }
1840 
1841  /* Now we have the complete list of fieldnames, let's generate the SQL query. First let's make sure
1842  we reserve enough space for tables with lots of columns */
1843  j = 0;
1844 
1845  /*TODO: this really should be rewritten to use stringbuffer */
1846  for (i = 0; i < state->fieldcount; i++)
1847  j += strlen( state->pgfieldnames[i]) + 10; /*add extra space for the quotes to quote identify and any embedded quotes that may need escaping */
1848 
1849  state->main_scan_query = malloc(1024 + j);
1850 
1851  sprintf(state->main_scan_query, "DECLARE cur ");
1852  if (state->config->binary)
1853  strcat(state->main_scan_query, "BINARY ");
1854 
1855  strcat(state->main_scan_query, "CURSOR FOR SELECT ");
1856 
1857  for (i = 0; i < state->fieldcount; i++)
1858  {
1859  /* Comma-separated column names */
1860  if (i > 0)
1861  strcat(state->main_scan_query, ",");
1862 
1863  if (state->config->binary)
1864  sprintf(buf, "%s::text", quote_identifier(state->pgfieldnames[i]) ) ;
1865  else
1866  sprintf(buf, "%s", quote_identifier(state->pgfieldnames[i]) );
1867 
1868  strcat(state->main_scan_query, buf);
1869  }
1870 
1871  /* If we found a valid geometry/geography column then use it */
1872  if (state->geo_col_name)
1873  {
1874  /* If this is the (only) column, no need for the initial comma */
1875  if (state->fieldcount > 0)
1876  strcat(state->main_scan_query, ",");
1877 
1878 #ifdef WORDS_BIGENDIAN
1879  if (state->pgis_major_version > 0)
1880  {
1881  sprintf(buf, "ST_asEWKB(ST_SetSRID(%s::geometry, 0), 'XDR') AS _geoX", quote_identifier(state->geo_col_name) );
1882  }
1883  else
1884  {
1885  sprintf(buf, "asbinary(%s::geometry, 'XDR') AS _geoX",
1886  quote_identifier(state->geo_col_name) );
1887  }
1888 #else
1889  if (state->pgis_major_version > 0)
1890  {
1891  sprintf(buf, "ST_AsEWKB(ST_SetSRID(%s::geometry, 0), 'NDR') AS _geoX", quote_identifier(state->geo_col_name) ) ;
1892  }
1893  else
1894  {
1895  sprintf(buf, "asbinary(%s::geometry, 'NDR') AS _geoX",
1896  quote_identifier(state->geo_col_name) );
1897  }
1898 #endif
1899 
1900  strcat(state->main_scan_query, buf);
1901  }
1902 
1903  if (state->schema)
1904  {
1905  sprintf(buf, " FROM \"%s\".\"%s\"", state->schema, state->table);
1906  }
1907  else
1908  {
1909  sprintf(buf, " FROM \"%s\"", state->table);
1910  }
1911 
1912  strcat(state->main_scan_query, buf);
1913 
1914  /* Order by 'gid' (if found) */
1915  if (gidfound)
1916  {
1917  sprintf(buf, " ORDER BY \"gid\"");
1918  strcat(state->main_scan_query, buf);
1919  }
1920 
1921  /* Now we've finished with the result set, we can dispose of it */
1922  PQclear(res);
1923 
1924  LWDEBUGF(3, "FINAL QUERY: %s\n", state->main_scan_query);
1925 
1926  /*
1927  * Begin the transaction
1928  * (a cursor can only be defined inside a transaction block)
1929  */
1930  res = PQexec(state->conn, "BEGIN");
1931  if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1932  {
1933  snprintf(state->message, SHPDUMPERMSGLEN, _("Error starting transaction: %s"), PQresultErrorMessage(res));
1934  PQclear(res);
1935  return SHPDUMPERERR;
1936  }
1937 
1938  PQclear(res);
1939 
1940  /* Execute the main scan query */
1941  res = PQexec(state->conn, state->main_scan_query);
1942  if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1943  {
1944  snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing main scan query: %s"), PQresultErrorMessage(res));
1945  PQclear(res);
1946  return SHPDUMPERERR;
1947  }
1948 
1949  PQclear(res);
1950 
1951  /* Setup initial scan state */
1952  state->currow = 0;
1953  state->curresrow = 0;
1954  state->currescount = 0;
1955  state->fetchres = NULL;
1956 
1957  /* Generate the fetch query */
1958  tmpint = asprintf(&(state->fetch_query), "FETCH %d FROM cur", state->config->fetchsize);
1959 
1960  return SHPDUMPEROK;
1961 }
1962 
1963 
1964 /* Append the next row to the output shapefile */
1966 {
1967  char *hexewkb = NULL;
1968  unsigned char *hexewkb_binary = NULL;
1969  size_t hexewkb_len;
1970  char *val;
1971  SHPObject *obj = NULL;
1972  LWGEOM *lwgeom;
1973 
1974  int i, geocolnum = 0;
1975 
1976  /* If we try to go pass the end of the table, fail immediately */
1977  if (state->currow > state->rowcount)
1978  {
1979  snprintf(state->message, SHPDUMPERMSGLEN, _("Tried to read past end of table!"));
1980  PQclear(state->fetchres);
1981  return SHPDUMPERERR;
1982  }
1983 
1984  /* If we have reached the end of the current batch, fetch a new one */
1985  if (state->curresrow == state->currescount && state->currow < state->rowcount)
1986  {
1987  /* Clear the previous batch results */
1988  if (state->fetchres)
1989  PQclear(state->fetchres);
1990 
1991  state->fetchres = PQexec(state->conn, state->fetch_query);
1992  if (PQresultStatus(state->fetchres) != PGRES_TUPLES_OK)
1993  {
1994  snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing fetch query: %s"), PQresultErrorMessage(state->fetchres));
1995  PQclear(state->fetchres);
1996  return SHPDUMPERERR;
1997  }
1998 
1999  state->curresrow = 0;
2000  state->currescount = PQntuples(state->fetchres);
2001  }
2002 
2003  /* Grab the id of the geo column if we have one */
2004  if (state->geo_col_name)
2005  geocolnum = PQfnumber(state->fetchres, "_geoX");
2006 
2007  /* Process the next record within the batch. First write out all of
2008  the non-geo fields */
2009  for (i = 0; i < state->fieldcount; i++)
2010  {
2011  /*
2012  * Transform NULL numbers to '0'
2013  * This is because the shapelib
2014  * won't easly take care of setting
2015  * nulls unless paying the acquisition
2016  * of a bug in long integer values
2017  */
2018  if (PQgetisnull(state->fetchres, state->curresrow, i))
2019  {
2020  val = nullDBFValue(state->dbffieldtypes[i]);
2021  }
2022  else
2023  {
2024  val = PQgetvalue(state->fetchres, state->curresrow, i);
2025  val = goodDBFValue(val, state->dbffieldtypes[i]);
2026  }
2027 
2028  /* Write it to the DBF file */
2029  if (!DBFWriteAttributeDirectly(state->dbf, state->currow, i, val))
2030  {
2031  snprintf(state->message, SHPDUMPERMSGLEN, _("Error: record %d could not be created"), state->currow);
2032  PQclear(state->fetchres);
2033  return SHPDUMPERERR;
2034  }
2035  }
2036 
2037  /* Now process the geo field, if present */
2038  if (state->geo_col_name)
2039  {
2040  /* Handle NULL shapes */
2041  if (PQgetisnull(state->fetchres, state->curresrow, geocolnum))
2042  {
2043  obj = SHPCreateSimpleObject(SHPT_NULL, 0, NULL, NULL, NULL);
2044  if (SHPWriteObject(state->shp, -1, obj) == -1)
2045  {
2046  snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing NULL shape for record %d"), state->currow);
2047  PQclear(state->fetchres);
2048  SHPDestroyObject(obj);
2049  return SHPDUMPERERR;
2050  }
2051  SHPDestroyObject(obj);
2052  }
2053  else
2054  {
2055  /* Get the value from the result set */
2056  val = PQgetvalue(state->fetchres, state->curresrow, geocolnum);
2057 
2058  if (!state->config->binary)
2059  {
2060  if (state->pgis_major_version > 0)
2061  {
2062  LWDEBUG(4, "PostGIS >= 1.0, non-binary cursor");
2063 
2064  /* Input is bytea encoded text field, so it must be unescaped and
2065  then converted to hexewkb string */
2066  hexewkb_binary = PQunescapeBytea((unsigned char *)val, &hexewkb_len);
2067  hexewkb = convert_bytes_to_hex(hexewkb_binary, hexewkb_len);
2068  }
2069  else
2070  {
2071  LWDEBUG(4, "PostGIS < 1.0, non-binary cursor");
2072 
2073  /* Input is already hexewkb string, so we can just
2074  copy directly to hexewkb */
2075  hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2076  hexewkb = malloc(hexewkb_len + 1);
2077  strncpy(hexewkb, val, hexewkb_len + 1);
2078  }
2079  }
2080  else /* binary */
2081  {
2082  LWDEBUG(4, "PostGIS (any version) using binary cursor");
2083 
2084  /* Input is binary field - must convert to hexewkb string */
2085  hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2086  hexewkb = convert_bytes_to_hex((unsigned char *)val, hexewkb_len);
2087  }
2088 
2089  LWDEBUGF(4, "HexEWKB - length: %d value: %s", strlen(hexewkb), hexewkb);
2090 
2091  /* Deserialize the LWGEOM */
2092  lwgeom = lwgeom_from_hexwkb(hexewkb, LW_PARSER_CHECK_NONE);
2093  if (!lwgeom)
2094  {
2095  snprintf(state->message, SHPDUMPERMSGLEN, _("Error parsing HEXEWKB for record %d"), state->currow);
2096  PQclear(state->fetchres);
2097  free(hexewkb);
2098  return SHPDUMPERERR;
2099  }
2100 
2101  /* Call the relevant method depending upon the geometry type */
2102  LWDEBUGF(4, "geomtype: %s\n", lwtype_name(lwgeom->type));
2103 
2104  switch (lwgeom->type)
2105  {
2106  case POINTTYPE:
2107  if (lwgeom_is_empty(lwgeom))
2108  {
2109  obj = create_point_empty(state, lwgeom_as_lwpoint(lwgeom));
2110  }
2111  else
2112  {
2113  obj = create_point(state, lwgeom_as_lwpoint(lwgeom));
2114  }
2115  break;
2116 
2117  case MULTIPOINTTYPE:
2118  obj = create_multipoint(state, lwgeom_as_lwmpoint(lwgeom));
2119  break;
2120 
2121  case POLYGONTYPE:
2122  obj = create_polygon(state, lwgeom_as_lwpoly(lwgeom));
2123  break;
2124 
2125  case MULTIPOLYGONTYPE:
2126  obj = create_multipolygon(state, lwgeom_as_lwmpoly(lwgeom));
2127  break;
2128 
2129  case LINETYPE:
2130  obj = create_linestring(state, lwgeom_as_lwline(lwgeom));
2131  break;
2132 
2133  case MULTILINETYPE:
2134  obj = create_multilinestring(state, lwgeom_as_lwmline(lwgeom));
2135  break;
2136 
2137  default:
2138  snprintf(state->message, SHPDUMPERMSGLEN, _("Unknown WKB type (%d) for record %d"), lwgeom->type, state->currow);
2139  PQclear(state->fetchres);
2140  SHPDestroyObject(obj);
2141  return SHPDUMPERERR;
2142  }
2143 
2144  /* Free both the original and geometries */
2145  lwgeom_free(lwgeom);
2146 
2147  /* Write the shape out to the file */
2148  if (SHPWriteObject(state->shp, -1, obj) == -1)
2149  {
2150  snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing shape %d"), state->currow);
2151  PQclear(state->fetchres);
2152  SHPDestroyObject(obj);
2153  return SHPDUMPERERR;
2154  }
2155 
2156  SHPDestroyObject(obj);
2157 
2158  /* Free the hexewkb (and temporary bytea unescaped string if used) */
2159  if (hexewkb) free(hexewkb);
2160  if (hexewkb_binary) PQfreemem(hexewkb_binary);
2161  }
2162  }
2163 
2164  /* Increment ready for next time */
2165  state->curresrow++;
2166  state->currow++;
2167 
2168  return SHPDUMPEROK;
2169 }
2170 
2171 
2172 /* Return a count of the number of rows in the table being dumped */
2173 int
2175 {
2176  return state->rowcount;
2177 }
2178 
2179 
2180 /* Close the specified table and flush all files to disk */
2181 int
2183 {
2184  int ret = SHPDUMPEROK;
2185 
2186  /* Clear the current batch fetch resource */
2187  PQclear(state->fetchres);
2188 
2189  /* If a geo column is present, generate the projection file */
2190  if (state->geo_col_name)
2191  ret = projFileCreate(state);
2192 
2193  /* Close the DBF and SHP files */
2194  if (state->dbf)
2195  DBFClose(state->dbf);
2196  if (state->shp)
2197  SHPClose(state->shp);
2198 
2199  return ret;
2200 }
2201 
2202 
2203 void
2205 {
2206  /* Destroy a state object created with ShpDumperConnect */
2207  int i;
2208 
2209  if (state != NULL)
2210  {
2211  /* Disconnect from the database */
2212  if (state->conn)
2213  PQfinish(state->conn);
2214 
2215  /* Free the query strings */
2216  if (state->fetch_query)
2217  free(state->fetch_query);
2218  if (state->main_scan_query)
2219  free(state->main_scan_query);
2220 
2221  /* Free the DBF information fields */
2222  if (state->dbffieldnames)
2223  {
2224  for (i = 0; i < state->fieldcount; i++)
2225  free(state->dbffieldnames[i]);
2226  free(state->dbffieldnames);
2227  }
2228 
2229  if (state->dbffieldtypes)
2230  free(state->dbffieldtypes);
2231 
2232  if (state->pgfieldnames)
2233  free(state->pgfieldnames);
2234 
2235  /* Free any column map fieldnames if specified */
2236  colmap_clean(&state->column_map);
2237 
2238  /* Free other names */
2239  if (state->table)
2240  free(state->table);
2241  if (state->schema)
2242  free(state->schema);
2243  if (state->geo_col_name)
2244  free(state->geo_col_name);
2245 
2246  /* Free the state itself */
2247  free(state);
2248  }
2249 }
2250 
2251 /*
2252  * quote_identifier()
2253  * Properly double-quote a SQL identifier.
2254  * Copied from PostgreSQL pg_upgrade/util.c
2255  */
2256 char *
2257 quote_identifier(const char *s)
2258 {
2259  char *result = malloc(strlen(s) * 2 + 3);
2260  char *r = result;
2261 
2262  *r++ = '"';
2263  while (*s)
2264  {
2265  if (*s == '"')
2266  *r++ = *s;
2267  *r++ = *s;
2268  s++;
2269  }
2270  *r++ = '"';
2271  *r++ = '\0';
2272 
2273  return result;
2274 }
char * s
Definition: cu_in_wkt.c:23
char * r
Definition: cu_in_wkt.c:24
char result[OUT_DOUBLE_BUFFER_SIZE]
Definition: cu_print.c:267
int SHPAPI_CALL DBFAddField(DBFHandle psDBF, const char *pszFieldName, DBFFieldType eType, int nWidth, int nDecimals)
Definition: dbfopen.c:788
DBFHandle SHPAPI_CALL DBFCreateEx(const char *pszFilename, const char *pszCodePage)
Definition: dbfopen.c:662
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
Definition: dbfopen.c:599
void SHPAPI_CALL DBFSetWriteEndOfFileChar(DBFHandle psDBF, int bWriteFlag)
Definition: dbfopen.c:2297
int SHPAPI_CALL DBFWriteAttributeDirectly(DBFHandle psDBF, int hEntity, int iField, void *pValue)
Definition: dbfopen.c:1437
void deparse_hex(uint8_t str, char *result)
Convert a char into a human readable hex digit.
Definition: lwgeom_api.c:618
LWLINE * lwgeom_as_lwline(const LWGEOM *lwgeom)
Definition: lwgeom.c:162
POINT4D getPoint4d(const POINTARRAY *pa, uint32_t n)
Definition: lwgeom_api.c:108
LWGEOM * lwgeom_from_hexwkb(const char *hexwkb, const char check)
Definition: lwin_wkb.c:858
LWMPOINT * lwgeom_as_lwmpoint(const LWGEOM *lwgeom)
Definition: lwgeom.c:225
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1138
#define LW_PARSER_CHECK_NONE
Definition: liblwgeom.h:2085
#define MULTILINETYPE
Definition: liblwgeom.h:120
#define LINETYPE
Definition: liblwgeom.h:117
#define MULTIPOINTTYPE
Definition: liblwgeom.h:119
LWMPOLY * lwgeom_as_lwmpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:243
int lwtype_is_collection(uint8_t type)
Determine whether a type number is a collection or not.
Definition: lwgeom.c:1088
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:116
int geometry_type_from_string(const char *str, uint8_t *type, int *z, int *m)
Utility function to get type number from string.
Definition: lwutil.c:489
LWMLINE * lwgeom_as_lwmline(const LWGEOM *lwgeom)
Definition: lwgeom.c:234
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:121
#define POLYGONTYPE
Definition: liblwgeom.h:118
const char * lwtype_name(uint8_t type)
Return the type name string associated with a type number (e.g.
Definition: lwutil.c:216
LWPOLY * lwgeom_as_lwpoly(const LWGEOM *lwgeom)
Definition: lwgeom.c:198
#define LWDEBUG(level, msg)
Definition: lwgeom_log.h:83
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
void * malloc(YYSIZE_T)
void free(void *)
static int lwgeom_is_empty(const LWGEOM *geom)
Return true or false depending on whether a geometry is an "empty" geometry (no vertices members)
Definition: lwinline.h:203
static LWPOINT * lwgeom_as_lwpoint(const LWGEOM *lwgeom)
Definition: lwinline.h:131
type
Definition: ovdump.py:42
tuple res
Definition: window.py:79
void set_dumper_config_defaults(SHPDUMPERCONFIG *config)
static int getMaxFieldSize(PGconn *conn, char *schema, char *table, char *fname)
static SHPObject * create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon)
int ShpDumperGetRecordCount(SHPDUMPERSTATE *state)
static SHPObject * create_multipolygon(SHPDUMPERSTATE *state, LWMPOLY *lwmultipolygon)
void ShpDumperDestroy(SHPDUMPERSTATE *state)
char * convert_bytes_to_hex(uint8_t *ewkb, size_t size)
Binary to hexewkb conversion function.
char * shapetypename(int num)
SHPDUMPERSTATE * ShpDumperCreate(SHPDUMPERCONFIG *config)
static SHPObject * create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring)
static int getTableInfo(SHPDUMPERSTATE *state)
int ShpDumperConnectDatabase(SHPDUMPERSTATE *state)
static int projFileCreate(SHPDUMPERSTATE *state)
Creates ESRI .prj file for this shp output It looks in the spatial_ref_sys table and outputs the srte...
int ShpLoaderGenerateShapeRow(SHPDUMPERSTATE *state)
static int reverse_points(int num_points, double *x, double *y, double *z, double *m)
static char * goodDBFValue(char *in, char fieldType)
Make appropriate formatting of a DBF value based on type.
static SHPObject * create_point(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
int ShpDumperCloseTable(SHPDUMPERSTATE *state)
static int is_clockwise(int num_points, double *x, double *y, double *z)
char * quote_identifier(const char *s)
int ShpDumperOpenTable(SHPDUMPERSTATE *state)
static SHPObject * create_point_empty(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
static char * nullDBFValue(char fieldType)
char * ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
#define MAX_DBF_FIELD_SIZE
static SHPObject * create_multipoint(SHPDUMPERSTATE *state, LWMPOINT *lwmultipoint)
static SHPObject * create_multilinestring(SHPDUMPERSTATE *state, LWMLINE *lwmultilinestring)
#define SHPDUMPERMSGLEN
#define SHPDUMPEROK
#define SHPDUMPERERR
#define SHPDUMPERWARN
#define SHPT_ARCZ
Definition: shapefil.h:354
SHPHandle SHPAPI_CALL SHPCreate(const char *pszShapeFile, int nShapeType)
Definition: shpopen.c:981
@ FTDouble
Definition: shapefil.h:641
@ FTString
Definition: shapefil.h:639
@ FTLogical
Definition: shapefil.h:642
@ FTDate
Definition: shapefil.h:643
@ FTInteger
Definition: shapefil.h:640
#define SHPT_MULTIPATCH
Definition: shapefil.h:361
#define SHPT_NULL
Definition: shapefil.h:348
#define SHPT_ARCM
Definition: shapefil.h:358
#define SHPT_POLYGONM
Definition: shapefil.h:359
#define SHPT_ARC
Definition: shapefil.h:350
void SHPAPI_CALL SHPClose(SHPHandle hSHP)
Definition: shpopen.c:881
#define SHPT_POLYGON
Definition: shapefil.h:351
void SHPAPI_CALL SHPDestroyObject(SHPObject *psObject)
Definition: shpopen.c:2643
#define SHPT_MULTIPOINT
Definition: shapefil.h:352
#define SHPT_POINTZ
Definition: shapefil.h:353
#define SHPT_MULTIPOINTZ
Definition: shapefil.h:356
#define SHPT_MULTIPOINTM
Definition: shapefil.h:360
#define SHPT_POINTM
Definition: shapefil.h:357
#define SHPT_POINT
Definition: shapefil.h:349
#define SHPT_POLYGONZ
Definition: shapefil.h:355
SHPObject SHPAPI_CALL1 * SHPCreateObject(int nSHPType, int nShapeId, int nParts, const int *panPartStart, const int *panPartType, int nVertices, const double *padfX, const double *padfY, const double *padfZ, const double *padfM);SHPObject SHPAPI_CALL1(*) SHPCreateSimpleObject(int nSHPType, int nVertices, const double *padfX, const double *padfY, const double *padfZ
static SHPCONNECTIONCONFIG * conn
char * encoding2codepage(const char *encoding)
Definition: shpcommon.c: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, SHPLIB_NULLPTR, SHPLIB_NULLPTR, nVertices, padfX, padfY, padfZ, SHPLIB_NULLPTR)
int SHPAPI_CALL SHPWriteObject(SHPHandle psSHP, int nShapeId, SHPObject *psObject)
Definition: shpopen.c:1336
uint8_t type
Definition: liblwgeom.h:476
POINTARRAY * points
Definition: liblwgeom.h:497
LWLINE ** geoms
Definition: liblwgeom.h:561
uint32_t ngeoms
Definition: liblwgeom.h:566
uint32_t ngeoms
Definition: liblwgeom.h:552
LWPOINT ** geoms
Definition: liblwgeom.h:547
uint32_t ngeoms
Definition: liblwgeom.h:580
LWPOLY ** geoms
Definition: liblwgeom.h:575
POINTARRAY * point
Definition: liblwgeom.h:485
POINTARRAY ** rings
Definition: liblwgeom.h:533
uint32_t nrings
Definition: liblwgeom.h:538
double m
Definition: liblwgeom.h:428
double x
Definition: liblwgeom.h:428
double z
Definition: liblwgeom.h:428
double y
Definition: liblwgeom.h:428
uint32_t npoints
Definition: liblwgeom.h:441
char * column_map_filename
SHPCONNECTIONCONFIG * conn
SHPDUMPERCONFIG * config
PGresult * fetchres
char message[SHPDUMPERMSGLEN]