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