1 /*
2 * SysDB - 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 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "utils/dbi.h"
33 #include "utils/error.h"
35 #include <assert.h>
37 #include <dbi/dbi.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
44 /*
45 * private data types
46 */
48 typedef struct {
49 char *key;
50 char *value;
51 } sdb_dbi_option_t;
53 struct sdb_dbi_options {
54 sdb_dbi_option_t *options;
55 size_t options_num;
56 };
58 struct sdb_dbi_client {
59 char *driver;
60 char *database;
62 dbi_conn conn;
64 sdb_dbi_options_t *options;
65 };
67 /*
68 * private helper functions
69 */
71 static const char *
72 sdb_dbi_strerror(dbi_conn conn)
73 {
74 const char *errmsg = NULL;
75 dbi_conn_error(conn, &errmsg);
76 return errmsg;
77 } /* sdb_dbi_strerror */
79 static int
80 sdb_dbi_get_field(dbi_result res, unsigned int i,
81 int type, sdb_data_t *data)
82 {
83 switch (type) {
84 case SDB_TYPE_INTEGER:
85 data->data.integer = dbi_result_get_longlong_idx(res, i);
86 break;
87 case SDB_TYPE_DECIMAL:
88 data->data.decimal = dbi_result_get_double_idx(res, i);
89 break;
90 case SDB_TYPE_STRING:
91 data->data.string = dbi_result_get_string_copy_idx(res, i);
92 break;
93 case SDB_TYPE_DATETIME:
94 {
95 /* libdbi does not provide any higher resolutions than that */
96 time_t datetime = dbi_result_get_datetime_idx(res, i);
97 data->data.datetime = SECS_TO_SDB_TIME(datetime);
98 }
99 break;
100 case SDB_TYPE_BINARY:
101 {
102 size_t length = dbi_result_get_field_length_idx(res, i);
103 unsigned char *datum = dbi_result_get_binary_copy_idx(res, i);
104 data->data.binary.length = length;
105 data->data.binary.datum = datum;
106 }
107 break;
108 default:
109 sdb_log(SDB_LOG_ERR, "dbi: Unexpected type %i while "
110 "parsing query result.", type);
111 return -1;
112 }
114 data->type = type;
115 return 0;
116 } /* sdb_dbi_get_field */
118 static int
119 sdb_dbi_get_data(sdb_dbi_client_t *client, dbi_result res,
120 unsigned int num_fields, sdb_dbi_data_cb callback,
121 sdb_object_t *user_data)
122 {
123 sdb_data_t data[num_fields];
124 int types[num_fields];
125 unsigned int i;
127 unsigned long long num_rows;
128 unsigned long long success = 0, n;
130 assert(client && res && callback);
131 assert(num_fields > 0);
133 for (i = 0; i < num_fields; ++i) {
134 types[i] = dbi_result_get_field_type_idx(res, i + 1);
135 if (types[i] == DBI_TYPE_ERROR) {
136 sdb_log(SDB_LOG_ERR, "dbi: failed to fetch data: %s",
137 sdb_dbi_strerror(client->conn));
138 return -1;
139 }
140 types[i] = DBI_TYPE_TO_SC(types[i]);
141 }
143 num_rows = dbi_result_get_numrows(res);
144 if (num_rows < 1)
145 return -1;
147 for (n = 0; n < num_rows; ++n) {
148 int status;
150 if (! dbi_result_seek_row(res, n + 1)) {
151 sdb_log(SDB_LOG_ERR, "dbi: Failed to retrieve row %llu: %s",
152 n, sdb_dbi_strerror(client->conn));
153 continue;
154 }
156 for (i = 0; i < num_fields; ++i)
157 if (sdb_dbi_get_field(res, (unsigned int)(i + 1),
158 types[i], &data[i]))
159 continue;
161 status = callback(client, num_fields, data, user_data);
162 for (i = 0; i < num_fields; ++i)
163 sdb_data_free_datum(&data[i]);
165 if (status)
166 continue;
168 ++success;
169 }
171 if (! success)
172 return -1;
173 return 0;
174 } /* sdb_dbi_get_data */
176 /*
177 * public API
178 */
180 sdb_dbi_options_t *
181 sdb_dbi_options_create(void)
182 {
183 sdb_dbi_options_t *options;
185 options = malloc(sizeof(*options));
186 if (! options)
187 return NULL;
189 options->options = NULL;
190 options->options_num = 0;
191 return options;
192 } /* sdb_dbi_options_create */
194 int
195 sdb_dbi_options_add(sdb_dbi_options_t *options,
196 const char *key, const char *value)
197 {
198 sdb_dbi_option_t *new;
200 if ((! options) || (! key) || (! value))
201 return -1;
203 new = realloc(options->options,
204 (options->options_num + 1) * sizeof(*options->options));
205 if (! new)
206 return -1;
208 options->options = new;
209 new = options->options + options->options_num;
211 new->key = strdup(key);
212 new->value = strdup(value);
214 if ((! new->key) || (! new->value)) {
215 if (new->key)
216 free(new->key);
217 if (new->value)
218 free(new->value);
219 return -1;
220 }
222 ++options->options_num;
223 return 0;
224 } /* sdb_dbi_options_add */
226 void
227 sdb_dbi_options_destroy(sdb_dbi_options_t *options)
228 {
229 size_t i;
231 if (! options)
232 return;
234 for (i = 0; i < options->options_num; ++i) {
235 sdb_dbi_option_t *opt = options->options + i;
237 if (opt->key)
238 free(opt->key);
239 if (opt->value)
240 free(opt->value);
241 }
243 if (options->options)
244 free(options->options);
245 options->options = NULL;
246 options->options_num = 0;
247 free(options);
248 } /* sdb_dbi_options_destroy */
250 sdb_dbi_client_t *
251 sdb_dbi_client_create(const char *driver, const char *database)
252 {
253 sdb_dbi_client_t *client;
255 if ((! driver) || (! database))
256 return NULL;
258 client = malloc(sizeof(*client));
259 if (! client)
260 return NULL;
261 memset(client, 0, sizeof(*client));
263 client->conn = NULL;
264 client->options = NULL;
266 client->driver = strdup(driver);
267 client->database = strdup(database);
268 if ((! client->driver) || (! client->database)) {
269 sdb_dbi_client_destroy(client);
270 return NULL;
271 }
272 return client;
273 } /* sdb_dbi_client_create */
275 int
276 sdb_dbi_client_set_options(sdb_dbi_client_t *client,
277 sdb_dbi_options_t *options)
278 {
279 if (! client)
280 return -1;
282 if (client->options)
283 sdb_dbi_options_destroy(client->options);
284 client->options = options;
285 return 0;
286 } /* sdb_dbi_client_set_options */
288 int
289 sdb_dbi_client_connect(sdb_dbi_client_t *client)
290 {
291 dbi_driver driver;
292 size_t i;
294 if ((! client) || (! client->driver) || (! client->database))
295 return -1;
297 if (client->conn) {
298 dbi_conn_close(client->conn);
299 client->conn = NULL;
300 }
302 driver = dbi_driver_open(client->driver);
303 if (! driver) {
304 sdb_error_set("dbi: failed to open DBI driver '%s'; "
305 "possibly it's not installed.\n",
306 client->driver);
308 sdb_error_append("dbi: known drivers:\n");
309 for (driver = dbi_driver_list(NULL); driver;
310 driver = dbi_driver_list(driver)) {
311 sdb_error_append("\t- %s\n", dbi_driver_get_name(driver));
312 }
313 sdb_error_chomp();
314 sdb_error_log(SDB_LOG_ERR);
315 return -1;
316 }
318 client->conn = dbi_conn_open(driver);
319 if (! client->conn) {
320 sdb_log(SDB_LOG_ERR, "dbi: failed to open connection "
321 "object.");
322 return -1;
323 }
325 if (client->options) {
326 for (i = 0; i < client->options->options_num; ++i) {
327 const char *opt;
329 if (! dbi_conn_set_option(client->conn,
330 client->options->options[i].key,
331 client->options->options[i].value))
332 continue;
333 /* else: error */
335 sdb_error_set("dbi: failed to set option '%s': %s\n",
336 client->options->options[i].key,
337 sdb_dbi_strerror(client->conn));
339 sdb_error_append("dbi: known driver options:\n");
340 for (opt = dbi_conn_get_option_list(client->conn, NULL); opt;
341 opt = dbi_conn_get_option_list(client->conn, opt))
342 sdb_error_append("\t- %s\n", opt);
343 sdb_error_chomp();
344 sdb_error_log(SDB_LOG_ERR);
346 dbi_conn_close(client->conn);
347 client->conn = NULL;
348 return -1;
349 }
350 }
352 if (dbi_conn_set_option(client->conn, "dbname", client->database)) {
353 sdb_log(SDB_LOG_ERR, "dbi: failed to set option 'dbname': %s",
354 sdb_dbi_strerror(client->conn));
355 dbi_conn_close(client->conn);
356 client->conn = NULL;
357 return -1;
358 }
360 if (dbi_conn_connect(client->conn) < 0) {
361 sdb_log(SDB_LOG_ERR, "dbi: failed to connect to database '%s': %s",
362 client->database, sdb_dbi_strerror(client->conn));
363 dbi_conn_close(client->conn);
364 client->conn = NULL;
365 return -1;
366 }
367 return 0;
368 } /* sdb_dbi_client_connect */
370 int
371 sdb_dbi_client_check_conn(sdb_dbi_client_t *client)
372 {
373 if (! client)
374 return -1;
376 if (! client->conn)
377 return sdb_dbi_client_connect(client);
379 if (dbi_conn_ping(client->conn))
380 return 0;
381 return sdb_dbi_client_connect(client);
382 } /* sdb_dbi_client_check_conn */
384 int
385 sdb_dbi_exec_query(sdb_dbi_client_t *client, const char *query,
386 sdb_dbi_data_cb callback, sdb_object_t *user_data, int n, ...)
387 {
388 dbi_result res;
389 unsigned int num_fields;
391 int status;
393 if ((! client) || (! client->conn) || (! query))
394 return -1;
396 res = dbi_conn_query(client->conn, query);
397 if (! res) {
398 sdb_log(SDB_LOG_ERR, "dbi: failed to execute query '%s': %s",
399 query, sdb_dbi_strerror(client->conn));
400 return -1;
401 }
403 if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
404 sdb_log(SDB_LOG_ERR, "dbi: failed to fetch rows for query "
405 "'%s': %s", query, sdb_dbi_strerror(client->conn));
406 dbi_result_free(res);
407 return -1;
408 }
410 if (dbi_result_get_numrows(res) < 1) { /* no data */
411 dbi_result_free(res);
412 return 0;
413 }
415 num_fields = dbi_result_get_numfields(res);
417 if (n >= 0) {
418 va_list types;
419 int i;
421 if (n != (int)num_fields) {
422 sdb_log(SDB_LOG_ERR, "dbi: number of returned fields (%i) "
423 "does not match the number of requested fields (%i) "
424 "for query '%s'.", num_fields, n, query);
425 dbi_result_free(res);
426 return -1;
427 }
429 va_start(types, n);
430 status = 0;
432 for (i = 0; i < n; ++i) {
433 unsigned short field_type = dbi_result_get_field_type_idx(res,
434 (unsigned int)(i + 1));
436 unsigned int type = va_arg(types, unsigned int);
438 field_type = DBI_TYPE_TO_SC(field_type);
440 /* column count starts at 1 */
441 if ((unsigned int)field_type != type) {
442 sdb_log(SDB_LOG_ERR, "dbi: type of column '%s' (%u) "
443 "does not match requested type (%u).",
444 dbi_result_get_field_name(res, (unsigned int)i + 1),
445 field_type, type);
446 status = -1;
447 }
448 }
450 va_end(types);
452 if (status) {
453 dbi_result_free(res);
454 return status;
455 }
456 }
458 if (num_fields < 1) { /* no data */
459 dbi_result_free(res);
460 return 0;
461 }
463 status = sdb_dbi_get_data(client, res, num_fields, callback, user_data);
465 dbi_result_free(res);
466 return status;
467 } /* sdb_dbi_exec_query */
469 void
470 sdb_dbi_client_destroy(sdb_dbi_client_t *client)
471 {
472 if (! client)
473 return;
475 if (client->driver)
476 free(client->driver);
477 client->driver = NULL;
479 if (client->database)
480 free(client->database);
481 client->database = NULL;
483 if (client->conn)
484 dbi_conn_close(client->conn);
485 client->conn = NULL;
487 if (client->options)
488 sdb_dbi_options_destroy(client->options);
489 client->options = NULL;
491 free(client);
492 } /* sdb_dbi_client_destroy */
494 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */