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