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