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