summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: bf3b8e6)
raw | patch | inline | side by side (parent: bf3b8e6)
author | Sebastian Harl <sh@tokkee.org> | |
Mon, 10 Dec 2012 14:37:48 +0000 (15:37 +0100) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Mon, 10 Dec 2012 14:37:48 +0000 (15:37 +0100) |
This module provides helper functions for using libdbi. Currently, data types
and functions for managing connection options, a database connection as well
as a generic query helper are available.
The query helper (sc_dbi_exec_query()) uses a specified callback function to
handle each row as returned from the SQL query. A custom data-type (based on
the libdbi data-types) is used to pass the query results to the handler
callback.
and functions for managing connection options, a database connection as well
as a generic query helper are available.
The query helper (sc_dbi_exec_query()) uses a specified callback function to
handle each row as returned from the SQL query. A custom data-type (based on
the libdbi data-types) is used to pass the query results to the handler
callback.
configure.ac | patch | blob | history | |
src/Makefile.am | patch | blob | history | |
src/include/utils/dbi.h | [new file with mode: 0644] | patch | blob |
src/utils/dbi.c | [new file with mode: 0644] | patch | blob |
diff --git a/configure.ac b/configure.ac
index fe9331df053cb85ef664fa5d3c1c452ef2b4bb27..d6e21c146b3d30a2af02b43f87f6833f59077271 100644 (file)
--- a/configure.ac
+++ b/configure.ac
AC_CHECK_HEADERS(libgen.h)
dnl Check for dependencies.
+AC_ARG_WITH([libdbi],
+ [AS_HELP_STRING([--with-libdbi], [libdbi support (default: auto)])],
+ [with_libdbi="$withval"],
+ [with_libdbi="yes"])
+if test "x$with_libdbi" = "xyes"; then
+ AC_CHECK_HEADERS([dbi/dbi.h],
+ [with_libdbi="yes"],
+ [with_libdbi="no (dbi/dbi.h) not found"])
+fi
+if test "x$with_libdbi" = "xyes"; then
+ AC_CHECK_LIB([dbi], [dbi_initialize],
+ [with_libdbi="yes"],
+ [with_libdbi="no (libdbi or symbol 'dbi_initialize' not found)"])
+fi
+AM_CONDITIONAL([BUILD_WITH_LIBDBI], test "x$with_libdbi" = "xyes")
+
+dnl Feature checks.
build_documentation="yes"
have_xsltproc="yes"
AC_MSG_RESULT([ Features:])
AC_MSG_RESULT([ documentation: . . . . . . $build_documentation])
AC_MSG_RESULT()
+AC_MSG_RESULT([ Libraries:])
+AC_MSG_RESULT([ libdbi: . . . . . . . . . $with_libdbi])
+AC_MSG_RESULT()
AC_MSG_RESULT([ Backends:])
AC_MSG_RESULT([ collectd: . . . . . . . . . $enable_collectd])
AC_MSG_RESULT()
diff --git a/src/Makefile.am b/src/Makefile.am
index b2816ebeebde632d506aef92971329e007fde181..4544b5d66412f0f8b7fba0601136c84edcfba5f5 100644 (file)
--- a/src/Makefile.am
+++ b/src/Makefile.am
libsyscollector_la_LIBADD = $(LIBLTDL) -lrt liboconfig/liboconfig.la
libsyscollector_la_DEPENDENCIES = liboconfig/liboconfig.la
+if BUILD_WITH_LIBDBI
+libsyscollector_la_SOURCES += \
+ utils/dbi.c include/utils/dbi.h
+libsyscollector_la_LIBADD += -ldbi
+endif
+
bin_PROGRAMS = syscollectord
syscollectord_SOURCES = daemon/syscollectord.c include/syscollector.h \
diff --git a/src/include/utils/dbi.h b/src/include/utils/dbi.h
--- /dev/null
+++ b/src/include/utils/dbi.h
@@ -0,0 +1,161 @@
+/*
+ * syscollector - src/include/utils/dbi.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SC_UTILS_DBI_H
+#define SC_UTILS_DBI_H 1
+
+#include "utils/time.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * sc_dbi_data_t:
+ * A datum retrieved from a single field of a query result row.
+ *
+ * The string and binary objects are managed by libdbi, thus, they must not be
+ * freed or modified. If you want to keep them, make sure to make a copy.
+ */
+typedef struct {
+ int type;
+ union {
+ int64_t integer; /* DBI_TYPE_INTEGER */
+ double decimal; /* DBI_TYPE_DECIMAL */
+ const char *string; /* DBI_TYPE_STRING */
+ sc_time_t datetime; /* DBI_TYPE_DATETIME */
+ struct {
+ size_t length;
+ const unsigned char *datum;
+ } binary; /* DBI_TYPE_BINARY */
+ } data;
+} sc_dbi_data_t;
+
+struct sc_dbi_options;
+typedef struct sc_dbi_options sc_dbi_options_t;
+
+struct sc_dbi_client;
+typedef struct sc_dbi_client sc_dbi_client_t;
+
+typedef int (*sc_dbi_data_cb)(sc_dbi_client_t *, size_t, sc_dbi_data_t *);
+
+/*
+ * sc_dbi_options_t:
+ * This object stores DBI connection options (key/value) (e.g. host, dbname,
+ * etc.). It may be used to dynamically create the list of options before
+ * applying it to some client object.
+ */
+sc_dbi_options_t *
+sc_dbi_options_create(void);
+
+int
+sc_dbi_options_add(sc_dbi_options_t *options,
+ const char *key, const char *value);
+
+void
+sc_dbi_options_destroy(sc_dbi_options_t *options);
+
+/*
+ * sc_dbi_client_create:
+ * Creates a new DBI client object using the specified DBI / DBD 'driver' and
+ * connecting to the specified 'database'.
+ *
+ * Returns:
+ * - the client object on success
+ * - NULL else
+ */
+sc_dbi_client_t *
+sc_dbi_client_create(const char *driver, const char *database);
+
+/*
+ * sc_dbi_client_set_options:
+ * Apply connection options to an existing client object. This has to be done
+ * before actually connecting to the database using sc_dbi_client_connect().
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_dbi_client_set_options(sc_dbi_client_t *client,
+ sc_dbi_options_t *options);
+
+/*
+ * sc_dbi_client_connect:
+ * Connect to the database using the options registered beforehand.
+ *
+ * This function may also be used to reconnect to the database.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_dbi_client_connect(sc_dbi_client_t *client);
+
+/*
+ * sc_dbi_exec_query:
+ * Execute an SQL query on the database. The specified 'callback' will be
+ * called for each row returned from the query. If 'n' is a value equal to or
+ * greater than zero, it specifies the number of columns that are expected in
+ * the query result. For each column, the caller then needs to also specify
+ * the requested type (see the DBI_TYPE_* constants). If the number or types
+ * do not match, an error will be reported and the query will fail. That is,
+ * this allows to let sc_dbi_exec_query() do basic verification of the
+ * returned values.
+ *
+ * The callback will receive the client object and an array containing the
+ * field values of the current row. Any string / binary objects are managed by
+ * libdbi, thus, it must not be freed or modified. If you need to keep the
+ * object, make sure to make a copy of it.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_dbi_exec_query(sc_dbi_client_t *client, const char *query,
+ sc_dbi_data_cb callback, int n, ...);
+
+/*
+ * sc_dbi_client_destroy:
+ * Disconnect from the database and destroy the client object.
+ */
+void
+sc_dbi_client_destroy(sc_dbi_client_t *client);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_UTILS_DBI_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/utils/dbi.c b/src/utils/dbi.c
--- /dev/null
+++ b/src/utils/dbi.c
@@ -0,0 +1,455 @@
+/*
+ * syscollector - src/utils/dbi.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "utils/dbi.h"
+
+#include <assert.h>
+
+#include <dbi/dbi.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * private data types
+ */
+
+typedef struct {
+ char *key;
+ char *value;
+} sc_dbi_option_t;
+
+struct sc_dbi_options {
+ sc_dbi_option_t *options;
+ size_t options_num;
+};
+
+struct sc_dbi_client {
+ char *driver;
+ char *database;
+
+ dbi_conn conn;
+
+ sc_dbi_options_t *options;
+};
+
+/*
+ * private helper functions
+ */
+
+static const char *
+sc_dbi_strerror(dbi_conn conn)
+{
+ const char *errmsg = NULL;
+ dbi_conn_error(conn, &errmsg);
+ return errmsg;
+} /* sc_dbi_strerror */
+
+static int
+sc_dbi_get_field(dbi_result res, unsigned int i,
+ int type, sc_dbi_data_t *data)
+{
+ switch (type) {
+ case DBI_TYPE_INTEGER:
+ data->data.integer = dbi_result_get_longlong_idx(res, i);
+ break;
+ case DBI_TYPE_DECIMAL:
+ data->data.decimal = dbi_result_get_double_idx(res, i);
+ break;
+ case DBI_TYPE_STRING:
+ data->data.string = dbi_result_get_string_idx(res, i);
+ break;
+ case DBI_TYPE_DATETIME:
+ {
+ /* libdbi does not provide any higher resolutions than that */
+ time_t datetime = dbi_result_get_datetime_idx(res, i);
+ data->data.datetime = SECS_TO_SC_TIME(datetime);
+ }
+ break;
+ case DBI_TYPE_BINARY:
+ {
+ size_t length = dbi_result_get_field_length_idx(res, i);
+ const unsigned char *datum = dbi_result_get_binary_idx(res, i);
+ data->data.binary.length = length;
+ data->data.binary.datum = datum;
+ }
+ break;
+ default:
+ fprintf(stderr, "dbi: Unexpected type %i while "
+ "parsing query result.\n", type);
+ return -1;
+ }
+
+ data->type = type;
+ return 0;
+} /* sc_dbi_get_field */
+
+static int
+sc_dbi_get_data(sc_dbi_client_t *client, dbi_result res,
+ unsigned int num_fields, sc_dbi_data_cb callback)
+{
+ sc_dbi_data_t data[num_fields];
+ int types[num_fields];
+ unsigned int i;
+
+ unsigned long long num_rows;
+ unsigned long long success = 0, n;
+
+ assert(client && res && callback);
+ assert(num_fields > 0);
+
+ for (i = 0; i < num_fields; ++i) {
+ types[i] = dbi_result_get_field_type_idx(res, i + 1);
+ if (types[i] == DBI_TYPE_ERROR) {
+ fprintf(stderr, "dbi: failed to fetch data: %s\n",
+ sc_dbi_strerror(client->conn));
+ return -1;
+ }
+ }
+
+ num_rows = dbi_result_get_numrows(res);
+ if (num_rows < 1)
+ return -1;
+
+ for (n = 0; n < num_rows; ++n) {
+ if (! dbi_result_seek_row(res, n + 1)) {
+ fprintf(stderr, "dbi: Failed to retrieve row %llu: %s\n",
+ n, sc_dbi_strerror(client->conn));
+ continue;
+ }
+
+ for (i = 0; i < num_fields; ++i)
+ if (sc_dbi_get_field(res, (unsigned int)(i + 1),
+ types[i], &data[i]))
+ continue;
+
+ if (callback(client, num_fields, data))
+ continue;
+
+ ++success;
+ }
+
+ if (! success)
+ return -1;
+ return 0;
+} /* sc_dbi_get_data */
+
+/*
+ * public API
+ */
+
+sc_dbi_options_t *
+sc_dbi_options_create(void)
+{
+ sc_dbi_options_t *options;
+
+ options = malloc(sizeof(options));
+ if (! options)
+ return NULL;
+
+ options->options = NULL;
+ options->options_num = 0;
+ return options;
+} /* sc_dbi_options_create */
+
+int
+sc_dbi_options_add(sc_dbi_options_t *options,
+ const char *key, const char *value)
+{
+ sc_dbi_option_t *new;
+
+ if ((! options) || (! key) || (! value))
+ return -1;
+
+ new = realloc(options->options,
+ (options->options_num + 1) * sizeof(*options->options));
+ if (! new)
+ return -1;
+
+ options->options = new;
+ new = options->options + options->options_num;
+
+ new->key = strdup(key);
+ new->value = strdup(value);
+
+ if ((! new->key) || (! new->value)) {
+ if (new->key)
+ free(new->key);
+ if (new->value)
+ free(new->value);
+ return -1;
+ }
+
+ ++options->options_num;
+ return 0;
+} /* sc_dbi_options_add */
+
+void
+sc_dbi_options_destroy(sc_dbi_options_t *options)
+{
+ size_t i;
+
+ if (! options)
+ return;
+
+ for (i = 0; i < options->options_num; ++i) {
+ sc_dbi_option_t *opt = options->options + i;
+
+ if (opt->key)
+ free(opt->key);
+ if (opt->value)
+ free(opt->value);
+ }
+
+ if (options->options)
+ free(options->options);
+ options->options = NULL;
+ options->options_num = 0;
+ free(options);
+} /* sc_dbi_options_destroy */
+
+sc_dbi_client_t *
+sc_dbi_client_create(const char *driver, const char *database)
+{
+ sc_dbi_client_t *client;
+
+ if ((! driver) || (! database))
+ return NULL;
+
+ client = malloc(sizeof(*client));
+ if (! client)
+ return NULL;
+ memset(client, 0, sizeof(*client));
+
+ client->conn = NULL;
+ client->options = NULL;
+
+ client->driver = strdup(driver);
+ client->database = strdup(database);
+ if ((! client->driver) || (! client->database)) {
+ sc_dbi_client_destroy(client);
+ return NULL;
+ }
+ return client;
+} /* sc_dbi_client_create */
+
+int
+sc_dbi_client_set_options(sc_dbi_client_t *client,
+ sc_dbi_options_t *options)
+{
+ if (! client)
+ return -1;
+
+ if (client->options)
+ sc_dbi_options_destroy(client->options);
+ client->options = options;
+ return 0;
+} /* sc_dbi_client_set_options */
+
+int
+sc_dbi_client_connect(sc_dbi_client_t *client)
+{
+ dbi_driver driver;
+ size_t i;
+
+ if ((! client) || (! client->driver) || (! client->database))
+ return -1;
+
+ if (client->conn)
+ dbi_conn_close(client->conn);
+
+ driver = dbi_driver_open(client->driver);
+ if (! driver) {
+ fprintf(stderr, "dbi: failed to open DBI driver '%s'; "
+ "possibly it's not installed.\n",
+ client->driver);
+
+ fprintf(stderr, "dbi: known drivers:\n");
+ for (driver = dbi_driver_list(NULL); driver;
+ driver = dbi_driver_list(driver)) {
+ fprintf(stderr, "\t- %s\n", dbi_driver_get_name(driver));
+ }
+ return -1;
+ }
+
+ client->conn = dbi_conn_open(driver);
+ if (! client->conn) {
+ fprintf(stderr, "dbi: failed to open connection object.\n");
+ return -1;
+ }
+
+ if (client->options) {
+ for (i = 0; i < client->options->options_num; ++i) {
+ const char *opt;
+
+ if (! dbi_conn_set_option(client->conn,
+ client->options->options[i].key,
+ client->options->options[i].value))
+ continue;
+ /* else: error */
+
+ fprintf(stderr, "dbi: failed to set option '%s': %s\n",
+ client->options->options[i].key,
+ sc_dbi_strerror(client->conn));
+
+ fprintf(stderr, "dbi: known driver options:\n");
+ for (opt = dbi_conn_get_option_list(client->conn, NULL); opt;
+ opt = dbi_conn_get_option_list(client->conn, opt))
+ fprintf(stderr, "\t- %s\n", opt);
+
+ dbi_conn_close(client->conn);
+ return -1;
+ }
+ }
+
+ if (dbi_conn_set_option(client->conn, "dbname", client->database)) {
+ fprintf(stderr, "dbi: failed to set option 'dbname': %s\n",
+ sc_dbi_strerror(client->conn));
+ dbi_conn_close(client->conn);
+ return -1;
+ }
+
+ if (dbi_conn_connect(client->conn) < 0) {
+ fprintf(stderr, "dbi: failed to connect to database '%s': %s\n",
+ client->database, sc_dbi_strerror(client->conn));
+ dbi_conn_close(client->conn);
+ return -1;
+ }
+ return 0;
+} /* sc_dbi_client_connect */
+
+int
+sc_dbi_exec_query(sc_dbi_client_t *client, const char *query,
+ sc_dbi_data_cb callback, int n, ...)
+{
+ dbi_result res;
+ unsigned int num_fields;
+
+ int status;
+
+ if ((! client) || (! query))
+ return -1;
+
+ res = dbi_conn_query(client->conn, query);
+ if (! res) {
+ fprintf(stderr, "dbi: failed to execute query '%s': %s\n",
+ query, sc_dbi_strerror(client->conn));
+ return -1;
+ }
+
+ if (dbi_result_get_numrows(res) == DBI_ROW_ERROR) {
+ fprintf(stderr, "dbi: failed to fetch rows for query '%s': %s\n",
+ query, sc_dbi_strerror(client->conn));
+ dbi_result_free(res);
+ return -1;
+ }
+
+ if (dbi_result_get_numrows(res) < 1) { /* no data */
+ dbi_result_free(res);
+ return 0;
+ }
+
+ num_fields = dbi_result_get_numfields(res);
+
+ if (n >= 0) {
+ va_list types;
+ int i;
+
+ if (n != (int)num_fields) {
+ fprintf(stderr, "dbi: number of returned fields (%i) does not "
+ "match the number of requested fields (%i) "
+ "for query '%s'.\n", num_fields, n, query);
+ dbi_result_free(res);
+ return -1;
+ }
+
+ va_start(types, n);
+ status = 0;
+
+ for (i = 0; i < n; ++i) {
+ unsigned short field_type = dbi_result_get_field_type_idx(res,
+ (unsigned int)(i + 1));
+
+ unsigned int type = va_arg(types, unsigned int);
+
+ /* column count starts at 1 */
+ if ((unsigned int)field_type != type) {
+ fprintf(stderr, "dbi: type of column '%s' (%u) does not match "
+ "requested type (%u).\n",
+ dbi_result_get_field_name(res, (unsigned int)i + 1),
+ field_type, type);
+ status = -1;
+ }
+ }
+
+ va_end(types);
+
+ if (status) {
+ dbi_result_free(res);
+ return status;
+ }
+ }
+
+ if (num_fields < 1) { /* no data */
+ dbi_result_free(res);
+ return 0;
+ }
+
+ status = sc_dbi_get_data(client, res, num_fields, callback);
+
+ dbi_result_free(res);
+ return status;
+} /* sc_dbi_exec_query */
+
+void
+sc_dbi_client_destroy(sc_dbi_client_t *client)
+{
+ if (! client)
+ return;
+
+ if (client->driver)
+ free(client->driver);
+ client->driver = NULL;
+
+ if (client->database)
+ free(client->database);
+ client->database = NULL;
+
+ if (client->conn)
+ dbi_conn_close(client->conn);
+
+ if (client->options)
+ sc_dbi_options_destroy(client->options);
+ client->options = NULL;
+
+ free(client);
+} /* sc_dbi_client_destroy */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+