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