postgresql plugin: Added support for <Result> blocks.
authorSebastian Harl <sh@tokkee.org>
Sun, 1 Feb 2009 21:48:01 +0000 (22:48 +0100)
committerSebastian Harl <sh@tokkee.org>
Sun, 1 Feb 2009 22:13:53 +0000 (23:13 +0100)
This is a replacement for the "Column" config option (which has been
deprecated). <Result> blocks allow for much more flexible handling of the
data. Besides being able to use data types with multiple data sources, the
plugin now supports to generate the type instance using information from the
database.

An example <Result> block might look like this (taken from the manpage):

  <Result>
    Type counter
    InstancePrefix "rt36_tickets"
    InstancesFrom "type"
    ValuesFrom "count"
  </Result>

src/collectd.conf.pod
src/postgresql.c
src/postgresql_default.conf

index a92497f..00c7ed3 100644 (file)
@@ -1589,10 +1589,28 @@ L<http://www.postgresql.org/docs/manuals/>.
 
   <Plugin postgresql>
     <Query magic>
-      Statement "SELECT magic, spells FROM wizard WHERE host = $1;"
+      Statement "SELECT magic FROM wizard WHERE host = $1;"
       Param hostname
-      Column gauge magic
-      Column counter spells
+      <Result>
+        Type gauge
+        InstancePrefix "magic"
+        ValuesFrom magic
+      </Result>
+    </Query>
+
+    <Query rt36_tickets>
+      Statement "SELECT COUNT(type) AS count, type \
+                        FROM (SELECT CASE \
+                                     WHEN resolved = 'epoch' THEN 'open' \
+                                     ELSE 'resolved' END AS type \
+                                     FROM tickets) type \
+                        GROUP BY type;"
+      <Result>
+        Type counter
+        InstancePrefix "rt36_tickets"
+        InstancesFrom "type"
+        ValuesFrom "count"
+      </Result>
     </Query>
 
     <Database foo>
@@ -1604,15 +1622,25 @@ L<http://www.postgresql.org/docs/manuals/>.
       KRBSrvName "kerberos_service_name"
       Query magic
     </Database>
+
     <Database bar>
       Service "service_name"
+      Query backend # predefined
+      Query rt36_tickets
     </Database>
   </Plugin>
 
 The B<Query> block defines one database query which may later be used by a
 database definition. It accepts a single mandatory argument which specifies
-the name of the query. The names of all queries have to be unique. The
-following configuration options are available to define the query:
+the name of the query. The names of all queries have to be unique (see the
+B<MinPGVersion> and B<MaxPGVersion> options below for an exception to this
+rule). The following configuration options are available to define the query:
+
+In each B<Query> block, there is one or more B<Result> blocks. B<Result>
+blocks define how to handle the values returned from the query. They define
+which column holds which value and how to dispatch that value to the daemon.
+Multiple B<Result> blocks may be used to extract multiple values from a single
+query.
 
 =over 4
 
@@ -1628,6 +1656,8 @@ Any SQL command which may return data (such as C<SELECT> or C<SHOW>) is
 allowed. Note, however, that only a single command may be used. Semicolons are
 allowed as long as a single non-empty command has been specified only.
 
+The returned lines will be handled separately one after another.
+
 =item B<Query> I<sql query statement>
 
 This is a deprecated synonym for B<Statement>. It will be removed in version 5
@@ -1660,12 +1690,63 @@ The username used to connect to the database.
 Please note that parameters are only supported by PostgreSQL's protocol
 version 3 and above which was introduced in version 7.4 of PostgreSQL.
 
+=item B<Type> I<type>
+
+The I<type> name to be used when dispatching the values. The type describes
+how to handle the data and where to store it. See L<types.db(5)> for more
+details on types and their configuration. The number and type of values (as
+selected by the B<ValuesFrom> option) has to match the type of the given name.
+
+This option is required inside a B<Result> block.
+
+=item B<InstancePrefix> I<prefix>
+
+=item B<InstancesFrom> I<column0> [I<column1> ...]
+
+Specify how to create the "TypeInstance" for each data set (i.E<nbsp>e. line).
+B<InstancePrefix> defines a static prefix that will be prepended to all type
+instances. B<InstancesFrom> defines the column names whose values will be used
+to create the type instance. Multiple values will be joined together using the
+hyphen (C<->) as separation character.
+
+The plugin itself does not check whether or not all built instances are
+different. It is your responsibility to assure that each is unique.
+
+Both options are optional. If none is specified, the type instance will be
+empty.
+
+=item B<ValuesFrom> I<column0> [I<column1> ...]
+
+Names the columns whose content is used as the actual data for the data sets
+that are dispatched to the daemon. How many such columns you need is
+determined by the B<Type> setting as explained above. If you specify too many
+or not enough columns, the plugin will complain about that and no data will be
+submitted to the daemon.
+
+The actual data type, as seen by PostgreSQL, is not that important as long as
+it represents numbers. The plugin will automatically cast the values to the
+right type if it know how to do that. For that, it uses the L<strtoll(3)> and
+L<strtod(3)> functions, so anything supported by those functions is supported
+by the plugin as well.
+
+This option is required inside a B<Result> block and may be specified multiple
+times. If multiple B<ValuesFrom> options are specified, the columns are read
+in the given order.
+
 =item B<Column> I<type> [I<type instance>]
 
-Specify the I<type> and optional I<type instance> used to dispatch the value
-of each result column. Detailed information about types and their
-configuration can be found in L<types.db(5)>. The number and order of the
-B<Column> options has to match the columns of the query result.
+This is a deprecated alternative to a B<Result> block. It will be removed in
+version 5 of collectd. It is equivalent to the following B<Result> block:
+
+  <Result>
+    Type I<type>
+    InstancePrefix I<type instance>
+    ValuesFrom I<name of the x. column>
+  </Result>
+
+The order of the B<Column> options defines which columns of the query result
+should be used. The first option specifies the data found in the first column,
+the second option that of the second column, and so on.
 
 =item B<MinPGVersion> I<version>
 
index ccd357c..bb2f0d2 100644 (file)
@@ -83,10 +83,17 @@ typedef enum {
 } c_psql_param_t;
 
 typedef struct {
-       char *type;
-       char *type_instance;
-       int   ds_type;
-} c_psql_col_t;
+       char  *type;
+       char  *instance_prefix;
+       char **instances_str;
+       int   *instances;
+       int    instances_num;
+       char **values_str; /* may be NULL, even if values_num != 0 in
+                             case the "Column" option has been used */
+       int   *values;
+       int   *ds_types;
+       int    values_num;
+} c_psql_result_t;
 
 typedef struct {
        char *name;
@@ -95,8 +102,8 @@ typedef struct {
        c_psql_param_t *params;
        int             params_num;
 
-       c_psql_col_t *cols;
-       int           cols_num;
+       c_psql_result_t *results;
+       int              results_num;
 
        int min_pg_version;
        int max_pg_version;
@@ -145,6 +152,54 @@ static int             queries_num      = 0;
 static c_psql_database_t *databases     = NULL;
 static int                databases_num = 0;
 
+static c_psql_result_t *c_psql_result_new (c_psql_query_t *query)
+{
+       c_psql_result_t *res;
+
+       ++query->results_num;
+       if (NULL == (query->results = (c_psql_result_t *)realloc (query->results,
+                                       query->results_num * sizeof (*query->results)))) {
+               log_err ("Out of memory.");
+               exit (5);
+       }
+       res = query->results + query->results_num - 1;
+
+       res->type    = NULL;
+
+       res->instance_prefix = NULL;
+       res->instances_str   = NULL;
+       res->instances       = NULL;
+       res->instances_num   = 0;
+
+       res->values_str = NULL;
+       res->values     = NULL;
+       res->ds_types   = NULL;
+       res->values_num = 0;
+       return res;
+} /* c_psql_result_new */
+
+static void c_psql_result_delete (c_psql_result_t *res)
+{
+       int i;
+
+       sfree (res->type);
+
+       sfree (res->instance_prefix);
+
+       for (i = 0; i < res->instances_num; ++i)
+               sfree (res->instances_str[i]);
+       sfree (res->instances_str);
+       sfree (res->instances);
+       res->instances_num = 0;
+
+       for (i = 0; (NULL != res->values_str) && (i < res->values_num); ++i)
+               sfree (res->values_str[i]);
+       sfree (res->values_str);
+       sfree (res->values);
+       sfree (res->ds_types);
+       res->values_num = 0;
+} /* c_psql_result_delete */
+
 static c_psql_query_t *c_psql_query_new (const char *name)
 {
        c_psql_query_t *query;
@@ -163,14 +218,46 @@ static c_psql_query_t *c_psql_query_new (const char *name)
        query->params     = NULL;
        query->params_num = 0;
 
-       query->cols     = NULL;
-       query->cols_num = 0;
+       query->results     = NULL;
+       query->results_num = 0;
 
        query->min_pg_version = 0;
        query->max_pg_version = INT_MAX;
        return query;
 } /* c_psql_query_new */
 
+static int c_psql_query_init (c_psql_query_t *query)
+{
+       int i;
+
+       /* Get the data set definitions for each query definition. */
+       for (i = 0; i < query->results_num; ++i) {
+               c_psql_result_t  *res = query->results + i;
+               const data_set_t *ds;
+
+               int j;
+
+               ds = plugin_get_ds (res->type);
+               if (NULL == ds) {
+                       log_err ("Result: Unknown type \"%s\".", res->type);
+                       return -1;
+               }
+
+               if (res->values_num != ds->ds_num) {
+                       log_err ("Result: Invalid type \"%s\" - "
+                                       "expected %i data source%s, got %i.",
+                                       res->type, res->values_num,
+                                       (1 == res->values_num) ? "" : "s",
+                                       ds->ds_num);
+                       return -1;
+               }
+
+               for (j = 0; j < res->values_num; ++j)
+                       res->ds_types[j] = ds->ds[j].type;
+       }
+       return 0;
+} /* c_psql_query_init */
+
 static void c_psql_query_delete (c_psql_query_t *query)
 {
        int i;
@@ -181,12 +268,10 @@ static void c_psql_query_delete (c_psql_query_t *query)
        sfree (query->params);
        query->params_num = 0;
 
-       for (i = 0; i < query->cols_num; ++i) {
-               sfree (query->cols[i].type);
-               sfree (query->cols[i].type_instance);
-       }
-       sfree (query->cols);
-       query->cols_num = 0;
+       for (i = 0; i < query->results_num; ++i)
+               c_psql_result_delete (query->results + i);
+       sfree (query->results);
+       query->results_num = 0;
        return;
 } /* c_psql_query_delete */
 
@@ -294,14 +379,18 @@ static void c_psql_database_delete (c_psql_database_t *db)
        return;
 } /* c_psql_database_delete */
 
-static void submit (const c_psql_database_t *db,
-               const char *type, const char *type_instance,
-               value_t *values, size_t values_len)
+static void submit (const c_psql_database_t *db, const c_psql_result_t *res,
+               char **instances, value_t *values)
 {
        value_list_t vl = VALUE_LIST_INIT;
 
+       int instances_num = res->instances_num;
+
+       if (NULL != res->instance_prefix)
+               ++instances_num;
+
        vl.values     = values;
-       vl.values_len = values_len;
+       vl.values_len = res->values_num;
        vl.time       = time (NULL);
 
        if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
@@ -313,42 +402,114 @@ static void submit (const c_psql_database_t *db,
        sstrncpy (vl.plugin, "postgresql", sizeof (vl.plugin));
        sstrncpy (vl.plugin_instance, db->database, sizeof (vl.plugin_instance));
 
-       sstrncpy (vl.type, type, sizeof (vl.type));
+       sstrncpy (vl.type, res->type, sizeof (vl.type));
 
-       if (NULL != type_instance)
-               sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
+       if (0 < instances_num) {
+               vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+               strjoin (vl.type_instance, sizeof (vl.type_instance),
+                               instances, instances_num, "-");
+
+               if ('\0' != vl.type_instance[sizeof (vl.type_instance) - 1]) {
+                       vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+                       log_warn ("Truncated type instance: %s.", vl.type_instance);
+               }
+       }
 
        plugin_dispatch_values (&vl);
        return;
 } /* submit */
 
-static void submit_counter (const c_psql_database_t *db,
-               const char *type, const char *type_instance,
-               const char *value)
+static int c_psql_get_colnum (PGresult *pgres,
+               char **strings, int *numbers, int idx)
 {
-       value_t values[1];
+       int colnum;
 
-       if ((NULL == value) || ('\0' == *value))
-               return;
+       if (0 <= numbers[idx])
+               return numbers[idx];
 
-       values[0].counter = atoll (value);
-       submit (db, type, type_instance, values, 1);
-       return;
-} /* submit_counter */
+       colnum = PQfnumber (pgres, strings[idx]);
+       if (0 > colnum)
+               log_err ("No such column: %s.", strings[idx]);
+
+       numbers[idx] = colnum;
+       return colnum;
+} /* c_psql_get_colnum */
 
-static void submit_gauge (const c_psql_database_t *db,
-               const char *type, const char *type_instance,
-               const char *value)
+static void c_psql_dispatch_row (c_psql_database_t *db, c_psql_query_t *query,
+               PGresult *pgres, int row)
 {
-       value_t values[1];
+       int i;
 
-       if ((NULL == value) || ('\0' == *value))
-               return;
+       for (i = 0; i < query->results_num; ++i) {
+               c_psql_result_t *res = query->results + i;
 
-       values[0].gauge = atof (value);
-       submit (db, type, type_instance, values, 1);
-       return;
-} /* submit_gauge */
+               char   *instances[res->instances_num + 1];
+               value_t values[res->values_num];
+
+               int offset = 0, status = 0, j;
+
+               /* get the instance name */
+               if (NULL != res->instance_prefix) {
+                       instances[0] = res->instance_prefix;
+                       offset = 1;
+               }
+
+               for (j = 0; (0 == status) && (j < res->instances_num); ++j) {
+                       int col = c_psql_get_colnum (pgres,
+                                       res->instances_str, res->instances, j);
+
+                       if (0 > col) {
+                               status = -1;
+                               break;
+                       }
+
+                       instances[j + offset] = PQgetvalue (pgres, row, col);
+                       if (NULL == instances[j + offset])
+                               instances[j + offset] = "";
+               }
+
+               /* get the values */
+               for (j = 0; (0 == status) && (j < res->values_num); ++j) {
+                       int col = c_psql_get_colnum (pgres,
+                                       res->values_str, res->values, j);
+
+                       char *value_str;
+                       char *endptr = NULL;
+
+                       if (0 > col) {
+                               status = -1;
+                               break;
+                       }
+
+                       value_str = PQgetvalue (pgres, row, col);
+                       if ((NULL == value_str) || ('\0' == *value_str))
+                               value_str = "0";
+
+                       if (res->ds_types[j] == DS_TYPE_COUNTER)
+                               values[j].counter = (counter_t)strtoll (value_str, &endptr, 0);
+                       else if (res->ds_types[j] == DS_TYPE_GAUGE)
+                               values[j].gauge = (gauge_t)strtod (value_str, &endptr);
+                       else {
+                               log_err ("Invalid type \"%s\" (%i).",
+                                               res->type, res->ds_types[j]);
+                       }
+
+                       if (value_str == endptr) {
+                               log_err ("Failed to parse string as number: %s.", value_str);
+                               status = -1;
+                               break;
+                       }
+                       else if ((NULL != endptr) && ('\0' != *endptr))
+                               log_warn ("Ignoring trailing garbage after number: %s.",
+                                               endptr);
+               }
+
+               if (0 != status)
+                       continue;
+
+               submit (db, res, instances, values);
+       }
+} /* c_psql_dispatch_row */
 
 static int c_psql_check_connection (c_psql_database_t *db)
 {
@@ -463,28 +624,9 @@ static int c_psql_exec_query (c_psql_database_t *db, int idx)
        }
 
        cols = PQnfields (res);
-       if (query->cols_num != cols) {
-               log_err ("SQL query returned wrong number of fields "
-                               "(expected: %i, got: %i)", query->cols_num, cols);
-               log_info ("SQL query was: %s", query->stmt);
-               PQclear (res);
-               return -1;
-       }
-
-       for (i = 0; i < rows; ++i) {
-               int j;
 
-               for (j = 0; j < cols; ++j) {
-                       c_psql_col_t col = query->cols[j];
-
-                       char *value = PQgetvalue (res, i, j);
-
-                       if (col.ds_type == DS_TYPE_COUNTER)
-                               submit_counter (db, col.type, col.type_instance, value);
-                       else if (col.ds_type == DS_TYPE_GAUGE)
-                               submit_gauge (db, col.type, col.type_instance, value);
-               }
-       }
+       for (i = 0; i < rows; ++i)
+               c_psql_dispatch_row (db, query, res, i);
        PQclear (res);
        return 0;
 } /* c_psql_exec_query */
@@ -550,32 +692,11 @@ static int c_psql_init (void)
        if ((NULL == databases) || (0 == databases_num))
                return 0;
 
-       for (i = 0; i < queries_num; ++i) {
-               c_psql_query_t *query = queries + i;
-               int j;
-
-               for (j = 0; j < query->cols_num; ++j) {
-                       c_psql_col_t     *col = query->cols + j;
-                       const data_set_t *ds;
-
-                       ds = plugin_get_ds (col->type);
-                       if (NULL == ds) {
-                               log_err ("Column: Unknown type \"%s\".", col->type);
-                               c_psql_shutdown ();
-                               return -1;
-                       }
-
-                       if (1 != ds->ds_num) {
-                               log_err ("Column: Invalid type \"%s\" - types defining "
-                                               "one data source are supported only (got: %i).",
-                                               col->type, ds->ds_num);
-                               c_psql_shutdown ();
-                               return -1;
-                       }
-
-                       col->ds_type = ds->ds[0].type;
+       for (i = 0; i < queries_num; ++i)
+               if (0 != c_psql_query_init (queries + i)) {
+                       c_psql_shutdown ();
+                       return -1;
                }
-       }
 
        for (i = 0; i < databases_num; ++i) {
                c_psql_database_t *db = databases + i;
@@ -661,6 +782,34 @@ static int config_set_i (char *name, int *var, const oconfig_item_t *ci)
        return 0;
 } /* config_set_i */
 
+static int config_append_array_s (char *name, char ***var, int *len,
+               const oconfig_item_t *ci)
+{
+       int i;
+
+       if ((0 != ci->children_num) || (1 > ci->values_num)) {
+               log_err ("%s expects at least one argument.", name);
+               return 1;
+       }
+
+       for (i = 0; i < ci->values_num; ++i) {
+               if (OCONFIG_TYPE_STRING != ci->values[i].type) {
+                       log_err ("%s expects string arguments.", name);
+                       return 1;
+               }
+       }
+
+       *len += ci->values_num;
+       if (NULL == (*var = (char **)realloc (*var, *len * sizeof (**var)))) {
+               log_err ("Out of memory.");
+               exit (5);
+       }
+
+       for (i = *len - ci->values_num; i < *len; ++i)
+               (*var)[i] = sstrdup (ci->values[i].value.string);
+       return 0;
+} /* config_append_array_s */
+
 static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
 {
        c_psql_param_t param;
@@ -695,9 +844,75 @@ static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
        return 0;
 } /* config_set_param */
 
-static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
+static int config_set_result (c_psql_query_t *query, const oconfig_item_t *ci)
 {
-       c_psql_col_t *col;
+       c_psql_result_t *res;
+
+       int status = 0, i;
+
+       if (0 != ci->values_num) {
+               log_err ("<Result> does not expect any arguments.");
+               return 1;
+       }
+
+       res = c_psql_result_new (query);
+
+       for (i = 0; i < ci->children_num; ++i) {
+               oconfig_item_t *c = ci->children + i;
+
+               if (0 == strcasecmp (c->key, "Type"))
+                       config_set_s ("Type", &res->type, c);
+               else if (0 == strcasecmp (c->key, "InstancePrefix"))
+                       config_set_s ("InstancePrefix", &res->instance_prefix, c);
+               else if (0 == strcasecmp (c->key, "InstancesFrom"))
+                       config_append_array_s ("InstancesFrom",
+                                       &res->instances_str, &res->instances_num, c);
+               else if (0 == strcasecmp (c->key, "ValuesFrom"))
+                       config_append_array_s ("ValuesFrom",
+                                       &res->values_str, &res->values_num, c);
+               else
+                       log_warn ("Ignoring unknown config key \"%s\".", c->key);
+       }
+
+       if (NULL == res->type) {
+               log_warn ("Query \"%s\": Missing Type option in <Result> block.",
+                               query->name);
+               status = 1;
+       }
+
+       if (NULL == res->values_str) {
+               log_warn ("Query \"%s\": Missing ValuesFrom option in <Result> block.",
+                               query->name);
+               status = 1;
+       }
+
+       if (0 != status) {
+               c_psql_result_delete (res);
+               --query->results_num;
+               return status;
+       }
+
+       /* preallocate memory to cache the column numbers and data types */
+       res->values = (int *)smalloc (res->values_num * sizeof (*res->values));
+       for (i = 0; i < res->values_num; ++i)
+               res->values[i] = -1;
+
+       res->instances = (int *)smalloc (res->instances_num
+                       * sizeof (*res->instances));
+       for (i = 0; i < res->instances_num; ++i)
+               res->instances[i] = -1;
+
+       res->ds_types = (int *)smalloc (res->values_num
+                       * sizeof (*res->ds_types));
+       for (i = 0; i < res->values_num; ++i)
+               res->ds_types[i] = -1;
+       return 0;
+} /* config_set_result */
+
+static int config_set_column (c_psql_query_t *query, int col_num,
+               const oconfig_item_t *ci)
+{
+       c_psql_result_t *res;
 
        int i;
 
@@ -714,20 +929,17 @@ static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
                }
        }
 
-       ++query->cols_num;
-       if (NULL == (query->cols = (c_psql_col_t *)realloc (query->cols,
-                               query->cols_num * sizeof (*query->cols)))) {
-               log_err ("Out of memory.");
-               exit (5);
-       }
+       res = c_psql_result_new (query);
 
-       col = query->cols + query->cols_num - 1;
+       res->type = sstrdup (ci->values[0].value.string);
 
-       col->ds_type = -1;
+       if (2 == ci->values_num)
+               res->instance_prefix = sstrdup (ci->values[1].value.string);
 
-       col->type = sstrdup (ci->values[0].value.string);
-       col->type_instance = (2 == ci->values_num)
-               ? sstrdup (ci->values[1].value.string) : NULL;
+       res->values     = (int *)smalloc (sizeof (*res->values));
+       res->values[0]  = col_num;
+       res->ds_types   = (int *)smalloc (sizeof (*res->ds_types));
+       res->values_num = 1;
        return 0;
 } /* config_set_column */
 
@@ -770,7 +982,7 @@ static int c_psql_config_query (oconfig_item_t *ci)
 {
        c_psql_query_t *query;
 
-       int status = 0, i;
+       int status = 0, col_num = 0, i;
 
        if ((1 != ci->values_num)
                        || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
@@ -792,8 +1004,15 @@ static int c_psql_config_query (oconfig_item_t *ci)
                }
                else if (0 == strcasecmp (c->key, "Param"))
                        config_set_param (query, c);
-               else if (0 == strcasecmp (c->key, "Column"))
-                       config_set_column (query, c);
+               else if (0 == strcasecmp (c->key, "Result"))
+                       config_set_result (query, c);
+               /* backwards compat for versions < 4.6 */
+               else if (0 == strcasecmp (c->key, "Column")) {
+                       log_warn ("<Query>: 'Column' is deprecated - "
+                                       "use a <Result> block instead.");
+                       config_set_column (query, col_num, c);
+                       ++col_num;
+               }
                else if (0 == strcasecmp (c->key, "MinPGVersion"))
                        config_set_i ("MinPGVersion", &query->min_pg_version, c);
                else if (0 == strcasecmp (c->key, "MaxPGVersion"))
index ab0f178..9827b98 100644 (file)
@@ -1,13 +1,16 @@
 # Pre-defined queries of collectd's postgresql plugin.
 
 <Query backends>
-       Statement "SELECT count(*) \
+       Statement "SELECT count(*) AS count \
                FROM pg_stat_activity \
                WHERE datname = $1;"
 
        Param database
 
-       Column pg_numbackends
+       <Result>
+               Type "pg_numbackends"
+               ValuesFrom "count"
+       </Result>
 </Query>
 
 <Query transactions>
 
        Param database
 
-       Column pg_xact commit
-       Column pg_xact rollback
+       <Result>
+               Type "pg_xact"
+               InstancePrefix "commit"
+               ValuesFrom "xact_commit"
+       </Result>
+       <Result>
+               Type "pg_xact"
+               InstancePrefix "rollback"
+               ValuesFrom "xact_rollback"
+       </Result>
 </Query>
 
 <Query queries>
-       Statement "SELECT sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del) \
+       Statement "SELECT sum(n_tup_ins) AS ins, \
+                       sum(n_tup_upd) AS upd, \
+                       sum(n_tup_del) AS del \
                FROM pg_stat_user_tables;"
 
-       Column pg_n_tup_c ins
-       Column pg_n_tup_c upd
-       Column pg_n_tup_c del
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               ValuesFrom "del"
+       </Result>
 
        MaxPGVersion 80299
 </Query>
 
 <Query queries>
-       Statement "SELECT sum(n_tup_ins), sum(n_tup_upd), sum(n_tup_del), \
-                       sum(n_tup_hot_upd) \
+       Statement "SELECT sum(n_tup_ins) AS ins, \
+                       sum(n_tup_upd) AS upd, \
+                       sum(n_tup_del) AS del, \
+                       sum(n_tup_hot_upd) AS hot_upd \
                FROM pg_stat_user_tables;"
 
-       Column pg_n_tup_c ins
-       Column pg_n_tup_c upd
-       Column pg_n_tup_c del
-       Column pg_n_tup_c hot_upd
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "ins"
+               ValuesFrom "ins"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "upd"
+               ValuesFrom "upd"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "del"
+               ValuesFrom "del"
+       </Result>
+       <Result>
+               Type "pg_n_tup_c"
+               InstancePrefix "hot_upd"
+               ValuesFrom "hot_upd"
+       </Result>
 
        MinPGVersion 80300
 </Query>
 
 <Query query_plans>
-       Statement "SELECT sum(seq_scan), sum(seq_tup_read), \
-                       sum(idx_scan), sum(idx_tup_fetch) \
+       Statement "SELECT sum(seq_scan) AS seq, \
+                       sum(seq_tup_read) AS seq_tup_read, \
+                       sum(idx_scan) AS idx, \
+                       sum(idx_tup_fetch) AS idx_tup_fetch \
                FROM pg_stat_user_tables;"
 
-       Column pg_scan seq
-       Column pg_scan seq_tup_read
-       Column pg_scan idx
-       Column pg_scan idx_tup_fetch
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq"
+               ValuesFrom "seq"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "seq_tup_read"
+               ValuesFrom "seq_tup_read"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx"
+               ValuesFrom "idx"
+       </Result>
+       <Result>
+               Type "pg_scan"
+               InstancePrefix "idx_tup_fetch"
+               ValuesFrom "idx_tup_fetch"
+       </Result>
 </Query>
 
 <Query table_states>
-       Statement "SELECT sum(n_live_tup), sum(n_dead_tup) \
+       Statement "SELECT sum(n_live_tup) AS live, sum(n_dead_tup) AS dead \
                FROM pg_stat_user_tables;"
 
-       Column pg_n_tup_g live
-       Column pg_n_tup_g dead
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "live"
+               ValuesFrom "live"
+       </Result>
+       <Result>
+               Type "pg_n_tup_g"
+               InstancePrefix "dead"
+               ValuesFrom "dead"
+       </Result>
 
        MinPGVersion 80300
 </Query>
 
 <Query disk_io>
-       Statement "SELECT sum(heap_blks_read), sum(heap_blks_hit), \
-                       sum(idx_blks_read), sum(idx_blks_hit), \
-                       sum(toast_blks_read), sum(toast_blks_hit), \
-                       sum(tidx_blks_read), sum(tidx_blks_hit) \
+       Statement "SELECT sum(heap_blks_read) AS heap_read, \
+                       sum(heap_blks_hit) AS heap_hit, \
+                       sum(idx_blks_read) AS idx_read, \
+                       sum(idx_blks_hit) AS idx_hit, \
+                       sum(toast_blks_read) AS toast_read, \
+                       sum(toast_blks_hit) AS toast_hit, \
+                       sum(tidx_blks_read) AS tidx_read, \
+                       sum(tidx_blks_hit) AS tidx_hit \
                FROM pg_statio_user_tables;"
 
-       Column pg_blks heap_read
-       Column pg_blks heap_hit
-       Column pg_blks idx_read
-       Column pg_blks idx_hit
-       Column pg_blks toast_read
-       Column pg_blks toast_hit
-       Column pg_blks tidx_read
-       Column pg_blks tidx_hit
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_read"
+               ValuesFrom "heap_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "heap_hit"
+               ValuesFrom "heap_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_read"
+               ValuesFrom "idx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "idx_hit"
+               ValuesFrom "idx_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_read"
+               ValuesFrom "toast_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "toast_hit"
+               ValuesFrom "toast_hit"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_read"
+               ValuesFrom "tidx_read"
+       </Result>
+       <Result>
+               Type "pg_blks"
+               InstancePrefix "tidx_hit"
+               ValuesFrom "tidx_hit"
+       </Result>
 </Query>
 
 <Query disk_usage>
-       Statement "SELECT pg_database_size($1);"
+       Statement "SELECT pg_database_size($1) AS size;"
 
        Param database
 
-       Column pg_db_size
+       <Result>
+               Type pg_db_size
+               ValuesFrom "size"
+       </Result>
 </Query>
 
+# vim: set ft=config :
+