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