Code

Added missing daemon/config.h.
[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_data_t *data)
77 {
78         switch (type) {
79                 case SC_TYPE_INTEGER:
80                         data->data.integer = dbi_result_get_longlong_idx(res, i);
81                         break;
82                 case SC_TYPE_DECIMAL:
83                         data->data.decimal = dbi_result_get_double_idx(res, i);
84                         break;
85                 case SC_TYPE_STRING:
86                         data->data.string = dbi_result_get_string_idx(res, i);
87                         break;
88                 case SC_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 SC_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,
116                 sc_object_t *user_data)
118         sc_data_t data[num_fields];
119         int types[num_fields];
120         unsigned int i;
122         unsigned long long num_rows;
123         unsigned long long success = 0, n;
125         assert(client && res && callback);
126         assert(num_fields > 0);
128         for (i = 0; i < num_fields; ++i) {
129                 types[i] = dbi_result_get_field_type_idx(res, i + 1);
130                 if (types[i] == DBI_TYPE_ERROR) {
131                         fprintf(stderr, "dbi: failed to fetch data: %s\n",
132                                         sc_dbi_strerror(client->conn));
133                         return -1;
134                 }
135                 types[i] = DBI_TYPE_TO_SC(types[i]);
136         }
138         num_rows = dbi_result_get_numrows(res);
139         if (num_rows < 1)
140                 return -1;
142         for (n = 0; n < num_rows; ++n) {
143                 if (! dbi_result_seek_row(res, n + 1)) {
144                         fprintf(stderr, "dbi: Failed to retrieve row %llu: %s\n",
145                                         n, sc_dbi_strerror(client->conn));
146                         continue;
147                 }
149                 for (i = 0; i < num_fields; ++i)
150                         if (sc_dbi_get_field(res, (unsigned int)(i + 1),
151                                                 types[i], &data[i]))
152                                 continue;
154                 if (callback(client, num_fields, data, user_data))
155                         continue;
157                 ++success;
158         }
160         if (! success)
161                 return -1;
162         return 0;
163 } /* sc_dbi_get_data */
165 /*
166  * public API
167  */
169 sc_dbi_options_t *
170 sc_dbi_options_create(void)
172         sc_dbi_options_t *options;
174         options = malloc(sizeof(options));
175         if (! options)
176                 return NULL;
178         options->options = NULL;
179         options->options_num = 0;
180         return options;
181 } /* sc_dbi_options_create */
183 int
184 sc_dbi_options_add(sc_dbi_options_t *options,
185                 const char *key, const char *value)
187         sc_dbi_option_t *new;
189         if ((! options) || (! key) || (! value))
190                 return -1;
192         new = realloc(options->options,
193                         (options->options_num + 1) * sizeof(*options->options));
194         if (! new)
195                 return -1;
197         options->options = new;
198         new = options->options + options->options_num;
200         new->key = strdup(key);
201         new->value = strdup(value);
203         if ((! new->key) || (! new->value)) {
204                 if (new->key)
205                         free(new->key);
206                 if (new->value)
207                         free(new->value);
208                 return -1;
209         }
211         ++options->options_num;
212         return 0;
213 } /* sc_dbi_options_add */
215 void
216 sc_dbi_options_destroy(sc_dbi_options_t *options)
218         size_t i;
220         if (! options)
221                 return;
223         for (i = 0; i < options->options_num; ++i) {
224                 sc_dbi_option_t *opt = options->options + i;
226                 if (opt->key)
227                         free(opt->key);
228                 if (opt->value)
229                         free(opt->value);
230         }
232         if (options->options)
233                 free(options->options);
234         options->options = NULL;
235         options->options_num = 0;
236         free(options);
237 } /* sc_dbi_options_destroy */
239 sc_dbi_client_t *
240 sc_dbi_client_create(const char *driver, const char *database)
242         sc_dbi_client_t *client;
244         if ((! driver) || (! database))
245                 return NULL;
247         client = malloc(sizeof(*client));
248         if (! client)
249                 return NULL;
250         memset(client, 0, sizeof(*client));
252         client->conn = NULL;
253         client->options = NULL;
255         client->driver = strdup(driver);
256         client->database = strdup(database);
257         if ((! client->driver) || (! client->database)) {
258                 sc_dbi_client_destroy(client);
259                 return NULL;
260         }
261         return client;
262 } /* sc_dbi_client_create */
264 int
265 sc_dbi_client_set_options(sc_dbi_client_t *client,
266                 sc_dbi_options_t *options)
268         if (! client)
269                 return -1;
271         if (client->options)
272                 sc_dbi_options_destroy(client->options);
273         client->options = options;
274         return 0;
275 } /* sc_dbi_client_set_options */
277 int
278 sc_dbi_client_connect(sc_dbi_client_t *client)
280         dbi_driver driver;
281         size_t i;
283         if ((! client) || (! client->driver) || (! client->database))
284                 return -1;
286         if (client->conn)
287                 dbi_conn_close(client->conn);
289         driver = dbi_driver_open(client->driver);
290         if (! driver) {
291                 fprintf(stderr, "dbi: failed to open DBI driver '%s'; "
292                                 "possibly it's not installed.\n",
293                                 client->driver);
295                 fprintf(stderr, "dbi: known drivers:\n");
296                 for (driver = dbi_driver_list(NULL); driver;
297                                 driver = dbi_driver_list(driver)) {
298                         fprintf(stderr, "\t- %s\n", dbi_driver_get_name(driver));
299                 }
300                 return -1;
301         }
303         client->conn = dbi_conn_open(driver);
304         if (! client->conn) {
305                 fprintf(stderr, "dbi: failed to open connection object.\n");
306                 return -1;
307         }
309         if (client->options) {
310                 for (i = 0; i < client->options->options_num; ++i) {
311                         const char *opt;
313                         if (! dbi_conn_set_option(client->conn,
314                                                 client->options->options[i].key,
315                                                 client->options->options[i].value))
316                                 continue;
317                         /* else: error */
319                         fprintf(stderr, "dbi: failed to set option '%s': %s\n",
320                                         client->options->options[i].key,
321                                         sc_dbi_strerror(client->conn));
323                         fprintf(stderr, "dbi: known driver options:\n");
324                         for (opt = dbi_conn_get_option_list(client->conn, NULL); opt;
325                                         opt = dbi_conn_get_option_list(client->conn, opt))
326                                 fprintf(stderr, "\t- %s\n", opt);
328                         dbi_conn_close(client->conn);
329                         return -1;
330                 }
331         }
333         if (dbi_conn_set_option(client->conn, "dbname", client->database)) {
334                 fprintf(stderr, "dbi: failed to set option 'dbname': %s\n",
335                                 sc_dbi_strerror(client->conn));
336                 dbi_conn_close(client->conn);
337                 return -1;
338         }
340         if (dbi_conn_connect(client->conn) < 0) {
341                 fprintf(stderr, "dbi: failed to connect to database '%s': %s\n",
342                                 client->database, sc_dbi_strerror(client->conn));
343                 dbi_conn_close(client->conn);
344                 return -1;
345         }
346         return 0;
347 } /* sc_dbi_client_connect */
349 int
350 sc_dbi_exec_query(sc_dbi_client_t *client, const char *query,
351                 sc_dbi_data_cb callback, sc_object_t *user_data, int n, ...)
353         dbi_result res;
354         unsigned int num_fields;
356         int status;
358         if ((! client) || (! query))
359                 return -1;
361         res = dbi_conn_query(client->conn, query);
362         if (! res) {
363                 fprintf(stderr, "dbi: failed to execute query '%s': %s\n",
364                                 query, sc_dbi_strerror(client->conn));
365                 return -1;
366         }
368         if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
369                 fprintf(stderr, "dbi: failed to fetch rows for query '%s': %s\n",
370                                 query, sc_dbi_strerror(client->conn));
371                 dbi_result_free(res);
372                 return -1;
373         }
375         if (dbi_result_get_numrows(res) < 1) { /* no data */
376                 dbi_result_free(res);
377                 return 0;
378         }
380         num_fields = dbi_result_get_numfields(res);
382         if (n >= 0) {
383                 va_list types;
384                 int i;
386                 if (n != (int)num_fields) {
387                         fprintf(stderr, "dbi: number of returned fields (%i) does not "
388                                         "match the number of requested fields (%i) "
389                                         "for query '%s'.\n", num_fields, n, query);
390                         dbi_result_free(res);
391                         return -1;
392                 }
394                 va_start(types, n);
395                 status = 0;
397                 for (i = 0; i < n; ++i) {
398                         unsigned short field_type = dbi_result_get_field_type_idx(res,
399                                         (unsigned int)(i + 1));
401                         unsigned int type = va_arg(types, unsigned int);
403                         field_type = DBI_TYPE_TO_SC(field_type);
405                         /* column count starts at 1 */
406                         if ((unsigned int)field_type != type) {
407                                 fprintf(stderr, "dbi: type of column '%s' (%u) does not match "
408                                                 "requested type (%u).\n",
409                                                 dbi_result_get_field_name(res, (unsigned int)i + 1),
410                                                 field_type, type);
411                                 status = -1;
412                         }
413                 }
415                 va_end(types);
417                 if (status) {
418                         dbi_result_free(res);
419                         return status;
420                 }
421         }
423         if (num_fields < 1) { /* no data */
424                 dbi_result_free(res);
425                 return 0;
426         }
428         status = sc_dbi_get_data(client, res, num_fields, callback, user_data);
430         dbi_result_free(res);
431         return status;
432 } /* sc_dbi_exec_query */
434 void
435 sc_dbi_client_destroy(sc_dbi_client_t *client)
437         if (! client)
438                 return;
440         if (client->driver)
441                 free(client->driver);
442         client->driver = NULL;
444         if (client->database)
445                 free(client->database);
446         client->database = NULL;
448         if (client->conn)
449                 dbi_conn_close(client->conn);
451         if (client->options)
452                 sc_dbi_options_destroy(client->options);
453         client->options = NULL;
455         free(client);
456 } /* sc_dbi_client_destroy */
458 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */