--- rrdtool-1.3.7/configure.ac 2009-04-28 01:01:52.000000000 +0300 +++ rrdtool-1.3.99909042800/configure.ac 2009-04-28 01:01:52.000000000 +0300 @@ -474,6 +474,24 @@ CONFIGURE_PART(Find 3rd-Party Libraries) +AC_ARG_ENABLE(libdbi,AS_HELP_STRING([--disable-libdbi],[do not build in support for libdbi]),[have_libdbi=no],[ + XXX=$LIBS + LIBS="$LIBS -ldbi -ldl" + AC_MSG_CHECKING(for libdbi) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[dbi_initialize(NULL)]] + ) + ],[AC_DEFINE(HAVE_LIBDBI,[1],[have got libdbi installed]) + AC_MSG_RESULT([yes]) + have_libdbi=yes + ],[LIBS=$XXX + AC_MSG_RESULT([no]) + have_libdbi=no + ] + ) +]) +AM_CONDITIONAL(BUILD_LIBDBI,[test $have_libdbi != no]) AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no]) @@ -759,6 +777,7 @@ echo " Build rrdcgi: $enable_rrdcgi" echo " Build librrd MT: $enable_pthread" echo " Link with libintl: $enable_libintl" +echo " With libDBI: $have_libdbi" echo echo " Libraries: $ALL_LIBS" echo --- rrdtool-1.3.7/src/Makefile.am 2008-12-19 16:26:47.000000000 +0200 +++ rrdtool-1.3.99909042800/src/Makefile.am 2009-04-14 09:10:21.000000000 +0300 @@ -54,6 +59,10 @@ fnv.h rrd_graph.h \ rrd_is_thread_safe.h +if BUILD_LIBDBI +RRD_C_FILES += rrd_fetch_libdbi.c +endif + if BUILD_GETOPT noinst_HEADERS += rrd_getopt.h UPD_C_FILES += rrd_getopt.c rrd_getopt1.c --- rrdtool-1.3.7/src/rrd_fetch.c~ 2009-04-07 10:31:53.000000000 +0300 +++ rrdtool-1.3.99909042800/src/rrd_fetch.c 2008-12-22 23:23:49.000000000 +0200 @@ -218,6 +218,15 @@ *start, *end, *step); #endif +#ifdef HAVE_LIBDBI + /* handle libdbi datasources */ + if (strncmp("sql",filename,3)==0) { + if (filename[3]==filename[4]) { + return rrd_fetch_fn_libdbi(filename,cf_idx,start,end,step,ds_cnt,ds_namv,data); + } + } +#endif + rrd_file = rrd_open(filename, &rrd, RRD_READONLY); if (rrd_file == NULL) goto err_free; --- rrdtool-1.3.7/src/rrd_tool.h 2009-04-07 10:31:53.000000000 +0300 +++ rrdtool-1.3.99909042800/src/rrd_tool.h 2009-04-20 09:46:47.000000000 +0300 @@ -93,6 +92,16 @@ char ***ds_namv, rrd_value_t **data); + +#ifdef HAVE_LIBDBI +int rrd_fetch_fn_libdbi(char *filename, enum cf_en cf_idx, + time_t *start,time_t *end, + unsigned long *step, + unsigned long *ds_cnt, + char ***ds_namv, + rrd_value_t **data); +#endif + #define RRD_READONLY (1<<0) #define RRD_READWRITE (1<<1) #define RRD_CREAT (1<<2) --- rrdtool-1.3.7/src/rrd_fetch_libdbi.c 1970-01-01 03:00:00.000000000 +0300 +++ rrdtool-1.3.99909042800/src/rrd_fetch_libdbi.c 2008-11-20 15:04:38.000000000 +0200 @@ -0,0 +1,649 @@ +#include "rrd_tool.h" +#include +#include + +/* the structures */ +struct sql_table_helper { + dbi_conn conn; + int connected; + dbi_result result; + char const* filename; + char const* dbdriver; + char* table_start; + char* table_next; + char const* where; + char * timestamp; + char * value; +}; + +/* the prototypes */ +void _sql_close(struct sql_table_helper* th); +int _sql_setparam(struct sql_table_helper* th,char* key, char* value); +int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered); +char* _find_next_separator(char* start,char separator); +char* _find_next_separator_twice(char*start,char separator); +char _hexcharhelper(char c); +int _inline_unescape (char* string); +double rrd_fetch_dbi_double(dbi_result *result,int idx); +long rrd_fetch_dbi_long(dbi_result *result,int idx); + +/* the real code */ + +/* helpers to get correctly converted values from DB*/ +long rrd_fetch_dbi_long(dbi_result *result,int idx) { + char *ptmp=""; + long value=DNAN; + /* get the attributes for this filed */ + unsigned int attr=dbi_result_get_field_attribs_idx(result,idx); + unsigned int type=dbi_result_get_field_type_idx(result,idx); + /* return NAN if NULL */ + if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; } + /* do some conversions */ + switch (type) { + case DBI_TYPE_STRING: + ptmp=(char*)dbi_result_get_string_idx(result,idx); + value=atoi(ptmp); + break; + case DBI_TYPE_INTEGER: + if (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx); + } else { value=DNAN; + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); } + } + break; + case DBI_TYPE_DECIMAL: + if (attr & DBI_DECIMAL_SIZE4) { value=floor(dbi_result_get_float_idx(result,idx)); + } else if (attr & DBI_DECIMAL_SIZE8) { value=floor(dbi_result_get_double_idx(result,idx)); + } else { value=DNAN; + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); } + } + break; + case DBI_TYPE_BINARY: + attr=dbi_result_get_field_length_idx(result,idx); + ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx); + ptmp[attr-1]=0; + /* check for "known" libdbi error */ + if (strncmp("ERROR",ptmp,5)==0) { + if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) { + 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"); + } + } + /* convert to number */ + value=atoi(ptmp); + /* free pointer */ + free(ptmp); + break; + case DBI_TYPE_DATETIME: + value=dbi_result_get_datetime_idx(result,idx); + break; + default: + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); } + value=DNAN; + break; + } + return value; +} + +double rrd_fetch_dbi_double(dbi_result *result,int idx) { + char *ptmp=""; + double value=DNAN; + /* get the attributes for this filed */ + unsigned int attr=dbi_result_get_field_attribs_idx(result,idx); + unsigned int type=dbi_result_get_field_type_idx(result,idx); + /* return NAN if NULL */ + if(dbi_result_field_is_null_idx(result,idx)) { return DNAN; } + /* do some conversions */ + switch (type) { + case DBI_TYPE_STRING: + ptmp=(char*)dbi_result_get_string_idx(result,idx); + value=strtod(ptmp,NULL); + break; + case DBI_TYPE_INTEGER: + if (attr & DBI_INTEGER_SIZE1) { value=dbi_result_get_char_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE2) { value=dbi_result_get_short_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE3) { value=dbi_result_get_int_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE4) { value=dbi_result_get_int_idx(result,idx); + } else if (attr & DBI_INTEGER_SIZE8) { value=dbi_result_get_longlong_idx(result,idx); + } else { value=DNAN; + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type INTEGER\n",time(NULL),idx,attr ); } + } + break; + case DBI_TYPE_DECIMAL: + if (attr & DBI_DECIMAL_SIZE4) { value=dbi_result_get_float_idx(result,idx); + } else if (attr & DBI_DECIMAL_SIZE8) { value=dbi_result_get_double_idx(result,idx); + } else { value=DNAN; + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported attribute flags %i for type DECIMAL\n",time(NULL),idx,attr ); } + } + break; + case DBI_TYPE_BINARY: + attr=dbi_result_get_field_length_idx(result,idx); + ptmp=(char*)dbi_result_get_binary_copy_idx(result,idx); + ptmp[attr-1]=0; + /* check for "known" libdbi error */ + if (strncmp("ERROR",ptmp,5)==0) { + if (!getenv("RRD_NO_LIBDBI_BUG_WARNING")) { + 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"); + } + } + /* convert to number */ + value=strtod(ptmp,NULL); + /* free pointer */ + free(ptmp); + break; + case DBI_TYPE_DATETIME: + value=dbi_result_get_datetime_idx(result,idx); + break; + default: + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); } + value=DNAN; + break; + } + return value; +} + +void _sql_close(struct sql_table_helper* th) { + /* close only if connected */ + if (th->conn) { + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: close connection\n",time(NULL) ); } + /* shutdown dbi */ + dbi_conn_close(th->conn); + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: shutting down libdbi\n",time(NULL) ); } + dbi_shutdown(); + /* and assign empty */ + th->conn=NULL; + th->connected=0; + } +} + +int _sql_setparam(struct sql_table_helper* th,char* key, char* value) { + char* dbi_errstr=NULL; + dbi_driver driver; + /* if not connected */ + if (! th->conn) { + /* initialize some stuff */ + th->table_next=th->table_start; + th->result=NULL; + th->connected=0; + /* initialize db */ + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: initialize libDBI\n",time(NULL) ); } + dbi_initialize(NULL); + /* load the driver */ + driver=dbi_driver_open(th->dbdriver); + if (! driver) { + rrd_set_error( "libdbi - no such driver: %s (possibly a dynamic link problem of the driver being linked without -ldbi)",th->dbdriver); + return -1; + } + /* and connect to driver */ + th->conn=dbi_conn_open(driver); + /* and handle errors */ + if (! th->conn) { + rrd_set_error( "libdbi - could not open connection to driver %s",th->dbdriver); + dbi_shutdown(); + return -1; + } + } + if (th->connected) { + rrd_set_error( "we are already connected - can not set parameter %s=%s",key,value); + _sql_close(th); + return -1; + } + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: setting option %s to %s\n",time(NULL),key,value ); } + if (dbi_conn_set_option(th->conn,key,value)) { + dbi_conn_error(th->conn,(const char**)&dbi_errstr); + rrd_set_error( "libdbi: problems setting %s to %s - %s",key,value,dbi_errstr); + _sql_close(th); + return -1; + } + return 0; +} + +int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered) { + char* dbi_errstr=NULL; + char sql[10240]; + time_t startt=0,endt=0; + /*connect to the database if needed */ + if (! th->conn) { + rrd_set_error( "libdbi no parameters set for libdbi",th->filename,dbi_errstr); + return -1; + } + if (! th->connected) { + /* and now connect */ + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: connect to DB\n",time(NULL) ); } + if (dbi_conn_connect(th->conn) <0) { + dbi_conn_error(th->conn,(const char**)&dbi_errstr); + rrd_set_error( "libdbi: problems connecting to db with connect string %s - error: %s",th->filename,dbi_errstr); + _sql_close(th); + return -1; + } + th->connected=1; + } + /* now find out regarding an existing result-set */ + if (! th->result) { + /* return if table_next is NULL */ + if (th->table_next==NULL) { + if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: reached last table to connect to\n",time(NULL) ); } + /* but first close connection */ + _sql_close(th); + /* and return with end of data */ + return 0; + } + /* calculate the table to use next */ + th->table_start=th->table_next; + th->table_next=_find_next_separator(th->table_start,'+'); + _inline_unescape(th->table_start); + /* and prepare FULL SQL Statement */ + if (ordered) { + snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s ORDER BY %s", + th->timestamp,th->value,th->table_start,th->where,th->timestamp); + } else { + snprintf(sql,sizeof(sql)-1,"SELECT %s as rrd_time, %s as rrd_value FROM %s WHERE %s", + th->timestamp,th->value,th->table_start,th->where); + } + /* and execute sql */ + if (getenv("RRDDEBUGSQL")) { startt=time(NULL); fprintf(stderr,"RRDDEBUGSQL: %li: executing %s\n",startt,sql); } + th->result=dbi_conn_query(th->conn,sql); + if (startt) { endt=time(NULL);fprintf(stderr,"RRDDEBUGSQL: %li: timing %li\n",endt,endt-startt); } + /* handle error case */ + if (! th->result) { + dbi_conn_error(th->conn,(const char**)&dbi_errstr); + if (startt) { fprintf(stderr,"RRDDEBUGSQL: %li: error %s\n",endt,dbi_errstr); } + rrd_set_error("libdbi: problems with query: %s - errormessage: %s",sql,dbi_errstr); + _sql_close(th); + return -1; + } + } + /* and now fetch key and value */ + if (! dbi_result_next_row(th->result)) { + /* free result */ + dbi_result_free(th->result); + th->result=NULL; + /* and call recursively - this will open the next table or close connection as a whole*/ + return _sql_fetchrow(th,timestamp,value,ordered); + } + /* and return with flag for one value */ + *timestamp=rrd_fetch_dbi_long(th->result,1); + *value=rrd_fetch_dbi_double(th->result,2); + return 1; +} + +char* _find_next_separator(char* start,char separator) { + char* found=strchr(start,separator); + /* have we found it */ + if (found) { + /* then 0 terminate current string */ + *found=0; + /* and return the pointer past the separator */ + return (found+1); + } + /* not found, so return NULL */ + return NULL; +} + +char* _find_next_separator_twice(char*start,char separator) { + char *found=start; + /* find next separator in string*/ + while (found) { + /* if found and the next one is also a separator */ + if (found[1] == separator) { + /* then 0 terminate current string */ + *found=0; + /* and return the pointer past the current one*/ + return (found+2); + } + /* find next occurance */ + found=strchr(found+1,separator); + } + /* not found, so return NULL */ + return NULL; +} + +char _hexcharhelper(char c) { + switch (c) { + case '0': return 0 ; break; + case '1': return 1 ; break; + case '2': return 2 ; break; + case '3': return 3 ; break; + case '4': return 4 ; break; + case '5': return 5 ; break; + case '6': return 6 ; break; + case '7': return 7 ; break; + case '8': return 8 ; break; + case '9': return 9 ; break; + case 'a': return 10 ; break; + case 'b': return 11 ; break; + case 'c': return 12 ; break; + case 'd': return 13 ; break; + case 'e': return 14 ; break; + case 'f': return 15 ; break; + case 'A': return 10 ; break; + case 'B': return 11 ; break; + case 'C': return 12 ; break; + case 'D': return 13 ; break; + case 'E': return 14 ; break; + case 'F': return 15 ; break; + } + return -1; +} + +int _inline_unescape (char* string) { + char *src=string; + char *dst=string; + char c,h1,h2; + while((c= *src)) { + src++; + if (c == '%') { + if (*src == '%') { + /* increase src pointer by 1 skiping second % */ + src+=1; + } else { + /* try to calculate hex value from the next 2 values*/ + h1=_hexcharhelper(*src); + if (h1<0) { rrd_set_error( "string escape error at: %s\n",string);return(1); } + h2=_hexcharhelper(*(src+1)); + if (h2<0) { rrd_set_error( "string escape error at: %s\n",string);return(1); } + c=h2+(h1<<4); + /* increase src pointer by 2 skiping 2 chars */ + src+=2; + } + } + *dst=c; + dst++; + } + *dst=0; + return 0; +} + +int +rrd_fetch_fn_libdbi( + char *filename, /* name of the rrd */ + enum cf_en cf_idx, /* which consolidation function ?*/ + time_t *start, + time_t *end, /* which time frame do you want ? + * will be changed to represent reality */ + unsigned long *step, /* which stepsize do you want? + * will be changed to represent reality */ + unsigned long *ds_cnt, /* number of data sources in file */ + char ***ds_namv, /* names of data_sources */ + rrd_value_t **data) /* two dimensional array containing the data */ +{ + /* the separator used */ + char separator='/'; + /* a local copy of the filename - used for copying plus some pointer variables */ + char filenameworkcopy[10240]; + char *tmpptr=filenameworkcopy; + char *nextptr=NULL; + char *libdbiargs=NULL; + char *sqlargs=NULL; + /* the settings for the "works" of rrd */ + int fillmissing=0; + unsigned long minstepsize=300; + /* by default assume unixtimestamp */ + int isunixtime=1; + /* the result-set */ + long r_timestamp,l_timestamp,d_timestamp; + double r_value,l_value,d_value; + int r_status; + int rows; + long idx; + int derive=0; + /* the libdbi connection data and the table_help structure */ + struct sql_table_helper table_help; + char where[10240]; + table_help.conn=NULL; + table_help.where=where; + + /* some loop variables */ + int i=0; + + /* check header */ + if (strncmp("sql",filename,3)!=0) { + rrd_set_error( "formatstring wrong - %s",filename );return -1; + } + if (filename[3]!=filename[4]) { + rrd_set_error( "formatstring wrong - %s",filename );return -1; + } + + /* now make this the separator */ + separator=filename[3]; + + /* copy filename for local modifications during parsing */ + strncpy(filenameworkcopy,filename+5,sizeof(filenameworkcopy)); + + /* get the driver */ + table_help.dbdriver=tmpptr; + libdbiargs=_find_next_separator(tmpptr,separator); + if (! libdbiargs) { + /* error in argument */ + rrd_set_error( "formatstring wrong as we did not find \"%c\"- %s",separator,table_help.dbdriver); + return -1; + } + + /* now find the next double separator - this defines the args to the database */ + sqlargs=_find_next_separator_twice(libdbiargs,separator); + if (!sqlargs) { + rrd_set_error( "formatstring wrong for db arguments as we did not find \"%c%c\" in \"%s\"",separator,separator,libdbiargs); + return 1; + } + + /* now we can start with the SQL Statement - best to start with this first, + as then the error-handling is easier, as we do not have to handle libdbi shutdown as well */ + + /* parse the table(s) */ + table_help.table_start=sqlargs; + nextptr=_find_next_separator(table_help.table_start,separator); + if (! nextptr) { + /* error in argument */ + rrd_set_error( "formatstring wrong - %s",tmpptr); + return -1; + } + /* hex-unescape the value */ + if(_inline_unescape(table_help.table_start)) { return -1; } + + /* parse the unix timestamp column */ + table_help.timestamp=nextptr; + nextptr=_find_next_separator(nextptr,separator); + if (! nextptr) { + /* error in argument */ + rrd_set_error( "formatstring wrong - %s",tmpptr); + return -1; + } + /* if we have leading '*', then we have a TIMEDATE Field*/ + if (table_help.timestamp[0]=='*') { isunixtime=0; table_help.timestamp++; } + /* hex-unescape the value */ + if(_inline_unescape(table_help.timestamp)) { return -1; } + + /* parse the value column */ + table_help.value=nextptr; + nextptr=_find_next_separator(nextptr,separator); + if (! nextptr) { + /* error in argument */ + rrd_set_error( "formatstring wrong - %s",tmpptr); + return -1; + } + /* hex-unescape the value */ + if(_inline_unescape(table_help.value)) { return -1; } + + /* now prepare WHERE clause as empty string*/ + where[0]=0; + + /* and the where clause */ + sqlargs=nextptr; + while(sqlargs) { + /* find next separator */ + nextptr=_find_next_separator(sqlargs,separator); + /* now handle fields */ + if (strcmp(sqlargs,"derive")==0) { /* the derive option with the default allowed max delta */ + derive=600; + } else if (strcmp(sqlargs,"prediction")==0) { + rrd_set_error("argument prediction is no longer supported in a DEF - use new generic CDEF-functions instead"); + return -1; + } else if (strcmp(sqlargs,"sigma")==0) { + rrd_set_error("argument sigma is no longer supported in a DEF - use new generic CDEF-functions instead"); + return -1; + } else if (*sqlargs==0) { /* ignore empty */ + } else { /* else add to where string */ + if (where[0]) {strcat(where," AND ");} + strcat(where,sqlargs); + } + /* and continue loop with next pointer */ + sqlargs=nextptr; + } + /* and unescape */ + if(_inline_unescape(where)) { return -1; } + + /* now parse LIBDBI options - this start initializing libdbi and beyond this point we need to reset the db as well in case of errors*/ + while (libdbiargs) { + /* find separator */ + nextptr=_find_next_separator(libdbiargs,separator); + /* now find =, separating key from value*/ + tmpptr=_find_next_separator(libdbiargs,'='); + if (! tmpptr) { + rrd_set_error( "formatstring wrong for db arguments as we did not find \"=\" in \"%s\"",libdbiargs); + _sql_close(&table_help); + return 1; + } + /* hex-unescape the value */ + if(_inline_unescape(tmpptr)) { return -1; } + /* now handle the key/value pair */ + if (strcmp(libdbiargs,"rrdminstepsize")==0) { /* allow override for minstepsize */ + i=atoi(tmpptr);if (i>0) { minstepsize=i; } + } else if (strcmp(libdbiargs,"rrdfillmissing")==0) { /* allow override for minstepsize */ + i=atoi(tmpptr);if (i>0) { fillmissing=i; } + } else if (strcmp(libdbiargs,"rrdderivemaxstep")==0) { /* allow override for derived max delta */ + i=atoi(tmpptr);if (i>0) { if (derive) { derive=i; }} + } else { /* store in libdbi, as these are parameters */ + if (_sql_setparam(&table_help,libdbiargs,tmpptr)) { return -1; } + } + /* and continue loop with next pointer */ + libdbiargs=nextptr; + } + + /* and modify step if given */ + if (*step0) { + /* processing of value */ + /* calculate index for the timestamp */ + idx=(r_timestamp-(*start))/(*step); + /* some out of bounds checks on idx */ + if (idx<0) { idx=0;} + if (idx>rows) { idx=rows;} + /* and calculate derivative if necessary */ + if (derive) { + /* calc deltas */ + d_timestamp=r_timestamp-l_timestamp; + d_value=r_value-l_value; + /* assign current as last values */ + l_timestamp=r_timestamp; + l_value=r_value; + /* assign DNAN by default for value */ + r_value=DNAN; + /* check for timestamp delta to be within an acceptable range */ + if ((d_timestamp>0)&&(d_timestamp<2*derive)) { + /* only handle positive delta - avoid wrap-arrounds/counter resets showing up as spikes */ + if (d_value>0) { + /* and normalize to per second */ + r_value=d_value/d_timestamp; + } + } + } + /* only add value if we have a value that is not NAN */ + if (! isnan(r_value)) { + if ((*data)[idx*(*ds_cnt)+3]==0) { /* count is 0 so assign to overwrite DNAN */ + (*data)[idx*(*ds_cnt)+0]=r_value; /* MIN */ + (*data)[idx*(*ds_cnt)+1]=r_value; /* AVG */ + (*data)[idx*(*ds_cnt)+2]=r_value; /* MAX */ + (*data)[idx*(*ds_cnt)+3]=1; /* COUNT */ + (*data)[idx*(*ds_cnt)+4]=r_value; /* SIGMA */ + } else { + /* MIN */ + if ((*data)[idx*(*ds_cnt)+0]>r_value) { (*data)[idx*(*ds_cnt)+0]=r_value; } + /* AVG - at this moment still sum - corrected in post processing */ + (*data)[idx*(*ds_cnt)+1]+=r_value; + /* MAX */ + if ((*data)[idx*(*ds_cnt)+2]0) { + /* calc deviation first */ + if (count>2) { + r_value=count*(*data)[idx*(*ds_cnt)+4]-(*data)[idx*(*ds_cnt)+1]*(*data)[idx*(*ds_cnt)+1]; + if (r_value<0) { + r_value=DNAN; + } else { + r_value=sqrt(r_value/(count*(count-1))); + } + } + (*data)[idx*(*ds_cnt)+4]=r_value; + /* now the average */ + (*data)[idx*(*ds_cnt)+1]/=count; + } + } + + /* and return OK */ + return 0; +}