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 
59 /**
60  * @brief Make appropriate formatting of a DBF value based on type.
61  * Might return untouched input or pointer to static private
62  * buffer: use return value right away.
63  */
64 static char * goodDBFValue(char *in, char fieldType);
65 
66 /** @brief Binary to hexewkb conversion function */
67 char *convert_bytes_to_hex(uint8_t *ewkb, size_t size);
68 
69 static SHPObject *
create_point_empty(SHPDUMPERSTATE * state,LWPOINT * lwpoint)70 create_point_empty(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
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 *
create_point(SHPDUMPERSTATE * state,LWPOINT * lwpoint)82 create_point(SHPDUMPERSTATE *state, LWPOINT *lwpoint)
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 *
create_multipoint(SHPDUMPERSTATE * state,LWMPOINT * lwmultipoint)117 create_multipoint(SHPDUMPERSTATE *state, LWMPOINT *lwmultipoint)
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 *
create_polygon(SHPDUMPERSTATE * state,LWPOLY * lwpolygon)156 create_polygon(SHPDUMPERSTATE *state, LWPOLY *lwpolygon)
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 *
create_multipolygon(SHPDUMPERSTATE * state,LWMPOLY * lwmultipolygon)244 create_multipolygon(SHPDUMPERSTATE *state, LWMPOLY *lwmultipolygon)
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 *
create_linestring(SHPDUMPERSTATE * state,LWLINE * lwlinestring)348 create_linestring(SHPDUMPERSTATE *state, LWLINE *lwlinestring)
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 *
create_multilinestring(SHPDUMPERSTATE * state,LWMLINE * lwmultilinestring)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
reverse_points(int num_points,double * x,double * y,double * z,double * m)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
is_clockwise(int num_points,double * x,double * y,double * z)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
getMaxFieldSize(PGconn * conn,char * schema,char * table,char * fname)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 *
shapetypename(int num)580 shapetypename(int num)
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 *
nullDBFValue(char fieldType)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 
643 /**
644  * @brief Make appropriate formatting of a DBF value based on type.
645  * 		Might return untouched input or pointer to static private
646  * 		buffer: use return value right away.
647  */
648 static char *
goodDBFValue(char * in,char fieldType)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 
convert_bytes_to_hex(uint8_t * ewkb,size_t size)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 
694 /**
695  * @brief Creates ESRI .prj file for this shp output
696  * 		It looks in the spatial_ref_sys table and outputs the srtext field for this data
697  * 		If data is a table will use geometry_columns, if a query or view will read SRID from query output.
698  *	@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.
699  */
700 static int
projFileCreate(SHPDUMPERSTATE * state)701 projFileCreate(SHPDUMPERSTATE *state)
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 
736 	/** make our address space large enough to hold query with table/schema **/
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
getTableInfo(SHPDUMPERSTATE * state)858 getTableInfo(SHPDUMPERSTATE *state)
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
set_dumper_config_defaults(SHPDUMPERCONFIG * config)1126 set_dumper_config_defaults(SHPDUMPERCONFIG *config)
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 	config->quiet = 0;
1148 }
1149 
1150 /* Create a new shapefile state object */
1151 SHPDUMPERSTATE *
ShpDumperCreate(SHPDUMPERCONFIG * config)1152 ShpDumperCreate(SHPDUMPERCONFIG *config)
1153 {
1154 	SHPDUMPERSTATE *state;
1155 
1156 	/* Create a new state object and assign the config to it */
1157 	state = malloc(sizeof(SHPDUMPERSTATE));
1158 	state->config = config;
1159 
1160 	/* Set any state defaults */
1161 	state->conn = NULL;
1162 	state->outtype = 's';
1163 	state->outshptype = 0;
1164 	state->geom_oid = 0;
1165 	state->geog_oid = 0;
1166 	state->schema = NULL;
1167 	state->table = NULL;
1168 	state->geo_col_name = NULL;
1169 	state->fetch_query = NULL;
1170 	state->main_scan_query = NULL;
1171 	state->dbffieldnames = NULL;
1172 	state->dbffieldtypes = NULL;
1173 	state->pgfieldnames = NULL;
1174 	state->message[0] = '\0';
1175 	colmap_init(&state->column_map);
1176 
1177 	return state;
1178 }
1179 
1180 /* Generate the database connection string used by a state */
1181 char *
ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG * conn)1182 ShpDumperGetConnectionStringFromConn(SHPCONNECTIONCONFIG *conn)
1183 {
1184 	char *connstring;
1185 	int connlen;
1186 
1187 	connlen = 64 +
1188 		(conn->host ? strlen(conn->host) : 0) + (conn->port ? strlen(conn->port) : 0) +
1189 		(conn->username ? strlen(conn->username) : 0) + (conn->password ? strlen(conn->password) : 0) +
1190 		(conn->database ? strlen(conn->database) : 0);
1191 
1192 	connstring = malloc(connlen);
1193 	memset(connstring, 0, connlen);
1194 
1195 	if (conn->host)
1196 	{
1197 		strcat(connstring, " host=");
1198 		strcat(connstring, conn->host);
1199 	}
1200 
1201 	if (conn->port)
1202 	{
1203 		strcat(connstring, " port=");
1204 		strcat(connstring, conn->port);
1205 	}
1206 
1207 	if (conn->username)
1208 	{
1209 		strcat(connstring, " user=");
1210 		strcat(connstring, conn->username);
1211 	}
1212 
1213 	if (conn->password)
1214 	{
1215 		strcat(connstring, " password='");
1216 		strcat(connstring, conn->password);
1217 		strcat(connstring, "'");
1218 	}
1219 
1220 	if (conn->database)
1221 	{
1222 		strcat(connstring, " dbname=");
1223 		strcat(connstring, conn->database);
1224 	}
1225 
1226 	if ( ! getenv("PGCLIENTENCODING") )
1227 	{
1228 		strcat(connstring, " client_encoding=UTF8");
1229 	}
1230 
1231 	return connstring;
1232 }
1233 
1234 /* Connect to the database and identify the version of PostGIS (and any other
1235 capabilities required) */
1236 int
ShpDumperConnectDatabase(SHPDUMPERSTATE * state)1237 ShpDumperConnectDatabase(SHPDUMPERSTATE *state)
1238 {
1239 	PGresult *res;
1240 
1241 	char *connstring, *tmpvalue;
1242 
1243 	/* Generate the PostgreSQL connection string */
1244 	connstring = ShpDumperGetConnectionStringFromConn(state->config->conn);
1245 
1246 	/* Connect to the database */
1247 	state->conn = PQconnectdb(connstring);
1248 	if (PQstatus(state->conn) == CONNECTION_BAD)
1249 	{
1250 		snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQerrorMessage(state->conn));
1251 		free(connstring);
1252 		return SHPDUMPERERR;
1253 	}
1254 
1255 	/* Set datestyle to ISO */
1256 	res = PQexec(state->conn, "SET DATESTYLE='ISO'");
1257 	if (PQresultStatus(res) != PGRES_COMMAND_OK)
1258 	{
1259 		snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1260 		PQclear(res);
1261 		free(connstring);
1262 		return SHPDUMPERERR;
1263 	}
1264 
1265 	PQclear(res);
1266 
1267 	/* Retrieve PostGIS major version */
1268 	res = PQexec(state->conn, "SELECT postgis_version()");
1269 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
1270 	{
1271 		snprintf(state->message, SHPDUMPERMSGLEN, "%s", PQresultErrorMessage(res));
1272 		PQclear(res);
1273 		free(connstring);
1274 		return SHPDUMPERERR;
1275 	}
1276 
1277 	tmpvalue = PQgetvalue(res, 0, 0);
1278 	state->pgis_major_version = atoi(tmpvalue);
1279 
1280 	PQclear(res);
1281 
1282 	/* Find the OID for the geometry type */
1283 	res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geometry'");
1284 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
1285 	{
1286 		snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geometry oid: %s"), PQresultErrorMessage(res));
1287 		PQclear(res);
1288 		free(connstring);
1289 		return SHPDUMPERERR;
1290 	}
1291 
1292 	if (PQntuples(res) > 0)
1293 	{
1294 		tmpvalue = PQgetvalue(res, 0, 0);
1295 		state->geom_oid = atoi(tmpvalue);
1296 	}
1297 	else
1298 	{
1299 		snprintf(state->message, SHPDUMPERMSGLEN, _("Geometry type unknown (have you enabled postgis?)"));
1300 		PQclear(res);
1301 		free(connstring);
1302 		return SHPDUMPERERR;
1303 	}
1304 
1305 	PQclear(res);
1306 
1307 	/* Find the OID for the geography type */
1308 	res = PQexec(state->conn, "SELECT oid FROM pg_type WHERE typname = 'geography'");
1309 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
1310 	{
1311 		snprintf(state->message, SHPDUMPERMSGLEN, _("Error looking up geography oid: %s"), PQresultErrorMessage(res));
1312 		PQclear(res);
1313 		free(connstring);
1314 		return SHPDUMPERERR;
1315 	}
1316 
1317 	if (PQntuples(res) > 0)
1318 	{
1319 		/* Old databases don't have a geography type, so don't fail if we don't find it */
1320 		tmpvalue = PQgetvalue(res, 0, 0);
1321 		state->geog_oid = atoi(tmpvalue);
1322 	}
1323 
1324 	PQclear(res);
1325 
1326 	free(connstring);
1327 
1328 	return SHPDUMPEROK;
1329 }
1330 
1331 
1332 /* Open the specified table in preparation for extracting rows */
1333 int
ShpDumperOpenTable(SHPDUMPERSTATE * state)1334 ShpDumperOpenTable(SHPDUMPERSTATE *state)
1335 {
1336 	PGresult *res;
1337 
1338 	char buf[256];
1339 	char *query;
1340 	int gidfound = 0, i, j, ret, status;
1341 
1342 
1343 	/* Open the column map if one was specified */
1344 	if (state->config->column_map_filename)
1345 	{
1346 		ret = colmap_read(state->config->column_map_filename,
1347 		                  &state->column_map, state->message, SHPDUMPERMSGLEN);
1348 		if (!ret) return SHPDUMPERERR;
1349 	}
1350 
1351 	/* If a user-defined query has been specified, create and point the state to our new table */
1352 	if (state->config->usrquery)
1353 	{
1354 		state->table = malloc(20 + 20);		/* string + max long precision */
1355 		sprintf(state->table, "__pgsql2shp%lu_tmp_table", (long)getpid());
1356 
1357 		query = malloc(32 + strlen(state->table) + strlen(state->config->usrquery));
1358 
1359 		sprintf(query, "CREATE TEMP TABLE \"%s\" AS %s", state->table, state->config->usrquery);
1360 		res = PQexec(state->conn, query);
1361 		free(query);
1362 
1363 		/* Execute the code to create the table */
1364 		if (PQresultStatus(res) != PGRES_COMMAND_OK)
1365 		{
1366 			snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing user query: %s"), PQresultErrorMessage(res));
1367 			PQclear(res);
1368 			return SHPDUMPERERR;
1369 		}
1370 	}
1371 	else
1372 	{
1373 		/* Simply point the state to copies of the supplied schema and table */
1374 		state->table = strdup(state->config->table);
1375 		if (state->config->schema)
1376 			state->schema = strdup(state->config->schema);
1377 	}
1378 
1379 
1380 	/* Get the list of columns and their types for the selected table */
1381 	if (state->schema)
1382 	{
1383 		query = malloc(250 + strlen(state->schema) + strlen(state->table));
1384 
1385 		sprintf(query, "SELECT a.attname, a.atttypid, "
1386 		        "a.atttypmod, a.attlen FROM "
1387 		        "pg_attribute a, pg_class c, pg_namespace n WHERE "
1388 		        "n.nspname = '%s' AND a.attrelid = c.oid AND "
1389 		        "n.oid = c.relnamespace AND "
1390 		        "a.atttypid != 0 AND "
1391 		        "a.attnum > 0 AND c.relname = '%s'", state->schema, state->table);
1392 	}
1393 	else
1394 	{
1395 		query = malloc(250 + strlen(state->table));
1396 
1397 		sprintf(query, "SELECT a.attname, a.atttypid, "
1398 		        "a.atttypmod, a.attlen FROM "
1399 		        "pg_attribute a, pg_class c WHERE "
1400 		        "a.attrelid = c.oid and a.attnum > 0 AND "
1401 		        "a.atttypid != 0 AND "
1402 		        "c.relname = '%s' AND "
1403 		        "pg_catalog.pg_table_is_visible(c.oid)", state->table);
1404 	}
1405 
1406 	LWDEBUGF(3, "query is: %s\n", query);
1407 
1408 	res = PQexec(state->conn, query);
1409 	free(query);
1410 
1411 	if (PQresultStatus(res) != PGRES_TUPLES_OK)
1412 	{
1413 		snprintf(state->message, SHPDUMPERMSGLEN, _("Error querying for attributes: %s"), PQresultErrorMessage(res));
1414 		PQclear(res);
1415 		return SHPDUMPERERR;
1416 	}
1417 
1418 	if (!PQntuples(res))
1419 	{
1420 		snprintf(state->message, SHPDUMPERMSGLEN, _("Table %s does not exist"), state->table);
1421 		PQclear(res);
1422 		return SHPDUMPERERR;
1423 	}
1424 
1425 	/* If a shapefile name was specified, use it. Otherwise simply use the table name. */
1426 	if (state->config->shp_file != NULL)
1427 		state->shp_file = state->config->shp_file;
1428 	else
1429 		state->shp_file = state->table;
1430 
1431 	/* Create the dbf file: */
1432 	/* If there's a user-specified encoding hanging around, try and use that. */
1433 	/* Otherwise, just use UTF-8 encoding, since that's usually our client encoding. */
1434 	if ( getenv("PGCLIENTENCODING") )
1435 	{
1436 		char *codepage = encoding2codepage(getenv("PGCLIENTENCODING"));
1437 		state->dbf = DBFCreateEx(state->shp_file, codepage);
1438 	}
1439 	else
1440 	{
1441 		state->dbf = DBFCreateEx(state->shp_file, "UTF-8");
1442 	}
1443 
1444 	if (!state->dbf)
1445 	{
1446 		snprintf(state->message, SHPDUMPERMSGLEN, _("Could not create dbf file %s"), state->shp_file);
1447 		return SHPDUMPERERR;
1448 	}
1449 
1450 	/* Mimic old behaviour and skip the EOF character (1A) */
1451 	DBFSetWriteEndOfFileChar(state->dbf, 0);
1452 
1453 	/*
1454 	 * Scan the result setting fields to be returned in mainscan
1455 	 * query, filling the type_ary, and creating .dbf and .shp files.
1456 	 */
1457 	state->dbffieldnames = malloc(sizeof(char *) * PQntuples(res));
1458 	state->dbffieldtypes = malloc(sizeof(int) * PQntuples(res));
1459 	state->pgfieldnames = malloc(sizeof(char *) * PQntuples(res));
1460 	state->pgfieldlens = malloc(sizeof(int) * PQntuples(res));
1461 	state->pgfieldtypmods = malloc(sizeof(int) * PQntuples(res));
1462 	state->fieldcount = 0;
1463 	int tmpint = 1;
1464 
1465 	for (i = 0; i < PQntuples(res); i++)
1466 	{
1467 		char *ptr;
1468 
1469 		int pgfieldtype, pgtypmod, pgfieldlen;
1470 		char *pgfieldname;
1471 
1472 		int dbffieldtype, dbffieldsize, dbffielddecs;
1473 		char *dbffieldname;
1474 
1475 		pgfieldname = PQgetvalue(res, i, 0);
1476 		pgfieldtype = atoi(PQgetvalue(res, i, 1));
1477 		pgtypmod = atoi(PQgetvalue(res, i, 2));
1478 		pgfieldlen = atoi(PQgetvalue(res, i, 3));
1479 		dbffieldtype = -1;
1480 		dbffieldsize = 0;
1481 		dbffielddecs = 0;
1482 
1483 		/*
1484 		 * This is a geometry/geography column
1485 		 */
1486 		if (pgfieldtype == state->geom_oid || pgfieldtype == state->geog_oid)
1487 		{
1488 			/* If no geometry/geography column has been found yet... */
1489 			if (!state->geo_col_name)
1490 			{
1491 				/* If either no geo* column name was provided (in which case this is
1492 				   the first match) or we match the provided column name, we have
1493 				   found our geo* column */
1494 				if (!state->config->geo_col_name || !strcmp(state->config->geo_col_name, pgfieldname))
1495 				{
1496 					dbffieldtype = 9;
1497 
1498 					state->geo_col_name = strdup(pgfieldname);
1499 				}
1500 			}
1501 		}
1502 
1503 		/*
1504 		 * Everything else (non geometries) will be
1505 		 * a DBF attribute.
1506 		 */
1507 
1508 		/* Skip gid (if not asked to do otherwise */
1509 		if (!strcmp(pgfieldname, "gid") )
1510 		{
1511 			gidfound = 1;
1512 
1513 			if (!state->config->includegid)
1514 				continue;
1515 		}
1516 
1517 		/* Unescape any reserved column names */
1518 		ptr = pgfieldname;
1519 		if (!state->config->unescapedattrs)
1520 		{
1521 			if (*ptr == '_')
1522 				ptr += 2;
1523 		}
1524 
1525 		/*
1526 		 * This needs special handling since both xmin and _xmin
1527 		 * becomes __xmin when escaped
1528 		 */
1529 
1530 		/* Limit dbf field name to 10-digits */
1531 		dbffieldname = malloc(11);
1532 		strncpy(dbffieldname, ptr, 10);
1533 		dbffieldname[10] = '\0';
1534 
1535 		/* If a column map file has been passed in,
1536 		 * use this to create the dbf field name from
1537 		 * the PostgreSQL column name */
1538 		{
1539 			const char *mapped = colmap_dbf_by_pg(&state->column_map, pgfieldname);
1540 			if (mapped)
1541 			{
1542 				strncpy(dbffieldname, mapped, 10);
1543 				dbffieldname[10] = '\0';
1544 			}
1545 		}
1546 
1547 		/*
1548 		 * make sure the fields all have unique names,
1549 		 */
1550 		tmpint = 1;
1551 		for (j = 0; j < state->fieldcount; j++)
1552 		{
1553 			if (!strncasecmp(dbffieldname, state->dbffieldnames[j], 10))
1554 			{
1555 				sprintf(dbffieldname, "%.7s_%.2d", ptr, abs(tmpint) % 100);
1556 				tmpint++;
1557 				continue;
1558 			}
1559 		}
1560 
1561 		/* make UPPERCASE if keep_fieldname_case = 0 */
1562 		if (!state->config->keep_fieldname_case)
1563 		{
1564 			size_t nameit;
1565 			for (nameit = 0; nameit < strlen(dbffieldname); nameit++)
1566 				dbffieldname[nameit] = toupper(dbffieldname[nameit]);
1567 		}
1568 
1569 		/* Issue warning if column has been renamed */
1570 		if (strcasecmp(dbffieldname, pgfieldname))
1571 		{
1572 			if ( snprintf(buf, 256, _("Warning, field %s renamed to %s\n"),
1573 			              pgfieldname, dbffieldname) >= 256 )
1574 			{
1575 				buf[255] = '\0';
1576 			}
1577 			/* Note: we concatenate all warnings from the main loop as this is useful information */
1578 			strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message) - 1);
1579 
1580 			ret = SHPDUMPERWARN;
1581 		}
1582 
1583 
1584 		/*
1585 		 * Find appropriate type of dbf attributes
1586 		 */
1587 
1588 		/* int2 type */
1589 		if (pgfieldtype == 21)
1590 		{
1591 			/*
1592 			 * Longest text representation for
1593 			 * an int2 type (16bit) is 6 bytes
1594 			 * (-32768)
1595 			 */
1596 			dbffieldtype = FTInteger;
1597 			dbffieldsize = 6;
1598 			dbffielddecs = 0;
1599 		}
1600 
1601 		/* int4 type */
1602 		else if (pgfieldtype == 23)
1603 		{
1604 			/*
1605 			 * Longest text representation for
1606 			 * an int4 type (32bit) is 11 bytes
1607 			 * (-2147483648)
1608 			 */
1609 			dbffieldtype = FTInteger;
1610 			dbffieldsize = 11;
1611 			dbffielddecs = 0;
1612 		}
1613 
1614 		/* int8 type */
1615 		else if (pgfieldtype == 20)
1616 		{
1617 			/*
1618 			 * Longest text representation for
1619 			 * an int8 type (64bit) is 20 bytes
1620 			 * (-9223372036854775808)
1621 			 */
1622 			dbffieldtype = FTInteger;
1623 			dbffieldsize = 19;
1624 			dbffielddecs = 0;
1625 		}
1626 
1627 		/*
1628 		 * double or numeric types:
1629 		 *    700: float4
1630 		 *    701: float8
1631 		 *   1700: numeric
1632 		 *
1633 		 *
1634 		 * TODO: stricter handling of sizes
1635 		 */
1636 		else if (pgfieldtype == 700 || pgfieldtype == 701 || pgfieldtype == 1700)
1637 		{
1638 			dbffieldtype = FTDouble;
1639 			dbffieldsize = 32;
1640 			dbffielddecs = 10;
1641 		}
1642 
1643 		/*
1644 		 * Boolean field, we use FTLogical
1645 		 */
1646 		else if (pgfieldtype == 16)
1647 		{
1648 			dbffieldtype = FTLogical;
1649 			dbffieldsize = 1;
1650 			dbffielddecs = 0;
1651 		}
1652 
1653 		/*
1654 		 * Date field
1655 		 */
1656 		else if (pgfieldtype == 1082)
1657 		{
1658 			dbffieldtype = FTDate;
1659 			dbffieldsize = 8;
1660 			dbffielddecs = 0;
1661 		}
1662 
1663 		/*
1664 		 * time, timetz, timestamp, or timestamptz field.
1665 		 */
1666 		else if (pgfieldtype == 1083 || pgfieldtype == 1266 || pgfieldtype == 1114 || pgfieldtype == 1184)
1667 		{
1668 			int secondsize;
1669 
1670 			switch (pgtypmod)
1671 			{
1672 			case -1:
1673 				secondsize = 6 + 1;
1674 				break;
1675 			case 0:
1676 				secondsize = 0;
1677 				break;
1678 			default:
1679 				secondsize = pgtypmod + 1;
1680 				break;
1681 			}
1682 
1683 			/* We assume the worst case scenario for all of these:
1684 			 * date = '5874897-12-31' = 13
1685 			 * date = '294276-11-20' = 12 (with --enable-interger-datetimes)
1686 			 * time = '00:00:00' = 8
1687 			 * zone = '+01:39:52' = 9 (see Europe/Helsinki around 1915)
1688 			 */
1689 
1690 			/* time */
1691 			if (pgfieldtype == 1083)
1692 			{
1693 				dbffieldsize = 8 + secondsize;
1694 			}
1695 			/* timetz */
1696 			else if (pgfieldtype == 1266)
1697 			{
1698 				dbffieldsize = 8 + secondsize + 9;
1699 			}
1700 			/* timestamp */
1701 			else if (pgfieldtype == 1114)
1702 			{
1703 				dbffieldsize = 13 + 1 + 8 + secondsize;
1704 			}
1705 			/* timestamptz */
1706 			else if (pgfieldtype == 1184)
1707 			{
1708 				dbffieldsize = 13 + 1 + 8 + secondsize + 9;
1709 			}
1710 
1711 			dbffieldtype = FTString;
1712 			dbffielddecs = 0;
1713 		}
1714 
1715 		/*
1716 		 * uuid type 36 bytes (12345678-9012-3456-7890-123456789012)
1717 		 */
1718 		else if (pgfieldtype == 2950)
1719 		{
1720 			dbffieldtype = FTString;
1721 			dbffieldsize = 36;
1722 			dbffielddecs = 0;
1723 		}
1724 
1725 		/*
1726 		 * For variable-sized fields we know about, we use
1727 		 * the maximum allowed size.
1728 		  * 1042 is bpchar,  1043 is varchar
1729 		 */
1730 		else if ((pgfieldtype == 1042 || pgfieldtype == 1043) && pgtypmod != -1)
1731 		{
1732 			/*
1733 			 * mod is maximum allowed size, including
1734 			 * header which contains *real* size.
1735 			 */
1736 			dbffieldtype = FTString;
1737 			dbffieldsize = pgtypmod - 4; /* 4 is header size */
1738 			dbffielddecs = 0;
1739 		}
1740 
1741 		/* For all other valid non-geometry/geography fields... */
1742 		else if (dbffieldtype == -1)
1743 		{
1744 			/*
1745 			* For types we don't know anything about, all
1746 			* we can do is query the table for the maximum field
1747 			* size.
1748 			*/
1749 			dbffieldsize = getMaxFieldSize(state->conn, state->schema, state->table, pgfieldname);
1750 			if (dbffieldsize == -1)
1751 			{
1752 				free(dbffieldname);
1753 				return 0;
1754 			}
1755 
1756 			if (!dbffieldsize)
1757 				dbffieldsize = 32;
1758 
1759 			/* might 0 be a good size ? */
1760 
1761 			dbffieldtype = FTString;
1762 			dbffielddecs = 0;
1763 
1764 			/* Check to make sure the final field size isn't too large */
1765 			if (dbffieldsize > MAX_DBF_FIELD_SIZE)
1766 			{
1767 				/* Note: we concatenate all warnings from the main loop as this is useful information */
1768 				snprintf(buf, 256, _("Warning: values of field '%s' exceeding maximum dbf field width (%d) "
1769 					"will be truncated.\n"), dbffieldname, MAX_DBF_FIELD_SIZE);
1770 				strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1771 				dbffieldsize = MAX_DBF_FIELD_SIZE;
1772 
1773 				ret = SHPDUMPERWARN;
1774 			}
1775 		}
1776 
1777 		LWDEBUGF(3, "DBF FIELD_NAME: %s, SIZE: %d\n", dbffieldname, dbffieldsize);
1778 
1779 		if (dbffieldtype != 9)
1780 		{
1781 			/* Add the field to the DBF file */
1782 			if (DBFAddField(state->dbf, dbffieldname, dbffieldtype, dbffieldsize, dbffielddecs) == -1)
1783 			{
1784 				snprintf(state->message, SHPDUMPERMSGLEN, _("Error: field %s of type %d could not be created."), dbffieldname, dbffieldtype);
1785 
1786 				return SHPDUMPERERR;
1787 			}
1788 
1789 			/* Add the field information to our field arrays */
1790 			state->dbffieldnames[state->fieldcount] = dbffieldname;
1791 			state->dbffieldtypes[state->fieldcount] = dbffieldtype;
1792 			state->pgfieldnames[state->fieldcount] = pgfieldname;
1793 			state->pgfieldlens[state->fieldcount] = pgfieldlen;
1794 			state->pgfieldtypmods[state->fieldcount] = pgtypmod;
1795 
1796 			state->fieldcount++;
1797 		}
1798 	}
1799 
1800 	/* Now we have generated the field lists, grab some info about the table */
1801 	status = getTableInfo(state);
1802 	if (status == SHPDUMPERERR)
1803 		return SHPDUMPERERR;
1804 
1805 	LWDEBUGF(3, "rows: %d\n", state->rowcount);
1806 	LWDEBUGF(3, "shptype: %c\n", state->outtype);
1807 	LWDEBUGF(3, "shpouttype: %d\n", state->outshptype);
1808 
1809 	/* If we didn't find a geometry/geography column... */
1810 	if (!state->geo_col_name)
1811 	{
1812 		if (state->config->geo_col_name)
1813 		{
1814 			/* A geo* column was specified, but not found */
1815 			snprintf(state->message, SHPDUMPERMSGLEN, _("%s: no such attribute in table %s"), state->config->geo_col_name, state->table);
1816 
1817 			return SHPDUMPERERR;
1818 		}
1819 		else
1820 		{
1821 			/* No geo* column specified so we can only create the DBF section -
1822 			   but let's issue a warning... */
1823 			snprintf(buf, 256, _("No geometry column found.\nThe DBF file will be created but not the shx or shp files.\n"));
1824 			strncat(state->message, buf, SHPDUMPERMSGLEN - strlen(state->message));
1825 
1826 			state->shp = NULL;
1827 
1828 			ret = SHPDUMPERWARN;
1829 		}
1830 	}
1831 	else
1832 	{
1833 		/* Since we have found a geo* column, open the shapefile */
1834 		state->shp = SHPCreate(state->shp_file, state->outshptype);
1835 		if (!state->shp)
1836 		{
1837 			snprintf(state->message, SHPDUMPERMSGLEN, _("Could not open shapefile %s!"), state->shp_file);
1838 
1839 			return SHPDUMPERERR;
1840 		}
1841 	}
1842 
1843 
1844 	/* Now we have the complete list of fieldnames, let's generate the SQL query. First let's make sure
1845 	   we reserve enough space for tables with lots of columns */
1846 	j = 0;
1847 	/*TODO: this really should be rewritten to use stringbuffer */
1848 	for (i = 0; i < state->fieldcount; i++)
1849 		j += strlen( state->pgfieldnames[i]) + 10;	/*add extra space for the quotes to quote identify and any embedded quotes that may need escaping */
1850 
1851 	state->main_scan_query = malloc(1024 + j);
1852 
1853 	sprintf(state->main_scan_query, "DECLARE cur ");
1854 	if (state->config->binary)
1855 		strcat(state->main_scan_query, "BINARY ");
1856 
1857 	strcat(state->main_scan_query, "CURSOR FOR SELECT ");
1858 
1859 	for (i = 0; i < state->fieldcount; i++)
1860 	{
1861 		/* Comma-separated column names */
1862 		if (i > 0)
1863 			strcat(state->main_scan_query, ",");
1864 
1865 		if (state->config->binary)
1866 			sprintf(buf, "%s::text", quote_identifier(state->pgfieldnames[i]) ) ;
1867 		else
1868 			sprintf(buf, "%s", quote_identifier(state->pgfieldnames[i]) );
1869 
1870 		strcat(state->main_scan_query, buf);
1871 	}
1872 
1873 	/* If we found a valid geometry/geography column then use it */
1874 	if (state->geo_col_name)
1875 	{
1876 		/* If this is the (only) column, no need for the initial comma */
1877 		if (state->fieldcount > 0)
1878 			strcat(state->main_scan_query, ",");
1879 
1880 #ifdef WORDS_BIGENDIAN
1881 		if (state->pgis_major_version > 0)
1882 		{
1883 			sprintf(buf, "ST_asEWKB(ST_SetSRID(%s::geometry, 0), 'XDR') AS _geoX", quote_identifier(state->geo_col_name) );
1884 		}
1885 		else
1886 		{
1887 			sprintf(buf, "asbinary(%s::geometry, 'XDR') AS _geoX",
1888 			        quote_identifier(state->geo_col_name) );
1889 		}
1890 #else
1891 		if (state->pgis_major_version > 0)
1892 		{
1893 			sprintf(buf, "ST_AsEWKB(ST_SetSRID(%s::geometry, 0), 'NDR') AS _geoX", quote_identifier(state->geo_col_name) ) ;
1894 		}
1895 		else
1896 		{
1897 			sprintf(buf, "asbinary(%s::geometry, 'NDR') AS _geoX",
1898 			        quote_identifier(state->geo_col_name) );
1899 		}
1900 #endif
1901 
1902 		strcat(state->main_scan_query, buf);
1903 	}
1904 
1905 	if (state->schema)
1906 	{
1907 		sprintf(buf, " FROM \"%s\".\"%s\"", state->schema, state->table);
1908 	}
1909 	else
1910 	{
1911 		sprintf(buf, " FROM \"%s\"", state->table);
1912 	}
1913 
1914 	strcat(state->main_scan_query, buf);
1915 
1916 	/* Order by 'gid' (if found) */
1917 	if (gidfound)
1918 	{
1919 		sprintf(buf, " ORDER BY \"gid\"");
1920 		strcat(state->main_scan_query, buf);
1921 	}
1922 
1923 	/* Now we've finished with the result set, we can dispose of it */
1924 	PQclear(res);
1925 
1926 	LWDEBUGF(3, "FINAL QUERY: %s\n", state->main_scan_query);
1927 
1928 	/*
1929 	 * Begin the transaction
1930 	 * (a cursor can only be defined inside a transaction block)
1931 	 */
1932 	res = PQexec(state->conn, "BEGIN");
1933 	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1934 	{
1935 		snprintf(state->message, SHPDUMPERMSGLEN, _("Error starting transaction: %s"), PQresultErrorMessage(res));
1936 		PQclear(res);
1937 		return SHPDUMPERERR;
1938 	}
1939 
1940 	PQclear(res);
1941 
1942 	/* Execute the main scan query */
1943 	res = PQexec(state->conn, state->main_scan_query);
1944 	if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
1945 	{
1946 		snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing main scan query: %s"), PQresultErrorMessage(res));
1947 		PQclear(res);
1948 		return SHPDUMPERERR;
1949 	}
1950 
1951 	PQclear(res);
1952 
1953 	/* Setup initial scan state */
1954 	state->currow = 0;
1955 	state->curresrow = 0;
1956 	state->currescount = 0;
1957 	state->fetchres = NULL;
1958 
1959 	/* Generate the fetch query */
1960 	state->fetch_query = malloc(256);
1961 	sprintf(state->fetch_query, "FETCH %d FROM cur", state->config->fetchsize);
1962 
1963 	return SHPDUMPEROK;
1964 }
1965 
1966 
1967 /* Append the next row to the output shapefile */
ShpLoaderGenerateShapeRow(SHPDUMPERSTATE * state)1968 int ShpLoaderGenerateShapeRow(SHPDUMPERSTATE *state)
1969 {
1970 	char *hexewkb = NULL;
1971 	unsigned char *hexewkb_binary = NULL;
1972 	size_t hexewkb_len;
1973 	char *val;
1974 	SHPObject *obj = NULL;
1975 	LWGEOM *lwgeom;
1976 
1977 	int i, geocolnum = 0;
1978 
1979 	/* If we try to go pass the end of the table, fail immediately */
1980 	if (state->currow > state->rowcount)
1981 	{
1982 		snprintf(state->message, SHPDUMPERMSGLEN, _("Tried to read past end of table!"));
1983 		PQclear(state->fetchres);
1984 		return SHPDUMPERERR;
1985 	}
1986 
1987 	/* If we have reached the end of the current batch, fetch a new one */
1988 	if (state->curresrow == state->currescount && state->currow < state->rowcount)
1989 	{
1990 		/* Clear the previous batch results */
1991 		if (state->fetchres)
1992 			PQclear(state->fetchres);
1993 
1994 		state->fetchres = PQexec(state->conn, state->fetch_query);
1995 		if (PQresultStatus(state->fetchres) != PGRES_TUPLES_OK)
1996 		{
1997 			snprintf(state->message, SHPDUMPERMSGLEN, _("Error executing fetch query: %s"), PQresultErrorMessage(state->fetchres));
1998 			PQclear(state->fetchres);
1999 			return SHPDUMPERERR;
2000 		}
2001 
2002 		state->curresrow = 0;
2003 		state->currescount = PQntuples(state->fetchres);
2004 	}
2005 
2006 	/* Grab the id of the geo column if we have one */
2007 	if (state->geo_col_name)
2008 		geocolnum = PQfnumber(state->fetchres, "_geoX");
2009 
2010 	/* Process the next record within the batch. First write out all of
2011 	the non-geo fields */
2012 	for (i = 0; i < state->fieldcount; i++)
2013 	{
2014 		/*
2015 		* Transform NULL numbers to '0'
2016 		* This is because the shapelib
2017 		* won't easly take care of setting
2018 		* nulls unless paying the acquisition
2019 		* of a bug in long integer values
2020 		*/
2021 		if (PQgetisnull(state->fetchres, state->curresrow, i))
2022 		{
2023 			val = nullDBFValue(state->dbffieldtypes[i]);
2024 		}
2025 		else
2026 		{
2027 			val = PQgetvalue(state->fetchres, state->curresrow, i);
2028 			val = goodDBFValue(val, state->dbffieldtypes[i]);
2029 		}
2030 
2031 		/* Write it to the DBF file */
2032 		if (!DBFWriteAttributeDirectly(state->dbf, state->currow, i, val))
2033 		{
2034 			snprintf(state->message, SHPDUMPERMSGLEN, _("Error: record %d could not be created"), state->currow);
2035 			PQclear(state->fetchres);
2036 			return SHPDUMPERERR;
2037 		}
2038 	}
2039 
2040 	/* Now process the geo field, if present */
2041 	if (state->geo_col_name)
2042 	{
2043 		/* Handle NULL shapes */
2044 		if (PQgetisnull(state->fetchres, state->curresrow, geocolnum))
2045 		{
2046 			obj = SHPCreateSimpleObject(SHPT_NULL, 0, NULL, NULL, NULL);
2047 			if (SHPWriteObject(state->shp, -1, obj) == -1)
2048 			{
2049 				snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing NULL shape for record %d"), state->currow);
2050 				PQclear(state->fetchres);
2051 				SHPDestroyObject(obj);
2052 				return SHPDUMPERERR;
2053 			}
2054 			SHPDestroyObject(obj);
2055 		}
2056 		else
2057 		{
2058 			/* Get the value from the result set */
2059 			val = PQgetvalue(state->fetchres, state->curresrow, geocolnum);
2060 
2061 			if (!state->config->binary)
2062 			{
2063 				if (state->pgis_major_version > 0)
2064 				{
2065 					LWDEBUG(4, "PostGIS >= 1.0, non-binary cursor");
2066 
2067 					/* Input is bytea encoded text field, so it must be unescaped and
2068 					then converted to hexewkb string */
2069 					hexewkb_binary = PQunescapeBytea((unsigned char *)val, &hexewkb_len);
2070 					hexewkb = convert_bytes_to_hex(hexewkb_binary, hexewkb_len);
2071 				}
2072 				else
2073 				{
2074 					LWDEBUG(4, "PostGIS < 1.0, non-binary cursor");
2075 
2076 					/* Input is already hexewkb string, so we can just
2077 					copy directly to hexewkb */
2078 					hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2079 					hexewkb = malloc(hexewkb_len + 1);
2080 					strncpy(hexewkb, val, hexewkb_len + 1);
2081 				}
2082 			}
2083 			else /* binary */
2084 			{
2085 				LWDEBUG(4, "PostGIS (any version) using binary cursor");
2086 
2087 				/* Input is binary field - must convert to hexewkb string */
2088 				hexewkb_len = PQgetlength(state->fetchres, state->curresrow, geocolnum);
2089 				hexewkb = convert_bytes_to_hex((unsigned char *)val, hexewkb_len);
2090 			}
2091 
2092 			LWDEBUGF(4, "HexEWKB - length: %d  value: %s", strlen(hexewkb), hexewkb);
2093 
2094 			/* Deserialize the LWGEOM */
2095 			lwgeom = lwgeom_from_hexwkb(hexewkb, LW_PARSER_CHECK_NONE);
2096 			if (!lwgeom)
2097 			{
2098 				snprintf(state->message, SHPDUMPERMSGLEN, _("Error parsing HEXEWKB for record %d"), state->currow);
2099 				PQclear(state->fetchres);
2100 				free(hexewkb);
2101 				return SHPDUMPERERR;
2102 			}
2103 
2104 			/* Call the relevant method depending upon the geometry type */
2105 			LWDEBUGF(4, "geomtype: %s\n", lwtype_name(lwgeom->type));
2106 
2107 			switch (lwgeom->type)
2108 			{
2109 			case POINTTYPE:
2110 				if (lwgeom_is_empty(lwgeom))
2111 				{
2112 					obj = create_point_empty(state, lwgeom_as_lwpoint(lwgeom));
2113 				}
2114 				else
2115 				{
2116 					obj = create_point(state, lwgeom_as_lwpoint(lwgeom));
2117 				}
2118 				break;
2119 
2120 			case MULTIPOINTTYPE:
2121 				obj = create_multipoint(state, lwgeom_as_lwmpoint(lwgeom));
2122 				break;
2123 
2124 			case POLYGONTYPE:
2125 				obj = create_polygon(state, lwgeom_as_lwpoly(lwgeom));
2126 				break;
2127 
2128 			case MULTIPOLYGONTYPE:
2129 				obj = create_multipolygon(state, lwgeom_as_lwmpoly(lwgeom));
2130 				break;
2131 
2132 			case LINETYPE:
2133 				obj = create_linestring(state, lwgeom_as_lwline(lwgeom));
2134 				break;
2135 
2136 			case MULTILINETYPE:
2137 				obj = create_multilinestring(state, lwgeom_as_lwmline(lwgeom));
2138 				break;
2139 
2140 			default:
2141 				snprintf(state->message, SHPDUMPERMSGLEN, _("Unknown WKB type (%d) for record %d"), lwgeom->type, state->currow);
2142 				PQclear(state->fetchres);
2143 				SHPDestroyObject(obj);
2144 				return SHPDUMPERERR;
2145 			}
2146 
2147 			/* Free both the original and geometries */
2148 			lwgeom_free(lwgeom);
2149 
2150 			/* Write the shape out to the file */
2151 			if (SHPWriteObject(state->shp, -1, obj) == -1)
2152 			{
2153 				snprintf(state->message, SHPDUMPERMSGLEN, _("Error writing shape %d"), state->currow);
2154 				PQclear(state->fetchres);
2155 				SHPDestroyObject(obj);
2156 				return SHPDUMPERERR;
2157 			}
2158 
2159 			SHPDestroyObject(obj);
2160 
2161 			/* Free the hexewkb (and temporary bytea unescaped string if used) */
2162 			if (hexewkb) free(hexewkb);
2163 			if (hexewkb_binary) PQfreemem(hexewkb_binary);
2164 		}
2165 	}
2166 
2167 	/* Increment ready for next time */
2168 	state->curresrow++;
2169 	state->currow++;
2170 
2171 	return SHPDUMPEROK;
2172 }
2173 
2174 
2175 /* Return a count of the number of rows in the table being dumped */
2176 int
ShpDumperGetRecordCount(SHPDUMPERSTATE * state)2177 ShpDumperGetRecordCount(SHPDUMPERSTATE *state)
2178 {
2179 	return state->rowcount;
2180 }
2181 
2182 
2183 /* Close the specified table and flush all files to disk */
2184 int
ShpDumperCloseTable(SHPDUMPERSTATE * state)2185 ShpDumperCloseTable(SHPDUMPERSTATE *state)
2186 {
2187 	int ret = SHPDUMPEROK;
2188 
2189 	/* Clear the current batch fetch resource */
2190 	PQclear(state->fetchres);
2191 
2192 	/* If a geo column is present, generate the projection file */
2193 	if (state->geo_col_name)
2194 		ret = projFileCreate(state);
2195 
2196 	/* Close the DBF and SHP files */
2197 	if (state->dbf)
2198 		DBFClose(state->dbf);
2199 	if (state->shp)
2200 		SHPClose(state->shp);
2201 
2202 	return ret;
2203 }
2204 
2205 
2206 void
ShpDumperDestroy(SHPDUMPERSTATE * state)2207 ShpDumperDestroy(SHPDUMPERSTATE *state)
2208 {
2209 	/* Destroy a state object created with ShpDumperConnect */
2210 	int i;
2211 
2212 	if (state != NULL)
2213 	{
2214 		/* Disconnect from the database */
2215 		if (state->conn)
2216 			PQfinish(state->conn);
2217 
2218 		/* Free the query strings */
2219 		if (state->fetch_query)
2220 			free(state->fetch_query);
2221 		if (state->main_scan_query)
2222 			free(state->main_scan_query);
2223 
2224 		/* Free the DBF information fields */
2225 		if (state->dbffieldnames)
2226 		{
2227 			for (i = 0; i < state->fieldcount; i++)
2228 				free(state->dbffieldnames[i]);
2229 			free(state->dbffieldnames);
2230 		}
2231 
2232 		if (state->dbffieldtypes)
2233 			free(state->dbffieldtypes);
2234 
2235 		if (state->pgfieldnames)
2236 			free(state->pgfieldnames);
2237 
2238 		/* Free any column map fieldnames if specified */
2239 		colmap_clean(&state->column_map);
2240 
2241 		/* Free other names */
2242 		if (state->table)
2243 			free(state->table);
2244 		if (state->schema)
2245 			free(state->schema);
2246 		if (state->geo_col_name)
2247 			free(state->geo_col_name);
2248 
2249 		/* Free the state itself */
2250 		free(state);
2251 	}
2252 }
2253 
2254 /*
2255  * quote_identifier()
2256  *		Properly double-quote a SQL identifier.
2257  *  Copied from PostgreSQL pg_upgrade/util.c
2258  */
2259 char *
quote_identifier(const char * s)2260 quote_identifier(const char *s)
2261 {
2262 	char	   *result = malloc(strlen(s) * 2 + 3);
2263 	char	   *r = result;
2264 
2265 	*r++ = '"';
2266 	while (*s)
2267 	{
2268 		if (*s == '"')
2269 			*r++ = *s;
2270 		*r++ = *s;
2271 		s++;
2272 	}
2273 	*r++ = '"';
2274 	*r++ = '\0';
2275 
2276 	return result;
2277 }
2278