1 /*
2  * pgeasy.c
3  *
4  */
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10 
11 #include "libpq-fe.h"
12 #include "halt.h"
13 #include "libpgeasy.h"
14 
15 #ifndef NUL
16 #define NUL '\0'
17 #endif
18 
19 #ifndef TRUE
20 #define TRUE	1
21 #endif
22 
23 #ifndef FALSE
24 #define FALSE	0
25 #endif
26 
27 /* GLOBAL VARIABLES */
28 static PGconn *conn;
29 static PGresult *res = NULL;
30 
31 static int	tuple;				/* stores fetch location */
32 
33 #define ON_ERROR_STOP		0
34 #define ON_ERROR_CONTINUE	1
35 
36 static int	on_error_state = ON_ERROR_STOP;		/* halt on errors? */
37 
38 static int	user_has_res = FALSE;
39 
40 static void add_res_tuple(void);
41 static void get_res_tuple(void);
42 static void del_res_tuple(void);
43 
44 
45 /*
46  *	connectdb - returns PGconn structure
47  */
48 PGconn *
connectdb(char * options)49 connectdb(char *options)
50 {
51 	/* make a connection to the database */
52 	conn = PQconnectdb(options);
53 	if (PQstatus(conn) == CONNECTION_BAD)
54 		halt("Connection to database using '%s' failed.\n%s\n", options,
55 			 PQerrorMessage(conn));
56 	return conn;
57 }
58 
59 
60 /*
61  *		disconnectdb
62  */
63 void
disconnectdb()64 disconnectdb()
65 {
66 	if (res != NULL && user_has_res == FALSE)
67 	{
68 		PQclear(res);
69 		res = NULL;
70 	}
71 
72 	PQfinish(conn);
73 }
74 
75 
76 /*
77  *	doquery - returns PGresult structure
78  */
79 PGresult *
doquery(char * query)80 doquery(char *query)
81 {
82 	if (res != NULL && user_has_res == FALSE)
83 		PQclear(res);
84 
85 	user_has_res = FALSE;
86 	res = PQexec(conn, query);
87 
88 	if (on_error_state == ON_ERROR_STOP &&
89 		(res == NULL ||
90 		 PQresultStatus(res) == PGRES_BAD_RESPONSE ||
91 		 PQresultStatus(res) == PGRES_NONFATAL_ERROR ||
92 		 PQresultStatus(res) == PGRES_FATAL_ERROR))
93 	{
94 		if (res != NULL)
95 			fprintf(stderr, "query error:  %s\n", PQresultErrorMessage(res));
96 		else
97 			fprintf(stderr, "connection error:  %s\n", PQerrorMessage(conn));
98 		PQfinish(conn);
99 		halt("failed query:  %s\n", query);
100 	}
101 	tuple = 0;
102 	return res;
103 }
104 
105 
106 /*
107  *	fetch - returns tuple number (starts at 0), or the value END_OF_TUPLES
108  *			NULL pointers are skipped
109  */
110 int
fetch(void * param,...)111 fetch(void *param,...)
112 {
113 	va_list		ap;
114 	int			arg,
115 				num_fields;
116 
117 	num_fields = PQnfields(res);
118 
119 	if (tuple >= PQntuples(res))
120 		return END_OF_TUPLES;
121 
122 	va_start(ap, param);
123 	for (arg = 0; arg < num_fields; arg++)
124 	{
125 		if (param != NULL)
126 		{
127 			if (PQfsize(res, arg) == -1)
128 			{
129 				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
130 				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
131 			}
132 			else
133 				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
134 		}
135 		param = va_arg(ap, char *);
136 	}
137 	va_end(ap);
138 	return tuple++;
139 }
140 
141 
142 /*
143  *		fetchwithnulls - returns tuple number (starts at 0),
144  *																						or the value END_OF_TUPLES
145  *		Returns TRUE or FALSE into null indicator variables
146  *		NULL pointers are skipped
147  */
148 int
fetchwithnulls(void * param,...)149 fetchwithnulls(void *param,...)
150 {
151 	va_list		ap;
152 	int			arg,
153 				num_fields;
154 
155 	num_fields = PQnfields(res);
156 
157 	if (tuple >= PQntuples(res))
158 		return END_OF_TUPLES;
159 
160 	va_start(ap, param);
161 	for (arg = 0; arg < num_fields; arg++)
162 	{
163 		if (param != NULL)
164 		{
165 			if (PQfsize(res, arg) == -1)
166 			{
167 				memcpy(param, PQgetvalue(res, tuple, arg), PQgetlength(res, tuple, arg));
168 				((char *) param)[PQgetlength(res, tuple, arg)] = NUL;
169 			}
170 			else
171 				memcpy(param, PQgetvalue(res, tuple, arg), PQfsize(res, arg));
172 		}
173 		param = va_arg(ap, char *);
174 		if (PQgetisnull(res, tuple, arg) != 0)
175 			*(int *) param = 1;
176 		else
177 			*(int *) param = 0;
178 		param = va_arg(ap, char *);
179 	}
180 	va_end(ap);
181 	return tuple++;
182 }
183 
184 
185 /*
186  *	reset_fetch
187  */
188 void
reset_fetch()189 reset_fetch()
190 {
191 	tuple = 0;
192 }
193 
194 
195 /*
196  *	on_error_stop
197  */
198 void
on_error_stop()199 on_error_stop()
200 {
201 	on_error_state = ON_ERROR_STOP;
202 }
203 
204 
205 /*
206  *	on_error_continue
207  */
208 void
on_error_continue()209 on_error_continue()
210 {
211 	on_error_state = ON_ERROR_CONTINUE;
212 }
213 
214 
215 /*
216  *	get_result
217  */
218 PGresult *
get_result()219 get_result()
220 {
221 	if (res == NULL)
222 		halt("get_result called with no result pointer used\n");
223 
224 	/* delete it if it is already there; we are about to re-add it */
225 	del_res_tuple();
226 
227 	/* we have to store the fetch location */
228 	add_res_tuple();
229 
230 	user_has_res = TRUE;
231 
232 	return res;
233 }
234 
235 
236 /*
237  *	set_result
238  */
239 void
set_result(PGresult * newres)240 set_result(PGresult *newres)
241 {
242 	if (newres == NULL)
243 		halt("set_result called with null result pointer\n");
244 
245 	if (res != NULL && user_has_res == FALSE)
246 	{
247 		/*
248 		 * Basically, throw away res. We can't return to it because the
249 		 * user doesn't have the res pointer.
250 		 */
251 		PQclear(res);
252 	}
253 
254 	user_has_res = FALSE;
255 
256 	res = newres;
257 
258 	get_res_tuple();
259 
260 	del_res_tuple();
261 }
262 
263 
264 /*
265  *	Routines to store res/tuple mapping
266  *	This is used to keep track of fetch locations while using get/set on
267  *	result sets.
268  *	Auto-growing array is used, with empty slots marked by res == NULL
269  */
270 
271 static struct res_tuple
272 {
273 	PGresult   *res;
274 	int			tuple;
275 }	*res_tuple = NULL;
276 
277 static int	res_tuple_len = 0;
278 
279 
280 /*
281  * add_res_tuple
282  */
283 static void
add_res_tuple(void)284 add_res_tuple(void)
285 {
286 	int			i,
287 				new_res_tuple_len = res_tuple_len ? res_tuple_len * 2 : 1;
288 
289 	for (i = 0; i < res_tuple_len; i++)
290 		/* Put it in an empty slot */
291 		if (res_tuple[i].res == NULL)
292 		{
293 			res_tuple[i].res = res;
294 			res_tuple[i].tuple = tuple;
295 			return;
296 		}
297 
298 	/* Need to grow array */
299 	res_tuple = realloc(res_tuple, new_res_tuple_len * sizeof(struct res_tuple));
300 
301 	/* clear new elements */
302 	for (i = res_tuple_len; i < new_res_tuple_len; i++)
303 	{
304 		res_tuple[i].res = NULL;
305 		res_tuple[i].tuple = 0;
306 	}
307 
308 	res_tuple_len = new_res_tuple_len;
309 
310 	/* recursion to add entry */
311 	add_res_tuple();
312 }
313 
314 
315 /*
316  * get_res_tuple
317  */
318 static void
get_res_tuple(void)319 get_res_tuple(void)
320 {
321 	int			i;
322 
323 	for (i = 0; i < res_tuple_len; i++)
324 		if (res_tuple[i].res == res)
325 		{
326 			tuple = res_tuple[i].tuple;
327 			return;
328 		}
329 	halt("get_res_tuple called with invalid result pointer\n");
330 }
331 
332 
333 /*
334  * del_res_tuple
335  */
336 static void
del_res_tuple(void)337 del_res_tuple(void)
338 {
339 	int			i;
340 
341 	for (i = 0; i < res_tuple_len; i++)
342 		if (res_tuple[i].res == res)
343 		{
344 			res_tuple[i].res = NULL;
345 			return;
346 		}
347 	return;
348 }
349