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