Code

utils unixsock: Added wrappers for clearerr(), feof(), and ferror().
[sysdb.git] / src / utils / dbi.c
1 /*
2  * syscollector - src/utils/dbi.c
3  * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #include "utils/dbi.h"
30 #include <assert.h>
32 #include <dbi/dbi.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
39 /*
40  * private data types
41  */
43 typedef struct {
44         char *key;
45         char *value;
46 } sc_dbi_option_t;
48 struct sc_dbi_options {
49         sc_dbi_option_t *options;
50         size_t options_num;
51 };
53 struct sc_dbi_client {
54         char *driver;
55         char *database;
57         dbi_conn conn;
59         sc_dbi_options_t *options;
60 };
62 /*
63  * private helper functions
64  */
66 static const char *
67 sc_dbi_strerror(dbi_conn conn)
68 {
69         const char *errmsg = NULL;
70         dbi_conn_error(conn, &errmsg);
71         return errmsg;
72 } /* sc_dbi_strerror */
74 static int
75 sc_dbi_get_field(dbi_result res, unsigned int i,
76                 int type, sc_dbi_data_t *data)
77 {
78         switch (type) {
79                 case DBI_TYPE_INTEGER:
80                         data->data.integer = dbi_result_get_longlong_idx(res, i);
81                         break;
82                 case DBI_TYPE_DECIMAL:
83                         data->data.decimal = dbi_result_get_double_idx(res, i);
84                         break;
85                 case DBI_TYPE_STRING:
86                         data->data.string = dbi_result_get_string_idx(res, i);
87                         break;
88                 case DBI_TYPE_DATETIME:
89                         {
90                                 /* libdbi does not provide any higher resolutions than that */
91                                 time_t datetime = dbi_result_get_datetime_idx(res, i);
92                                 data->data.datetime = SECS_TO_SC_TIME(datetime);
93                         }
94                         break;
95                 case DBI_TYPE_BINARY:
96                         {
97                                 size_t length = dbi_result_get_field_length_idx(res, i);
98                                 const unsigned char *datum = dbi_result_get_binary_idx(res, i);
99                                 data->data.binary.length = length;
100                                 data->data.binary.datum = datum;
101                         }
102                         break;
103                 default:
104                         fprintf(stderr, "dbi: Unexpected type %i while "
105                                         "parsing query result.\n", type);
106                         return -1;
107         }
109         data->type = type;
110         return 0;
111 } /* sc_dbi_get_field */
113 static int
114 sc_dbi_get_data(sc_dbi_client_t *client, dbi_result res,
115                 unsigned int num_fields, sc_dbi_data_cb callback)
117         sc_dbi_data_t data[num_fields];
118         int types[num_fields];
119         unsigned int i;
121         unsigned long long num_rows;
122         unsigned long long success = 0, n;
124         assert(client && res && callback);
125         assert(num_fields > 0);
127         for (i = 0; i < num_fields; ++i) {
128                 types[i] = dbi_result_get_field_type_idx(res, i + 1);
129                 if (types[i] == DBI_TYPE_ERROR) {
130                         fprintf(stderr, "dbi: failed to fetch data: %s\n",
131                                         sc_dbi_strerror(client->conn));
132                         return -1;
133                 }
134         }
136         num_rows = dbi_result_get_numrows(res);
137         if (num_rows < 1)
138                 return -1;
140         for (n = 0; n < num_rows; ++n) {
141                 if (! dbi_result_seek_row(res, n + 1)) {
142                         fprintf(stderr, "dbi: Failed to retrieve row %llu: %s\n",
143                                         n, sc_dbi_strerror(client->conn));
144                         continue;
145                 }
147                 for (i = 0; i < num_fields; ++i)
148                         if (sc_dbi_get_field(res, (unsigned int)(i + 1),
149                                                 types[i], &data[i]))
150                                 continue;
152                 if (callback(client, num_fields, data))
153                         continue;
155                 ++success;
156         }
158         if (! success)
159                 return -1;
160         return 0;
161 } /* sc_dbi_get_data */
163 /*
164  * public API
165  */
167 sc_dbi_options_t *
168 sc_dbi_options_create(void)
170         sc_dbi_options_t *options;
172         options = malloc(sizeof(options));
173         if (! options)
174                 return NULL;
176         options->options = NULL;
177         options->options_num = 0;
178         return options;
179 } /* sc_dbi_options_create */
181 int
182 sc_dbi_options_add(sc_dbi_options_t *options,
183                 const char *key, const char *value)
185         sc_dbi_option_t *new;
187         if ((! options) || (! key) || (! value))
188                 return -1;
190         new = realloc(options->options,
191                         (options->options_num + 1) * sizeof(*options->options));
192         if (! new)
193                 return -1;
195         options->options = new;
196         new = options->options + options->options_num;
198         new->key = strdup(key);
199         new->value = strdup(value);
201         if ((! new->key) || (! new->value)) {
202                 if (new->key)
203                         free(new->key);
204                 if (new->value)
205                         free(new->value);
206                 return -1;
207         }
209         ++options->options_num;
210         return 0;
211 } /* sc_dbi_options_add */
213 void
214 sc_dbi_options_destroy(sc_dbi_options_t *options)
216         size_t i;
218         if (! options)
219                 return;
221         for (i = 0; i < options->options_num; ++i) {
222                 sc_dbi_option_t *opt = options->options + i;
224                 if (opt->key)
225                         free(opt->key);
226                 if (opt->value)
227                         free(opt->value);
228         }
230         if (options->options)
231                 free(options->options);
232         options->options = NULL;
233         options->options_num = 0;
234         free(options);
235 } /* sc_dbi_options_destroy */
237 sc_dbi_client_t *
238 sc_dbi_client_create(const char *driver, const char *database)
240         sc_dbi_client_t *client;
242         if ((! driver) || (! database))
243                 return NULL;
245         client = malloc(sizeof(*client));
246         if (! client)
247                 return NULL;
248         memset(client, 0, sizeof(*client));
250         client->conn = NULL;
251         client->options = NULL;
253         client->driver = strdup(driver);
254         client->database = strdup(database);
255         if ((! client->driver) || (! client->database)) {
256                 sc_dbi_client_destroy(client);
257                 return NULL;
258         }
259         return client;
260 } /* sc_dbi_client_create */
262 int
263 sc_dbi_client_set_options(sc_dbi_client_t *client,
264                 sc_dbi_options_t *options)
266         if (! client)
267                 return -1;
269         if (client->options)
270                 sc_dbi_options_destroy(client->options);
271         client->options = options;
272         return 0;
273 } /* sc_dbi_client_set_options */
275 int
276 sc_dbi_client_connect(sc_dbi_client_t *client)
278         dbi_driver driver;
279         size_t i;
281         if ((! client) || (! client->driver) || (! client->database))
282                 return -1;
284         if (client->conn)
285                 dbi_conn_close(client->conn);
287         driver = dbi_driver_open(client->driver);
288         if (! driver) {
289                 fprintf(stderr, "dbi: failed to open DBI driver '%s'; "
290                                 "possibly it's not installed.\n",
291                                 client->driver);
293                 fprintf(stderr, "dbi: known drivers:\n");
294                 for (driver = dbi_driver_list(NULL); driver;
295                                 driver = dbi_driver_list(driver)) {
296                         fprintf(stderr, "\t- %s\n", dbi_driver_get_name(driver));
297                 }
298                 return -1;
299         }
301         client->conn = dbi_conn_open(driver);
302         if (! client->conn) {
303                 fprintf(stderr, "dbi: failed to open connection object.\n");
304                 return -1;
305         }
307         if (client->options) {
308                 for (i = 0; i < client->options->options_num; ++i) {
309                         const char *opt;
311                         if (! dbi_conn_set_option(client->conn,
312                                                 client->options->options[i].key,
313                                                 client->options->options[i].value))
314                                 continue;
315                         /* else: error */
317                         fprintf(stderr, "dbi: failed to set option '%s': %s\n",
318                                         client->options->options[i].key,
319                                         sc_dbi_strerror(client->conn));
321                         fprintf(stderr, "dbi: known driver options:\n");
322                         for (opt = dbi_conn_get_option_list(client->conn, NULL); opt;
323                                         opt = dbi_conn_get_option_list(client->conn, opt))
324                                 fprintf(stderr, "\t- %s\n", opt);
326                         dbi_conn_close(client->conn);
327                         return -1;
328                 }
329         }
331         if (dbi_conn_set_option(client->conn, "dbname", client->database)) {
332                 fprintf(stderr, "dbi: failed to set option 'dbname': %s\n",
333                                 sc_dbi_strerror(client->conn));
334                 dbi_conn_close(client->conn);
335                 return -1;
336         }
338         if (dbi_conn_connect(client->conn) < 0) {
339                 fprintf(stderr, "dbi: failed to connect to database '%s': %s\n",
340                                 client->database, sc_dbi_strerror(client->conn));
341                 dbi_conn_close(client->conn);
342                 return -1;
343         }
344         return 0;
345 } /* sc_dbi_client_connect */
347 int
348 sc_dbi_exec_query(sc_dbi_client_t *client, const char *query,
349                 sc_dbi_data_cb callback, int n, ...)
351         dbi_result res;
352         unsigned int num_fields;
354         int status;
356         if ((! client) || (! query))
357                 return -1;
359         res = dbi_conn_query(client->conn, query);
360         if (! res) {
361                 fprintf(stderr, "dbi: failed to execute query '%s': %s\n",
362                                 query, sc_dbi_strerror(client->conn));
363                 return -1;
364         }
366         if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
367                 fprintf(stderr, "dbi: failed to fetch rows for query '%s': %s\n",
368                                 query, sc_dbi_strerror(client->conn));
369                 dbi_result_free(res);
370                 return -1;
371         }
373         if (dbi_result_get_numrows(res) < 1) { /* no data */
374                 dbi_result_free(res);
375                 return 0;
376         }
378         num_fields = dbi_result_get_numfields(res);
380         if (n >= 0) {
381                 va_list types;
382                 int i;
384                 if (n != (int)num_fields) {
385                         fprintf(stderr, "dbi: number of returned fields (%i) does not "
386                                         "match the number of requested fields (%i) "
387                                         "for query '%s'.\n", num_fields, n, query);
388                         dbi_result_free(res);
389                         return -1;
390                 }
392                 va_start(types, n);
393                 status = 0;
395                 for (i = 0; i < n; ++i) {
396                         unsigned short field_type = dbi_result_get_field_type_idx(res,
397                                         (unsigned int)(i + 1));
399                         unsigned int type = va_arg(types, unsigned int);
401                         /* column count starts at 1 */
402                         if ((unsigned int)field_type != type) {
403                                 fprintf(stderr, "dbi: type of column '%s' (%u) does not match "
404                                                 "requested type (%u).\n",
405                                                 dbi_result_get_field_name(res, (unsigned int)i + 1),
406                                                 field_type, type);
407                                 status = -1;
408                         }
409                 }
411                 va_end(types);
413                 if (status) {
414                         dbi_result_free(res);
415                         return status;
416                 }
417         }
419         if (num_fields < 1) { /* no data */
420                 dbi_result_free(res);
421                 return 0;
422         }
424         status = sc_dbi_get_data(client, res, num_fields, callback);
426         dbi_result_free(res);
427         return status;
428 } /* sc_dbi_exec_query */
430 void
431 sc_dbi_client_destroy(sc_dbi_client_t *client)
433         if (! client)
434                 return;
436         if (client->driver)
437                 free(client->driver);
438         client->driver = NULL;
440         if (client->database)
441                 free(client->database);
442         client->database = NULL;
444         if (client->conn)
445                 dbi_conn_close(client->conn);
447         if (client->options)
448                 sc_dbi_options_destroy(client->options);
449         client->options = NULL;
451         free(client);
452 } /* sc_dbi_client_destroy */
454 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */