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