1 --- rrdtool-1.3.7/configure.ac 2009-04-28 01:01:52.000000000 +0300
2 +++ rrdtool-1.3.99909042800/configure.ac 2009-04-28 01:01:52.000000000 +0300
5 CONFIGURE_PART(Find 3rd-Party Libraries)
7 +AC_ARG_ENABLE(libdbi,AS_HELP_STRING([--disable-libdbi],[do not build in support for libdbi]),[have_libdbi=no],[
9 + LIBS="$LIBS -ldbi -ldl"
10 + AC_MSG_CHECKING(for libdbi)
12 + [AC_LANG_PROGRAM([[#include <dbi/dbi.h>]],
13 + [[dbi_initialize(NULL)]]
15 + ],[AC_DEFINE(HAVE_LIBDBI,[1],[have got libdbi installed])
16 + AC_MSG_RESULT([yes])
24 +AM_CONDITIONAL(BUILD_LIBDBI,[test $have_libdbi != no])
26 AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no])
29 echo " Build rrdcgi: $enable_rrdcgi"
30 echo " Build librrd MT: $enable_pthread"
31 echo " Link with libintl: $enable_libintl"
32 +echo " With libDBI: $have_libdbi"
34 echo " Libraries: $ALL_LIBS"
36 --- rrdtool-1.3.7/src/Makefile.am 2008-12-19 16:26:47.000000000 +0200
37 +++ rrdtool-1.3.99909042800/src/Makefile.am 2009-04-14 09:10:21.000000000 +0300
43 +RRD_C_FILES += rrd_fetch_libdbi.c
47 noinst_HEADERS += rrd_getopt.h
48 UPD_C_FILES += rrd_getopt.c rrd_getopt1.c
49 --- rrdtool-1.3.7/src/rrd_fetch.c~ 2009-04-07 10:31:53.000000000 +0300
50 +++ rrdtool-1.3.99909042800/src/rrd_fetch.c 2008-12-22 23:23:49.000000000 +0200
56 + /* handle libdbi datasources */
57 + if (strncmp("sql",filename,3)==0) {
58 + if (filename[3]==filename[4]) {
59 + return rrd_fetch_fn_libdbi(filename,cf_idx,start,end,step,ds_cnt,ds_namv,data);
64 rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
67 --- rrdtool-1.3.7/src/rrd_tool.h 2009-04-07 10:31:53.000000000 +0300
68 +++ rrdtool-1.3.99909042800/src/rrd_tool.h 2009-04-20 09:46:47.000000000 +0300
75 +int rrd_fetch_fn_libdbi(char *filename, enum cf_en cf_idx,
76 + time_t *start,time_t *end,
77 + unsigned long *step,
78 + unsigned long *ds_cnt,
80 + rrd_value_t **data);
83 #define RRD_READONLY (1<<0)
84 #define RRD_READWRITE (1<<1)
85 #define RRD_CREAT (1<<2)
86 --- rrdtool-1.3.7/src/rrd_fetch_libdbi.c 1970-01-01 03:00:00.000000000 +0300
87 +++ rrdtool-1.3.99909042800/src/rrd_fetch_libdbi.c 2008-11-20 15:04:38.000000000 +0200
89 +#include "rrd_tool.h"
94 +struct sql_table_helper {
98 + char const* filename;
99 + char const* dbdriver;
107 +/* the prototypes */
108 +void _sql_close(struct sql_table_helper* th);
109 +int _sql_setparam(struct sql_table_helper* th,char* key, char* value);
110 +int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered);
111 +char* _find_next_separator(char* start,char separator);
112 +char* _find_next_separator_twice(char*start,char separator);
113 +char _hexcharhelper(char c);
114 +int _inline_unescape (char* string);
115 +double rrd_fetch_dbi_double(dbi_result *result,int idx);
116 +long rrd_fetch_dbi_long(dbi_result *result,int idx);
120 +/* helpers to get correctly converted values from DB*/
121 +long rrd_fetch_dbi_long(dbi_result *result,int idx) {
124 + /* get the attributes for this filed */
125 + unsigned int attr=dbi_result_get_field_attribs_idx(result,idx);
126 + unsigned int type=dbi_result_get_field_type_idx(result,idx);
127 + /* return NAN if NULL */
128 + if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; }
129 + /* do some conversions */
131 + case DBI_TYPE_STRING:
132 + ptmp=(char*)dbi_result_get_string_idx(result,idx);
135 + case DBI_TYPE_INTEGER:
136 + if (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx);
137 + } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx);
138 + } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx);
139 + } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx);
140 + } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx);
141 + } else { value=DNAN;
142 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); }
145 + case DBI_TYPE_DECIMAL:
146 + if (attr & DBI_DECIMAL_SIZE4) { value=floor(dbi_result_get_float_idx(result,idx));
147 + } else if (attr & DBI_DECIMAL_SIZE8) { value=floor(dbi_result_get_double_idx(result,idx));
148 + } else { value=DNAN;
149 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); }
152 + case DBI_TYPE_BINARY:
153 + attr=dbi_result_get_field_length_idx(result,idx);
154 + ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx);
156 + /* check for "known" libdbi error */
157 + if (strncmp("ERROR",ptmp,5)==0) {
158 + if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) {
159 + fprintf(stderr,"rrdtool_fetch_libDBI: you have possibly triggered a bug in libDBI by using a (TINY,MEDIUM,LONG) TEXT field with mysql\n this may trigger a core dump in at least one version of libdbi\n if you are not touched by this bug and you find this message annoying\n please set the environment-variable RRD_NO_LIBDBI_BUG_WARNING to ignore this message\n");
162 + /* convert to number */
167 + case DBI_TYPE_DATETIME:
168 + value=dbi_result_get_datetime_idx(result,idx);
171 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
178 +double rrd_fetch_dbi_double(dbi_result *result,int idx) {
181 + /* get the attributes for this filed */
182 + unsigned int attr=dbi_result_get_field_attribs_idx(result,idx);
183 + unsigned int type=dbi_result_get_field_type_idx(result,idx);
184 + /* return NAN if NULL */
185 + if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; }
186 + /* do some conversions */
188 + case DBI_TYPE_STRING:
189 + ptmp=(char*)dbi_result_get_string_idx(result,idx);
190 + value=strtod(ptmp,NULL);
192 + case DBI_TYPE_INTEGER:
193 + if (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx);
194 + } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx);
195 + } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx);
196 + } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx);
197 + } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx);
198 + } else { value=DNAN;
199 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); }
202 + case DBI_TYPE_DECIMAL:
203 + if (attr & DBI_DECIMAL_SIZE4) { value=dbi_result_get_float_idx(result,idx);
204 + } else if (attr & DBI_DECIMAL_SIZE8) { value=dbi_result_get_double_idx(result,idx);
205 + } else { value=DNAN;
206 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); }
209 + case DBI_TYPE_BINARY:
210 + attr=dbi_result_get_field_length_idx(result,idx);
211 + ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx);
213 + /* check for "known" libdbi error */
214 + if (strncmp("ERROR",ptmp,5)==0) {
215 + if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) {
216 + fprintf(stderr,"rrdtool_fetch_libDBI: you have possibly triggered a bug in libDBI by using a (TINY,MEDIUM,LONG) TEXT field with mysql\n this may trigger a core dump in at least one version of libdbi\n if you are not touched by this bug and you find this message annoying\n please set the environment-variable RRD_NO_LIBDBI_BUG_WARNING to ignore this message\n");
219 + /* convert to number */
220 + value=strtod(ptmp,NULL);
224 + case DBI_TYPE_DATETIME:
225 + value=dbi_result_get_datetime_idx(result,idx);
228 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
235 +void _sql_close(struct sql_table_helper* th) {
236 + /* close only if connected */
238 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: close connection\n",time(NULL) ); }
240 + dbi_conn_close(th->conn);
241 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: shutting down libdbi\n",time(NULL) ); }
243 + /* and assign empty */
249 +int _sql_setparam(struct sql_table_helper* th,char* key, char* value) {
250 + char* dbi_errstr=NULL;
252 + /* if not connected */
254 + /* initialize some stuff */
255 + th->table_next=th->table_start;
258 + /* initialize db */
259 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: initialize libDBI\n",time(NULL) ); }
260 + dbi_initialize(NULL);
261 + /* load the driver */
262 + driver=dbi_driver_open(th->dbdriver);
264 + rrd_set_error( "libdbi - no such driver: %s (possibly a dynamic link problem of the driver being linked without -ldbi)",th->dbdriver);
267 + /* and connect to driver */
268 + th->conn=dbi_conn_open(driver);
269 + /* and handle errors */
271 + rrd_set_error( "libdbi - could not open connection to driver %s",th->dbdriver);
276 + if (th->connected) {
277 + rrd_set_error( "we are already connected - can not set parameter %s=%s",key,value);
281 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: setting option %s to %s\n",time(NULL),key,value ); }
282 + if (dbi_conn_set_option(th->conn,key,value)) {
283 + dbi_conn_error(th->conn,(const char**)&dbi_errstr);
284 + rrd_set_error( "libdbi: problems setting %s to %s - %s",key,value,dbi_errstr);
291 +int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered) {
292 + char* dbi_errstr=NULL;
294 + time_t startt=0,endt=0;
295 + /*connect to the database if needed */
297 + rrd_set_error( "libdbi no parameters set for libdbi",th->filename,dbi_errstr);
300 + if (! th->connected) {
301 + /* and now connect */
302 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: connect to DB\n",time(NULL) ); }
303 + if (dbi_conn_connect(th->conn) <0) {
304 + dbi_conn_error(th->conn,(const char**)&dbi_errstr);
305 + rrd_set_error( "libdbi: problems connecting to db with connect string %s - error: %s",th->filename,dbi_errstr);
311 + /* now find out regarding an existing result-set */
312 + if (! th->result) {
313 + /* return if table_next is NULL */
314 + if (th->table_next==NULL) {
315 + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: reached last table to connect to\n",time(NULL) ); }
316 + /* but first close connection */
318 + /* and return with end of data */
321 + /* calculate the table to use next */
322 + th->table_start=th->table_next;
323 + th->table_next=_find_next_separator(th->table_start,'+');
324 + _inline_unescape(th->table_start);
325 + /* and prepare FULL SQL Statement */
327 + snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s ORDER BY %s",
328 + th->timestamp,th->value,th->table_start,th->where,th->timestamp);
330 + snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s",
331 + th->timestamp,th->value,th->table_start,th->where);
333 + /* and execute sql */
334 + if (getenv("RRDDEBUGSQL")) { startt=time(NULL); fprintf(stderr,"RRDDEBUGSQL: %li: executing %s\n",startt,sql); }
335 + th->result=dbi_conn_query(th->conn,sql);
336 + if (startt) { endt=time(NULL);fprintf(stderr,"RRDDEBUGSQL: %li: timing %li\n",endt,endt-startt); }
337 + /* handle error case */
338 + if (! th->result) {
339 + dbi_conn_error(th->conn,(const char**)&dbi_errstr);
340 + if (startt) { fprintf(stderr,"RRDDEBUGSQL: %li: error %s\n",endt,dbi_errstr); }
341 + rrd_set_error("libdbi: problems with query: %s - errormessage: %s",sql,dbi_errstr);
346 + /* and now fetch key and value */
347 + if (! dbi_result_next_row(th->result)) {
349 + dbi_result_free(th->result);
351 + /* and call recursively - this will open the next table or close connection as a whole*/
352 + return _sql_fetchrow(th,timestamp,value,ordered);
354 + /* and return with flag for one value */
355 + *timestamp=rrd_fetch_dbi_long(th->result,1);
356 + *value=rrd_fetch_dbi_double(th->result,2);
360 +char* _find_next_separator(char* start,char separator) {
361 + char* found=strchr(start,separator);
362 + /* have we found it */
364 + /* then 0 terminate current string */
366 + /* and return the pointer past the separator */
369 + /* not found, so return NULL */
373 +char* _find_next_separator_twice(char*start,char separator) {
375 + /* find next separator in string*/
377 + /* if found and the next one is also a separator */
378 + if (found[1] == separator) {
379 + /* then 0 terminate current string */
381 + /* and return the pointer past the current one*/
384 + /* find next occurance */
385 + found=strchr(found+1,separator);
387 + /* not found, so return NULL */
391 +char _hexcharhelper(char c) {
393 + case '0': return 0 ; break;
394 + case '1': return 1 ; break;
395 + case '2': return 2 ; break;
396 + case '3': return 3 ; break;
397 + case '4': return 4 ; break;
398 + case '5': return 5 ; break;
399 + case '6': return 6 ; break;
400 + case '7': return 7 ; break;
401 + case '8': return 8 ; break;
402 + case '9': return 9 ; break;
403 + case 'a': return 10 ; break;
404 + case 'b': return 11 ; break;
405 + case 'c': return 12 ; break;
406 + case 'd': return 13 ; break;
407 + case 'e': return 14 ; break;
408 + case 'f': return 15 ; break;
409 + case 'A': return 10 ; break;
410 + case 'B': return 11 ; break;
411 + case 'C': return 12 ; break;
412 + case 'D': return 13 ; break;
413 + case 'E': return 14 ; break;
414 + case 'F': return 15 ; break;
419 +int _inline_unescape (char* string) {
427 + /* increase src pointer by 1 skiping second % */
430 + /* try to calculate hex value from the next 2 values*/
431 + h1=_hexcharhelper(*src);
432 + if (h1<0) { rrd_set_error( "string escape error at: %s\n",string);return(1); }
433 + h2=_hexcharhelper(*(src+1));
434 + if (h2<0) { rrd_set_error( "string escape error at: %s\n",string);return(1); }
436 + /* increase src pointer by 2 skiping 2 chars */
448 +rrd_fetch_fn_libdbi(
449 + char *filename, /* name of the rrd */
450 + enum cf_en cf_idx, /* which consolidation function ?*/
452 + time_t *end, /* which time frame do you want ?
453 + * will be changed to represent reality */
454 + unsigned long *step, /* which stepsize do you want?
455 + * will be changed to represent reality */
456 + unsigned long *ds_cnt, /* number of data sources in file */
457 + char ***ds_namv, /* names of data_sources */
458 + rrd_value_t **data) /* two dimensional array containing the data */
460 + /* the separator used */
461 + char separator='/';
462 + /* a local copy of the filename - used for copying plus some pointer variables */
463 + char filenameworkcopy[10240];
464 + char *tmpptr=filenameworkcopy;
465 + char *nextptr=NULL;
466 + char *libdbiargs=NULL;
467 + char *sqlargs=NULL;
468 + /* the settings for the "works" of rrd */
470 + unsigned long minstepsize=300;
471 + /* by default assume unixtimestamp */
473 + /* the result-set */
474 + long r_timestamp,l_timestamp,d_timestamp;
475 + double r_value,l_value,d_value;
480 + /* the libdbi connection data and the table_help structure */
481 + struct sql_table_helper table_help;
483 + table_help.conn=NULL;
484 + table_help.where=where;
486 + /* some loop variables */
490 + if (strncmp("sql",filename,3)!=0) {
491 + rrd_set_error( "formatstring wrong - %s",filename );return -1;
493 + if (filename[3]!=filename[4]) {
494 + rrd_set_error( "formatstring wrong - %s",filename );return -1;
497 + /* now make this the separator */
498 + separator=filename[3];
500 + /* copy filename for local modifications during parsing */
501 + strncpy(filenameworkcopy,filename+5,sizeof(filenameworkcopy));
503 + /* get the driver */
504 + table_help.dbdriver=tmpptr;
505 + libdbiargs=_find_next_separator(tmpptr,separator);
506 + if (! libdbiargs) {
507 + /* error in argument */
508 + rrd_set_error( "formatstring wrong as we did not find \"%c\"- %s",separator,table_help.dbdriver);
512 + /* now find the next double separator - this defines the args to the database */
513 + sqlargs=_find_next_separator_twice(libdbiargs,separator);
515 + rrd_set_error( "formatstring wrong for db arguments as we did not find \"%c%c\" in \"%s\"",separator,separator,libdbiargs);
519 + /* now we can start with the SQL Statement - best to start with this first,
520 + as then the error-handling is easier, as we do not have to handle libdbi shutdown as well */
522 + /* parse the table(s) */
523 + table_help.table_start=sqlargs;
524 + nextptr=_find_next_separator(table_help.table_start,separator);
526 + /* error in argument */
527 + rrd_set_error( "formatstring wrong - %s",tmpptr);
530 + /* hex-unescape the value */
531 + if(_inline_unescape(table_help.table_start)) { return -1; }
533 + /* parse the unix timestamp column */
534 + table_help.timestamp=nextptr;
535 + nextptr=_find_next_separator(nextptr,separator);
537 + /* error in argument */
538 + rrd_set_error( "formatstring wrong - %s",tmpptr);
541 + /* if we have leading '*', then we have a TIMEDATE Field*/
542 + if (table_help.timestamp[0]=='*') { isunixtime=0; table_help.timestamp++; }
543 + /* hex-unescape the value */
544 + if(_inline_unescape(table_help.timestamp)) { return -1; }
546 + /* parse the value column */
547 + table_help.value=nextptr;
548 + nextptr=_find_next_separator(nextptr,separator);
550 + /* error in argument */
551 + rrd_set_error( "formatstring wrong - %s",tmpptr);
554 + /* hex-unescape the value */
555 + if(_inline_unescape(table_help.value)) { return -1; }
557 + /* now prepare WHERE clause as empty string*/
560 + /* and the where clause */
563 + /* find next separator */
564 + nextptr=_find_next_separator(sqlargs,separator);
565 + /* now handle fields */
566 + if (strcmp(sqlargs,"derive")==0) { /* the derive option with the default allowed max delta */
568 + } else if (strcmp(sqlargs,"prediction")==0) {
569 + rrd_set_error("argument prediction is no longer supported in a DEF - use new generic CDEF-functions instead");
571 + } else if (strcmp(sqlargs,"sigma")==0) {
572 + rrd_set_error("argument sigma is no longer supported in a DEF - use new generic CDEF-functions instead");
574 + } else if (*sqlargs==0) { /* ignore empty */
575 + } else { /* else add to where string */
576 + if (where[0]) {strcat(where," AND ");}
577 + strcat(where,sqlargs);
579 + /* and continue loop with next pointer */
583 + if(_inline_unescape(where)) { return -1; }
585 + /* now parse LIBDBI options - this start initializing libdbi and beyond this point we need to reset the db as well in case of errors*/
586 + while (libdbiargs) {
587 + /* find separator */
588 + nextptr=_find_next_separator(libdbiargs,separator);
589 + /* now find =, separating key from value*/
590 + tmpptr=_find_next_separator(libdbiargs,'=');
592 + rrd_set_error( "formatstring wrong for db arguments as we did not find \"=\" in \"%s\"",libdbiargs);
593 + _sql_close(&table_help);
596 + /* hex-unescape the value */
597 + if(_inline_unescape(tmpptr)) { return -1; }
598 + /* now handle the key/value pair */
599 + if (strcmp(libdbiargs,"rrdminstepsize")==0) { /* allow override for minstepsize */
600 + i=atoi(tmpptr);if (i>0) { minstepsize=i; }
601 + } else if (strcmp(libdbiargs,"rrdfillmissing")==0) { /* allow override for minstepsize */
602 + i=atoi(tmpptr);if (i>0) { fillmissing=i; }
603 + } else if (strcmp(libdbiargs,"rrdderivemaxstep")==0) { /* allow override for derived max delta */
604 + i=atoi(tmpptr);if (i>0) { if (derive) { derive=i; }}
605 + } else { /* store in libdbi, as these are parameters */
606 + if (_sql_setparam(&table_help,libdbiargs,tmpptr)) { return -1; }
608 + /* and continue loop with next pointer */
609 + libdbiargs=nextptr;
612 + /* and modify step if given */
613 + if (*step<minstepsize) {*step=minstepsize;}
614 + *start-=(*start)%(*step);
615 + *end-=(*end)%(*step);
617 + /* and append the SQL WHERE Clause for the timeframe calculated above (adding AND if required) */
618 + if (where[0]) {strcat(where," AND ");}
621 + snprintf(where+i,sizeof(where)-1-i,"%li < %s AND %s < %li",*start,table_help.timestamp,table_help.timestamp,*end);
623 + char tsstart[64];strftime(tsstart,sizeof(tsstart),"%Y-%m-%d %H:%M:%S",localtime(start));
624 + char tsend[64];strftime(tsend,sizeof(tsend),"%Y-%m-%d %H:%M:%S",localtime(end));
625 + snprintf(where+i,sizeof(where)-1-i,"'%s' < %s AND %s < '%s'",tsstart,table_help.timestamp,table_help.timestamp,tsend);
628 + /* and now calculate the number of rows in the resultset... */
629 + rows=((*end)-(*start))/(*step)+2;
631 + /* define the result set variables/columns returned */
633 + *ds_namv=(char**)malloc((*ds_cnt)*sizeof(char*));
634 + for (i=0;i<(int)(*ds_cnt);i++) {
635 + tmpptr=(char*)malloc(sizeof(char) * DS_NAM_SIZE);
636 + (*ds_namv)[i]=tmpptr;
637 + /* now copy what is required */
639 + case 0: strncpy(tmpptr,"min",DS_NAM_SIZE-1); break;
640 + case 1: strncpy(tmpptr,"avg",DS_NAM_SIZE-1); break;
641 + case 2: strncpy(tmpptr,"max",DS_NAM_SIZE-1); break;
642 + case 3: strncpy(tmpptr,"count",DS_NAM_SIZE-1); break;
643 + case 4: strncpy(tmpptr,"sigma",DS_NAM_SIZE-1); break;
647 + /* allocate memory for resultset (with the following columns: min,avg,max,count,sigma) */
648 + i=rows * sizeof(rrd_value_t)*(*ds_cnt);
649 + if (((*data) = malloc(i))==NULL){
650 + /* and return error */
651 + rrd_set_error("malloc failed for %i bytes",i);
654 + /* and fill with NAN */
655 + for(i=0;i<rows;i++) {
656 + (*data)[i*(*ds_cnt)+0]=DNAN; /* MIN */
657 + (*data)[i*(*ds_cnt)+1]=DNAN; /* AVG */
658 + (*data)[i*(*ds_cnt)+2]=DNAN; /* MAX */
659 + (*data)[i*(*ds_cnt)+3]=0; /* COUNT */
660 + (*data)[i*(*ds_cnt)+4]=DNAN; /* SIGMA */
662 + /* and assign undefined values for last - in case of derived calculation */
663 + l_value=DNAN;l_timestamp=0;
664 + /* here goes the real work processing all data */
665 + while((r_status=_sql_fetchrow(&table_help,&r_timestamp,&r_value,derive))>0) {
666 + /* processing of value */
667 + /* calculate index for the timestamp */
668 + idx=(r_timestamp-(*start))/(*step);
669 + /* some out of bounds checks on idx */
670 + if (idx<0) { idx=0;}
671 + if (idx>rows) { idx=rows;}
672 + /* and calculate derivative if necessary */
675 + d_timestamp=r_timestamp-l_timestamp;
676 + d_value=r_value-l_value;
677 + /* assign current as last values */
678 + l_timestamp=r_timestamp;
680 + /* assign DNAN by default for value */
682 + /* check for timestamp delta to be within an acceptable range */
683 + if ((d_timestamp>0)&&(d_timestamp<2*derive)) {
684 + /* only handle positive delta - avoid wrap-arrounds/counter resets showing up as spikes */
686 + /* and normalize to per second */
687 + r_value=d_value/d_timestamp;
691 + /* only add value if we have a value that is not NAN */
692 + if (! isnan(r_value)) {
693 + if ((*data)[idx*(*ds_cnt)+3]==0) { /* count is 0 so assign to overwrite DNAN */
694 + (*data)[idx*(*ds_cnt)+0]=r_value; /* MIN */
695 + (*data)[idx*(*ds_cnt)+1]=r_value; /* AVG */
696 + (*data)[idx*(*ds_cnt)+2]=r_value; /* MAX */
697 + (*data)[idx*(*ds_cnt)+3]=1; /* COUNT */
698 + (*data)[idx*(*ds_cnt)+4]=r_value; /* SIGMA */
701 + if ((*data)[idx*(*ds_cnt)+0]>r_value) { (*data)[idx*(*ds_cnt)+0]=r_value; }
702 + /* AVG - at this moment still sum - corrected in post processing */
703 + (*data)[idx*(*ds_cnt)+1]+=r_value;
705 + if ((*data)[idx*(*ds_cnt)+2]<r_value) { (*data)[idx*(*ds_cnt)+2]=r_value; }
707 + (*data)[idx*(*ds_cnt)+3]++;
708 + /* SIGMA - at this moment still sum of squares - corrected in post processing */
709 + (*data)[idx*(*ds_cnt)+4]+=r_value*r_value;
713 + /* and check for negativ status, pass back immediately */
714 + if (r_status==-1) { return -1; }
716 + /* post processing */
717 + for(idx=0;idx<rows;idx++) {
718 + long count=(*data)[idx*(*ds_cnt)+3];
720 + /* calc deviation first */
722 + r_value=count*(*data)[idx*(*ds_cnt)+4]-(*data)[idx*(*ds_cnt)+1]*(*data)[idx*(*ds_cnt)+1];
726 + r_value=sqrt(r_value/(count*(count-1)));
729 + (*data)[idx*(*ds_cnt)+4]=r_value;
730 + /* now the average */
731 + (*data)[idx*(*ds_cnt)+1]/=count;
735 + /* and return OK */