Merge branch 'collectd-4.10' into collectd-5.0
[collectd.git] / src / oracle.c
index fa7e72f..80ae699 100644 (file)
@@ -1,6 +1,7 @@
 /**
  * collectd - src/oracle.c
- * Copyright (C) 2008  Florian octo Forster
+ * Copyright (C) 2008,2009  noris network AG
+ * Copyright (C) 2012       Florian octo Forster
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
  * affiliates. Other names may be trademarks of their respective owners.
  *
  * Authors:
- *   Florian octo Forster <octo at noris.net>
+ *   Florian octo Forster <octo at collectd.org>
  **/
 
 #include "collectd.h"
 #include "common.h"
 #include "plugin.h"
+#include "configfile.h"
+#include "utils_db_query.h"
 
 #include <oci.h>
 
 /*
  * Data types
  */
-struct o_query_s
-{
-  char    *name;
-  char    *statement;
-  char    *type;
-  char   **instances;
-  size_t   instances_num;
-  char   **values;
-  size_t   values_num;
-
-  OCIStmt *oci_statement;
-};
-typedef struct o_query_s o_query_t;
-
 struct o_database_s
 {
   char *name;
@@ -74,8 +63,9 @@ struct o_database_s
   char *username;
   char *password;
 
-  o_query_t **queries;
-  size_t      queries_num;
+  udb_query_preparation_area_t **q_prep_areas;
+  udb_query_t **queries;
+  size_t        queries_num;
 
   OCISvcCtx *oci_service_context;
 };
@@ -84,7 +74,7 @@ typedef struct o_database_s o_database_t;
 /*
  * Global variables
  */
-static o_query_t    **queries       = NULL;
+static udb_query_t  **queries       = NULL;
 static size_t         queries_num   = 0;
 static o_database_t **databases     = NULL;
 static size_t         databases_num = 0;
@@ -96,65 +86,65 @@ OCIError *oci_error = NULL;
  * Functions
  */
 static void o_report_error (const char *where, /* {{{ */
+    const char *db_name, const char *query_name,
     const char *what, OCIError *eh)
 {
   char buffer[2048];
   sb4 error_code;
   int status;
+  unsigned int record_number;
 
-  status = OCIErrorGet (eh, /* record number = */ 1,
-      /* sqlstate = */ NULL,
-      &error_code,
-      (text *) &buffer[0],
-      (ub4) sizeof (buffer),
-      OCI_HTYPE_ERROR);
-  buffer[sizeof (buffer) - 1] = 0;
+  if (db_name == NULL)
+    db_name = "(none)";
+  if (query_name == NULL)
+    query_name = "(none)";
 
-  if (status == OCI_SUCCESS)
+  /* An operation may cause / return multiple errors. Loop until we have
+   * handled all errors available (with a fail-save limit of 16). */
+  for (record_number = 1; record_number <= 16; record_number++)
   {
-    size_t buffer_length;
+    memset (buffer, 0, sizeof (buffer));
+    error_code = -1;
 
-    buffer_length = strlen (buffer);
-    while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
+    status = OCIErrorGet (eh, (ub4) record_number,
+        /* sqlstate = */ NULL,
+        &error_code,
+        (text *) &buffer[0],
+        (ub4) sizeof (buffer),
+        OCI_HTYPE_ERROR);
+    buffer[sizeof (buffer) - 1] = 0;
+
+    if (status == OCI_NO_DATA)
+      return;
+
+    if (status == OCI_SUCCESS)
     {
-      buffer_length--;
-      buffer[buffer_length] = 0;
-    }
+      size_t buffer_length;
 
-    ERROR ("oracle plugin: %s: %s failed: %s",
-        where, what, buffer);
-  }
-  else
-  {
-    ERROR ("oracle plugin: %s: %s failed. Additionally, OCIErrorGet failed with status %i.",
-        where, what, status);
+      buffer_length = strlen (buffer);
+      while ((buffer_length > 0) && (buffer[buffer_length - 1] < 32))
+      {
+        buffer_length--;
+        buffer[buffer_length] = 0;
+      }
+
+      ERROR ("oracle plugin: %s (db = %s, query = %s): %s failed: %s",
+          where, db_name, query_name, what, buffer);
+    }
+    else
+    {
+      ERROR ("oracle plugin: %s (db = %s, query = %s): %s failed. "
+          "Additionally, OCIErrorGet failed with status %i.",
+          where, db_name, query_name, what, status);
+      return;
+    }
   }
 } /* }}} void o_report_error */
 
-static void o_query_free (o_query_t *q) /* {{{ */
+static void o_database_free (o_database_t *db) /* {{{ */
 {
   size_t i;
 
-  if (q == NULL)
-    return;
-
-  sfree (q->name);
-  sfree (q->statement);
-  sfree (q->type);
-
-  for (i = 0; i < q->instances_num; i++)
-    sfree (q->instances[i]);
-  sfree (q->instances);
-
-  for (i = 0; i < q->values_num; i++)
-    sfree (q->values[i]);
-  sfree (q->values);
-
-  sfree (q);
-} /* }}} void o_query_free */
-
-static void o_database_free (o_database_t *db) /* {{{ */
-{
   if (db == NULL)
     return;
 
@@ -164,6 +154,11 @@ static void o_database_free (o_database_t *db) /* {{{ */
   sfree (db->password);
   sfree (db->queries);
 
+  if (db->q_prep_areas != NULL)
+    for (i = 0; i < db->queries_num; ++i)
+      udb_query_delete_preparation_area (db->q_prep_areas[i]);
+  free (db->q_prep_areas);
+
   sfree (db);
 } /* }}} void o_database_free */
 
@@ -172,9 +167,11 @@ static void o_database_free (o_database_t *db) /* {{{ */
  * <Plugin oracle>
  *   <Query "plugin_instance0">
  *     Statement "SELECT name, value FROM table"
- *     Type "gauge"
- *     InstancesFrom "name"
- *     ValuesFrom "value"
+ *     <Result>
+ *       Type "gauge"
+ *       InstancesFrom "name"
+ *       ValuesFrom "value"
+ *     </Result>
  *   </Query>
  *     
  *   <Database "plugin_instance1">
@@ -213,217 +210,6 @@ static int o_config_set_string (char **ret_string, /* {{{ */
   return (0);
 } /* }}} int o_config_set_string */
 
-static int o_config_add_string (char ***ret_array, /* {{{ */
-    size_t *ret_array_len, oconfig_item_t *ci)
-{
-  char **array;
-  size_t array_len;
-  int i;
-
-  if (ci->values_num < 1)
-  {
-    WARNING ("oracle plugin: The `%s' config option "
-        "needs at least one argument.", ci->key);
-    return (-1);
-  }
-
-  for (i = 0; i < ci->values_num; i++)
-  {
-    if (ci->values[i].type != OCONFIG_TYPE_STRING)
-    {
-      WARNING ("oracle plugin: Argument %i to the `%s' option "
-          "is not a string.", i + 1, ci->key);
-      return (-1);
-    }
-  }
-
-  array_len = *ret_array_len;
-  array = (char **) realloc (*ret_array,
-      sizeof (char *) * (array_len + ci->values_num));
-  if (array == NULL)
-  {
-    ERROR ("oracle plugin: realloc failed.");
-    return (-1);
-  }
-  *ret_array = array;
-
-  for (i = 0; i < ci->values_num; i++)
-  {
-    array[array_len] = strdup (ci->values[i].value.string);
-    if (array[array_len] == NULL)
-    {
-      ERROR ("oracle plugin: strdup failed.");
-      *ret_array_len = array_len;
-      return (-1);
-    }
-    array_len++;
-  }
-
-  *ret_array_len = array_len;
-  return (0);
-} /* }}} int o_config_add_string */
-
-static int o_config_add_query (oconfig_item_t *ci) /* {{{ */
-{
-  o_query_t *q;
-  int status;
-  int i;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("oracle plugin: The `Query' block "
-        "needs exactly one string argument.");
-    return (-1);
-  }
-
-  q = (o_query_t *) malloc (sizeof (*q));
-  if (q == NULL)
-  {
-    ERROR ("oracle plugin: malloc failed.");
-    return (-1);
-  }
-  memset (q, 0, sizeof (*q));
-
-  status = o_config_set_string (&q->name, ci);
-  if (status != 0)
-  {
-    sfree (q);
-    return (status);
-  }
-
-  /* Fill the `o_query_t' structure.. */
-  for (i = 0; i < ci->children_num; i++)
-  {
-    oconfig_item_t *child = ci->children + i;
-
-    if (strcasecmp ("Statement", child->key) == 0)
-      status = o_config_set_string (&q->statement, child);
-    else if (strcasecmp ("Type", child->key) == 0)
-      status = o_config_set_string (&q->type, child);
-    else if (strcasecmp ("InstancesFrom", child->key) == 0)
-      status = o_config_add_string (&q->instances, &q->instances_num, child);
-    else if (strcasecmp ("ValuesFrom", child->key) == 0)
-      status = o_config_add_string (&q->values, &q->values_num, child);
-    else
-    {
-      WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
-      status = -1;
-    }
-
-    if (status != 0)
-      break;
-  }
-
-  /* Check that all necessary options have been given. */
-  while (status == 0)
-  {
-    if (q->statement == NULL)
-    {
-      WARNING ("oracle plugin: `Statement' not given for query `%s'", q->name);
-      status = -1;
-    }
-    if (q->type == NULL)
-    {
-      WARNING ("oracle plugin: `Type' not given for query `%s'", q->name);
-      status = -1;
-    }
-    if (q->instances == NULL)
-    {
-      WARNING ("oracle plugin: `InstancesFrom' not given for query `%s'", q->name);
-      status = -1;
-    }
-    if (q->values == NULL)
-    {
-      WARNING ("oracle plugin: `ValuesFrom' not given for query `%s'", q->name);
-      status = -1;
-    }
-
-    break;
-  } /* while (status == 0) */
-
-  /* If all went well, add this query to the list of queries within the
-   * database structure. */
-  if (status == 0)
-  {
-    o_query_t **temp;
-
-    temp = (o_query_t **) realloc (queries,
-        sizeof (*queries) * (queries_num + 1));
-    if (temp == NULL)
-    {
-      ERROR ("oracle plugin: realloc failed");
-      status = -1;
-    }
-    else
-    {
-      queries = temp;
-      queries[queries_num] = q;
-      queries_num++;
-    }
-  }
-
-  if (status != 0)
-  {
-    o_query_free (q);
-    return (-1);
-  }
-
-  return (0);
-} /* }}} int o_config_add_query */
-
-static int o_config_add_database_query (o_database_t *db, /* {{{ */
-    oconfig_item_t *ci)
-{
-  o_query_t *q;
-  o_query_t **temp;
-  size_t i;
-
-  if ((ci->values_num != 1)
-      || (ci->values[0].type != OCONFIG_TYPE_STRING))
-  {
-    WARNING ("oracle plugin: The `Query' config option "
-        "needs exactly one string argument.");
-    return (-1);
-  }
-
-  q = NULL;
-  for (i = 0; i < queries_num; i++)
-  {
-    if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
-    {
-      q = queries[i];
-      break;
-    }
-  }
-
-  if (q == NULL)
-  {
-    WARNING ("oracle plugin: Database `%s': Unknown query `%s'. "
-        "Please make sure that the <Query \"%s\"> block comes before "
-        "the <Database \"%s\"> block.",
-        db->name, ci->values[0].value.string,
-        ci->values[0].value.string, db->name);
-    return (-1);
-  }
-
-  temp = (o_query_t **) realloc (db->queries,
-      sizeof (*db->queries) * (db->queries_num + 1));
-  if (temp == NULL)
-  {
-    ERROR ("oracle plugin: realloc failed");
-    return (-1);
-  }
-  else
-  {
-    db->queries = temp;
-    db->queries[db->queries_num] = q;
-    db->queries_num++;
-  }
-
-  return (0);
-} /* }}} int o_config_add_database_query */
-
 static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
 {
   o_database_t *db;
@@ -465,7 +251,8 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     else if (strcasecmp ("Password", child->key) == 0)
       status = o_config_set_string (&db->password, child);
     else if (strcasecmp ("Query", child->key) == 0)
-      status = o_config_add_database_query (db, child);
+      status = udb_query_pick_from_list (child, queries, queries_num,
+          &db->queries, &db->queries_num);
     else
     {
       WARNING ("oracle plugin: Option `%s' not allowed here.", child->key);
@@ -498,6 +285,34 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     break;
   } /* while (status == 0) */
 
+  while ((status == 0) && (db->queries_num > 0))
+  {
+    db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+        db->queries_num, sizeof (*db->q_prep_areas));
+
+    if (db->q_prep_areas == NULL)
+    {
+      WARNING ("oracle plugin: malloc failed");
+      status = -1;
+      break;
+    }
+
+    for (i = 0; i < db->queries_num; ++i)
+    {
+      db->q_prep_areas[i]
+        = udb_query_allocate_preparation_area (db->queries[i]);
+
+      if (db->q_prep_areas[i] == NULL)
+      {
+        WARNING ("oracle plugin: udb_query_allocate_preparation_area failed");
+        status = -1;
+        break;
+      }
+    }
+
+    break;
+  }
+
   /* If all went well, add this query to the list of queries within the
    * database structure. */
   if (status == 0)
@@ -536,12 +351,19 @@ static int o_config (oconfig_item_t *ci) /* {{{ */
   {
     oconfig_item_t *child = ci->children + i;
     if (strcasecmp ("Query", child->key) == 0)
-      o_config_add_query (child);
+      udb_query_create (&queries, &queries_num, child,
+          /* callback = */ NULL);
     else if (strcasecmp ("Database", child->key) == 0)
       o_config_add_database (child);
     else
     {
-      WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
+      WARNING ("oracle plugin: Ignoring unknown config option `%s'.", child->key);
+    }
+
+    if (queries_num > 0)
+    {
+      DEBUG ("oracle plugin: o_config: queries_num = %zu; queries[0] = %p; udb_query_get_user_data (queries[0]) = %p;",
+          queries_num, (void *) queries[0], udb_query_get_user_data (queries[0]));
     }
   } /* for (ci->children) */
 
@@ -583,131 +405,66 @@ static int o_init (void) /* {{{ */
   return (0);
 } /* }}} int o_init */
 
-static void o_submit (o_database_t *db, o_query_t *q, /* {{{ */
-    const data_set_t *ds, char **buffer_instances, char **buffer_values)
-{
-  value_list_t vl = VALUE_LIST_INIT;
-  size_t i;
-
-  assert (((size_t) ds->ds_num) == q->values_num);
-
-  vl.values = (value_t *) malloc (sizeof (value_t) * q->values_num);
-  if (vl.values == NULL)
-  {
-    ERROR ("oracle plugin: malloc failed.");
-    return;
-  }
-  vl.values_len = ds->ds_num;
-
-  for (i = 0; i < q->values_num; i++)
-  {
-    char *endptr;
-
-    endptr = NULL;
-    errno = 0;
-    if (ds->ds[i].type == DS_TYPE_COUNTER)
-      vl.values[i].counter = (counter_t) strtoll (buffer_values[i],
-          &endptr, /* base = */ 0);
-    else if (ds->ds[i].type == DS_TYPE_GAUGE)
-      vl.values[i].gauge = (gauge_t) strtod (buffer_values[i], &endptr);
-    else
-      errno = EINVAL;
-
-    if ((endptr == buffer_values[i]) || (errno != 0))
-    {
-      WARNING ("oracle plugin: o_submit: Parsing `%s' as %s failed.",
-          buffer_values[i],
-          (ds->ds[i].type == DS_TYPE_COUNTER) ? "counter" : "gauge");
-      vl.values[i].gauge = NAN;
-    }
-  }
-
-  vl.time = time (NULL);
-  sstrncpy (vl.host, hostname_g, sizeof (vl.host));
-  sstrncpy (vl.plugin, "oracle", sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
-  sstrncpy (vl.type, q->type, sizeof (vl.type));
-  strjoin (vl.type_instance, sizeof (vl.type_instance),
-      buffer_instances, q->instances_num, "-");
-  vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
-
-  plugin_dispatch_values (&vl);
-} /* }}} void o_submit */
-
 static int o_read_database_query (o_database_t *db, /* {{{ */
-    o_query_t *q)
+    udb_query_t *q, udb_query_preparation_area_t *prep_area)
 {
-  const data_set_t *ds;
-  ub4 param_counter; /* == number of columns */
-  int status;
-  size_t i;
-  ub4 j;
+  char **column_names;
+  char **column_values;
+  size_t column_num;
 
-  /* Scratch area for OCI to write values to */
-  char **buffer_instances;
-  char **buffer_values;
-
-  /* List of indizes of the instance and value columns. Only used for error
-   * checking. */
-  size_t *index_instances;
-  size_t *index_values;
+  OCIStmt *oci_statement;
 
   /* List of `OCIDefine' pointers. These defines map columns to the buffer
- * space declared above. */
  * space declared above. */
   OCIDefine **oci_defines;
 
-  ds = plugin_get_ds (q->type); /* {{{ */
-  if (ds == NULL)
-  {
-    WARNING ("oracle plugin: o_read_database_query (%s, %s): "
-        "plugin_get_ds (%s) failed. Please check if the type exists, "
-        "see types.db(5).",
-        db->name, q->name, q->type);
-    return (-1);
-  } /* }}} */
+  int status;
+  size_t i;
 
-  if (((size_t) ds->ds_num) != q->values_num)
-  {
-    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
-        "The query `%s' uses the type `%s' which requires exactly "
-        "%i value%s, but you specified %zu value-column%s. "
-        "See types.db(5) for details.",
-        db->name, q->name,
-        q->name, q->type,
-        ds->ds_num, (ds->ds_num == 1) ? "" : "s",
-        q->values_num, (q->values_num == 1) ? "" : "s");
-    return (-1);
-  }
+  oci_statement = udb_query_get_user_data (q);
 
   /* Prepare the statement */
-  if (q->oci_statement == NULL) /* {{{ */
+  if (oci_statement == NULL) /* {{{ */
   {
-    status = OCIHandleAlloc (oci_env, (void *) &q->oci_statement,
+    const char *statement;
+
+    statement = udb_query_get_statement (q);
+    assert (statement != NULL);
+
+    status = OCIHandleAlloc (oci_env, (void *) &oci_statement,
         OCI_HTYPE_STMT, /* user_data_size = */ 0, /* user_data = */ NULL);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIHandleAlloc", oci_error);
-      q->oci_statement = NULL;
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIHandleAlloc", oci_error);
+      oci_statement = NULL;
       return (-1);
     }
 
-    status = OCIStmtPrepare (q->oci_statement, oci_error,
-        (text *) q->statement, (ub4) strlen (q->statement),
+    status = OCIStmtPrepare (oci_statement, oci_error,
+        (text *) statement, (ub4) strlen (statement),
         /* language = */ OCI_NTV_SYNTAX,
         /* mode     = */ OCI_DEFAULT);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIStmtPrepare", oci_error);
-      OCIHandleFree (q->oci_statement, OCI_HTYPE_STMT);
-      q->oci_statement = NULL;
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIStmtPrepare", oci_error);
+      OCIHandleFree (oci_statement, OCI_HTYPE_STMT);
+      oci_statement = NULL;
       return (-1);
     }
-    assert (q->oci_statement != NULL);
+    udb_query_set_user_data (q, oci_statement);
+
+    DEBUG ("oracle plugin: o_read_database_query (%s, %s): "
+        "Successfully allocated statement handle.",
+        db->name, udb_query_get_name (q));
   } /* }}} */
 
+  assert (oci_statement != NULL);
+
   /* Execute the statement */
   status = OCIStmtExecute (db->oci_service_context, /* {{{ */
-      q->oci_statement,
+      oci_statement,
       oci_error,
       /* iters = */ 0,
       /* rowoff = */ 0,
@@ -715,44 +472,50 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
       /* mode = */ OCI_DEFAULT);
   if (status != OCI_SUCCESS)
   {
-    o_report_error ("o_read_database_query", "OCIStmtExecute", oci_error);
-    ERROR ("oracle plugin: o_read_database_query: "
-        "Failing statement was: %s", q->statement);
+    o_report_error ("o_read_database_query", db->name, udb_query_get_name (q),
+        "OCIStmtExecute", oci_error);
     return (-1);
   } /* }}} */
 
   /* Acquire the number of columns returned. */
-  param_counter = 0;
-  status = OCIAttrGet (q->oci_statement, OCI_HTYPE_STMT, /* {{{ */
-      &param_counter, /* size pointer = */ NULL, 
-      OCI_ATTR_PARAM_COUNT, oci_error);
-  if (status != OCI_SUCCESS)
+  do /* {{{ */
   {
-    o_report_error ("o_read_database_query", "OCIAttrGet", oci_error);
-    return (-1);
-  } /* }}} */
+    ub4 param_counter = 0;
+    status = OCIAttrGet (oci_statement, OCI_HTYPE_STMT, /* {{{ */
+        &param_counter, /* size pointer = */ NULL, 
+        OCI_ATTR_PARAM_COUNT, oci_error);
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIAttrGet", oci_error);
+      return (-1);
+    } /* }}} */
+
+    column_num = (size_t) param_counter;
+  } while (0); /* }}} */
 
   /* Allocate the following buffers:
    * 
-   *  - buffer_instances    q->instances_num x DATA_MAX_NAME_LEN
-   *  - buffer_values       q->values_num    x NUMBER_BUFFER_SIZE
-   *  - index_instances     q->instances_num
-   *  - index_values        q->values_num
-   *  - oci_defines         q->instances_num+q->values_num
+   *  +---------------+-----------------------------------+
+   *  ! Name          ! Size                              !
+   *  +---------------+-----------------------------------+
+   *  ! column_names  ! column_num x DATA_MAX_NAME_LEN    !
+   *  ! column_values ! column_num x DATA_MAX_NAME_LEN    !
+   *  ! oci_defines   ! column_num x sizeof (OCIDefine *) !
+   *  +---------------+-----------------------------------+
+   *
    * {{{ */
 #define NUMBER_BUFFER_SIZE 64
 
 #define FREE_ALL \
-  if (buffer_instances != NULL) { \
-    sfree (buffer_instances[0]); \
-    sfree (buffer_instances); \
+  if (column_names != NULL) { \
+    sfree (column_names[0]); \
+    sfree (column_names); \
   } \
-  if (buffer_values != NULL) { \
-    sfree (buffer_values[0]); \
-    sfree (buffer_values); \
+  if (column_values != NULL) { \
+    sfree (column_values[0]); \
+    sfree (column_values); \
   } \
-  sfree (index_instances); \
-  sfree (index_values); \
   sfree (oci_defines)
 
 #define ALLOC_OR_FAIL(ptr, ptr_size) \
@@ -768,50 +531,44 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
   } while (0)
 
   /* Initialize everything to NULL so the above works. */
-  buffer_instances = NULL;
-  buffer_values    = NULL;
-  index_instances  = NULL;
-  index_values     = NULL;
-  oci_defines      = NULL;
-
-  ALLOC_OR_FAIL (buffer_instances, q->instances_num * sizeof (char *));
-  ALLOC_OR_FAIL (buffer_instances[0], q->instances_num * DATA_MAX_NAME_LEN
-      * sizeof (char));
-  for (i = 1; i < q->instances_num; i++)
-    buffer_instances[i] = buffer_instances[i - 1] + DATA_MAX_NAME_LEN;
+  column_names  = NULL;
+  column_values = NULL;
+  oci_defines   = NULL;
 
-  ALLOC_OR_FAIL (buffer_values, q->values_num * sizeof (char *));
-  ALLOC_OR_FAIL (buffer_values[0], q->values_num * NUMBER_BUFFER_SIZE
+  ALLOC_OR_FAIL (column_names, column_num * sizeof (char *));
+  ALLOC_OR_FAIL (column_names[0], column_num * DATA_MAX_NAME_LEN
       * sizeof (char));
-  for (i = 1; i < q->values_num; i++)
-    buffer_values[i] = buffer_values[i - 1] + NUMBER_BUFFER_SIZE;
+  for (i = 1; i < column_num; i++)
+    column_names[i] = column_names[i - 1] + DATA_MAX_NAME_LEN;
 
-  ALLOC_OR_FAIL (index_instances, q->instances_num * sizeof (size_t));
-  ALLOC_OR_FAIL (index_values, q->values_num * sizeof (size_t));
+  ALLOC_OR_FAIL (column_values, column_num * sizeof (char *));
+  ALLOC_OR_FAIL (column_values[0], column_num * DATA_MAX_NAME_LEN
+      * sizeof (char));
+  for (i = 1; i < column_num; i++)
+    column_values[i] = column_values[i - 1] + DATA_MAX_NAME_LEN;
 
-  ALLOC_OR_FAIL (oci_defines, (q->instances_num + q->values_num)
-      * sizeof (OCIDefine *));
+  ALLOC_OR_FAIL (oci_defines, column_num * sizeof (OCIDefine *));
   /* }}} End of buffer allocations. */
 
   /* ``Define'' the returned data, i. e. bind the columns to the buffers
-   * returned above. */
-  for (j = 1; j <= param_counter; j++) /* {{{ */
+   * allocated above. */
+  for (i = 0; i < column_num; i++) /* {{{ */
   {
     char *column_name;
-    size_t column_name_length;
-    char column_name_copy[DATA_MAX_NAME_LEN];
-    size_t i;
+    ub4 column_name_length;
     OCIParam *oci_param;
 
     oci_param = NULL;
 
-    status = OCIParamGet (q->oci_statement, OCI_HTYPE_STMT, oci_error,
-        (void *) &oci_param, j);
+    status = OCIParamGet (oci_statement, OCI_HTYPE_STMT, oci_error,
+        (void *) &oci_param, (ub4) (i + 1));
     if (status != OCI_SUCCESS)
     {
       /* This is probably alright */
-      DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);", status, status);
-      o_report_error ("o_read_database_query", "OCIParamGet", oci_error);
+      DEBUG ("oracle plugin: o_read_database_query: status = %#x (= %i);",
+          status, status);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIParamGet", oci_error);
       status = OCI_SUCCESS;
       break;
     }
@@ -822,96 +579,56 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
         &column_name, &column_name_length, OCI_ATTR_NAME, oci_error);
     if (status != OCI_SUCCESS)
     {
-      o_report_error ("o_read_database_query", "OCIAttrGet (OCI_ATTR_NAME)",
-          oci_error);
+      OCIDescriptorFree (oci_param, OCI_DTYPE_PARAM);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIAttrGet (OCI_ATTR_NAME)", oci_error);
       continue;
     }
 
-    /* Ensure null-termination. */
-    memset (column_name_copy, 0, sizeof (column_name_copy));
-    if (column_name_length >= sizeof (column_name_copy))
-      column_name_length = sizeof (column_name_copy) - 1;
-    memcpy (column_name_copy, column_name, column_name_length);
-    column_name_copy[column_name_length] = 0;
-
-    DEBUG ("oracle plugin: o_read_database_query: column_name[%u] = %s; column_name_length = %zu;",
-        (unsigned int) j, column_name_copy, column_name_length);
-
-    for (i = 0; i < q->instances_num; i++)
-    {
-      if (strcasecmp (q->instances[i], column_name_copy) != 0)
-        continue;
-
-      status = OCIDefineByPos (q->oci_statement,
-          &oci_defines[i], oci_error, j,
-          buffer_instances[i], DATA_MAX_NAME_LEN, SQLT_STR,
-          NULL, NULL, NULL, OCI_DEFAULT);
-      index_instances[i] = j;
-
-      DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> instances[%zu]",
-          (unsigned int) j, column_name_copy, i);
-      break;
-    }
+    OCIDescriptorFree (oci_param, OCI_DTYPE_PARAM);
+    oci_param = NULL;
 
-    for (i = 0; i < q->values_num; i++)
+    /* Copy the name to column_names. Warning: The ``string'' returned by OCI
+     * may not be null terminated! */
+    memset (column_names[i], 0, DATA_MAX_NAME_LEN);
+    if (column_name_length >= DATA_MAX_NAME_LEN)
+      column_name_length = DATA_MAX_NAME_LEN - 1;
+    memcpy (column_names[i], column_name, column_name_length);
+    column_names[i][column_name_length] = 0;
+
+    DEBUG ("oracle plugin: o_read_database_query: column_names[%zu] = %s; "
+        "column_name_length = %"PRIu32";",
+        i, column_names[i], (uint32_t) column_name_length);
+
+    status = OCIDefineByPos (oci_statement,
+        &oci_defines[i], oci_error, (ub4) (i + 1),
+        column_values[i], DATA_MAX_NAME_LEN, SQLT_STR,
+        NULL, NULL, NULL, OCI_DEFAULT);
+    if (status != OCI_SUCCESS)
     {
-      if (strcasecmp (q->values[i], column_name_copy) != 0)
-        continue;
-
-      status = OCIDefineByPos (q->oci_statement,
-          &oci_defines[q->instances_num + i], oci_error, j,
-          buffer_values[i], NUMBER_BUFFER_SIZE, SQLT_STR,
-          NULL, NULL, NULL, OCI_DEFAULT);
-      index_values[i] = j;
-
-      DEBUG ("oracle plugin: o_read_database_query: column[%u] (%s) -> values[%zu]",
-          (unsigned int) j, column_name_copy, i);
-      break;
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIDefineByPos", oci_error);
+      continue;
     }
   } /* for (j = 1; j <= param_counter; j++) */
   /* }}} End of the ``define'' stuff. */
 
-  /* Iterate over all indizes and check that all columns from which we're
-   * supposed to read instances or values have been found. */
-  /* {{{ */
-  status = 0;
-  for (i = 0; i < q->instances_num; i++)
-  {
-    if (index_instances[i] > 0)
-      continue;
-
-    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
-        "Instance %zu of the `%s' query should be read from the column `%s', "
-        "but that column wasn't returned by the SQL statement. Please check "
-        "your configuration.",
-        db->name, q->name, (i + 1), q->name, q->instances[i]);
-    status++;
-  }
-
-  for (i = 0; i < q->values_num; i++)
-  {
-    if (index_values[i] > 0)
-      continue;
-
-    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
-        "Value %zu of the `%s' query should be read from the column `%s', "
-        "but that column wasn't returned by the SQL statement. Please check "
-        "your configuration.",
-        db->name, q->name, (i + 1), q->name, q->values[i]);
-    status++;
-  }
-
+  status = udb_query_prepare_result (q, prep_area, hostname_g,
+      /* plugin = */ "oracle", db->name, column_names, column_num,
+      /* interval = */ 0);
   if (status != 0)
   {
+    ERROR ("oracle plugin: o_read_database_query (%s, %s): "
+        "udb_query_prepare_result failed.",
+        db->name, udb_query_get_name (q));
     FREE_ALL;
     return (-1);
   }
-  /* }}} */
 
   /* Fetch and handle all the rows that matched the query. */
   while (42) /* {{{ */
   {
-    status = OCIStmtFetch2 (q->oci_statement, oci_error,
+    status = OCIStmtFetch2 (oci_statement, oci_error,
         /* nrows = */ 1, /* orientation = */ OCI_FETCH_NEXT,
         /* fetch offset = */ 0, /* mode = */ OCI_DEFAULT);
     if (status == OCI_NO_DATA)
@@ -921,25 +638,18 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
     }
     else if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
     {
-      o_report_error ("o_read_database_query", "OCIStmtFetch2", oci_error);
+      o_report_error ("o_read_database_query", db->name,
+          udb_query_get_name (q), "OCIStmtFetch2", oci_error);
       break;
     }
 
-    for (i = 0; i < q->instances_num; i++)
-    {
-      DEBUG ("oracle plugin: o_read_database_query: "
-          "buffer_instances[%zu] = %s;",
-           i, buffer_instances[i]);
-    }
-
-    for (i = 0; i < q->values_num; i++)
+    status = udb_query_handle_result (q, prep_area, column_values);
+    if (status != 0)
     {
-      DEBUG ("oracle plugin: o_read_database_query: "
-          "buffer_values[%zu] = %s;",
-           i, buffer_values[i]);
+      WARNING ("oracle plugin: o_read_database_query (%s, %s): "
+          "udb_query_handle_result failed.",
+          db->name, udb_query_get_name (q));
     }
-
-    o_submit (db, q, ds, buffer_instances, buffer_values);
   } /* }}} while (42) */
 
   /* DEBUG ("oracle plugin: o_read_database_query: This statement succeeded: %s", q->statement); */
@@ -955,6 +665,49 @@ static int o_read_database (o_database_t *db) /* {{{ */
   size_t i;
   int status;
 
+  if (db->oci_service_context != NULL)
+  {
+    OCIServer *server_handle;
+    ub4 connection_status;
+
+    server_handle = NULL;
+    status = OCIAttrGet ((void *) db->oci_service_context, OCI_HTYPE_SVCCTX, 
+        (void *) &server_handle, /* size pointer = */ NULL,
+        OCI_ATTR_SERVER, oci_error);
+    if (status != OCI_SUCCESS)
+    {
+      o_report_error ("o_read_database", db->name, NULL, "OCIAttrGet",
+          oci_error);
+      return (-1);
+    }
+
+    if (server_handle == NULL)
+    {
+      connection_status = OCI_SERVER_NOT_CONNECTED;
+    }
+    else /* if (server_handle != NULL) */
+    {
+      connection_status = 0;
+      status = OCIAttrGet ((void *) server_handle, OCI_HTYPE_SERVER,
+          (void *) &connection_status, /* size pointer = */ NULL,
+          OCI_ATTR_SERVER_STATUS, oci_error);
+      if (status != OCI_SUCCESS)
+      {
+        o_report_error ("o_read_database", db->name, NULL, "OCIAttrGet",
+            oci_error);
+        return (-1);
+      }
+    }
+
+    if (connection_status != OCI_SERVER_NORMAL)
+    {
+      INFO ("oracle plugin: Connection to %s lost. Trying to reconnect.",
+          db->name);
+      OCIHandleFree (db->oci_service_context, OCI_HTYPE_SVCCTX);
+      db->oci_service_context = NULL;
+    }
+  } /* if (db->oci_service_context != NULL) */
+
   if (db->oci_service_context == NULL)
   {
     status = OCILogon (oci_env, oci_error,
@@ -962,14 +715,22 @@ static int o_read_database (o_database_t *db) /* {{{ */
         (OraText *) db->username, (ub4) strlen (db->username),
         (OraText *) db->password, (ub4) strlen (db->password),
         (OraText *) db->connect_id, (ub4) strlen (db->connect_id));
-    if (status != OCI_SUCCESS)
+    if ((status != OCI_SUCCESS) && (status != OCI_SUCCESS_WITH_INFO))
     {
-      o_report_error ("o_read_database", "OCILogon", oci_error);
+      char errfunc[256];
+
+      ssnprintf (errfunc, sizeof (errfunc), "OCILogon(\"%s\")", db->connect_id);
+
+      o_report_error ("o_read_database", db->name, NULL, errfunc, oci_error);
       DEBUG ("oracle plugin: OCILogon (%s): db->oci_service_context = %p;",
           db->connect_id, db->oci_service_context);
       db->oci_service_context = NULL;
       return (-1);
     }
+    else if (status == OCI_SUCCESS_WITH_INFO)
+    {
+      /* TODO: Print NOTIFY message. */
+    }
     assert (db->oci_service_context != NULL);
   }
 
@@ -977,7 +738,7 @@ static int o_read_database (o_database_t *db) /* {{{ */
       db->connect_id, db->oci_service_context);
 
   for (i = 0; i < db->queries_num; i++)
-    o_read_database_query (db, db->queries[i]);
+    o_read_database_query (db, db->queries[i], db->q_prep_areas[i]);
 
   return (0);
 } /* }}} int o_read_database */
@@ -1004,13 +765,24 @@ static int o_shutdown (void) /* {{{ */
     }
   
   for (i = 0; i < queries_num; i++)
-    if (queries[i]->oci_statement != NULL)
+  {
+    OCIStmt *oci_statement;
+
+    oci_statement = udb_query_get_user_data (queries[i]);
+    if (oci_statement != NULL)
     {
-      OCIHandleFree (queries[i]->oci_statement, OCI_HTYPE_STMT);
-      queries[i]->oci_statement = NULL;
+      OCIHandleFree (oci_statement, OCI_HTYPE_STMT);
+      udb_query_set_user_data (queries[i], NULL);
     }
+  }
   
   OCIHandleFree (oci_env, OCI_HTYPE_ENV);
+  oci_env = NULL;
+
+  udb_query_free (queries, queries_num);
+  queries = NULL;
+  queries_num = 0;
+
   return (0);
 } /* }}} int o_shutdown */