]> git.pld-linux.org Git - packages/rrdtool.git/blob - rrdtool-dbi.patch
- backport from 1.4 (trunk) libdbi support
[packages/rrdtool.git] / rrdtool-dbi.patch
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
3 @@ -474,6 +474,24 @@
4  
5  CONFIGURE_PART(Find 3rd-Party Libraries)
6  
7 +AC_ARG_ENABLE(libdbi,AS_HELP_STRING([--disable-libdbi],[do not build in support for libdbi]),[have_libdbi=no],[
8 +  XXX=$LIBS
9 +  LIBS="$LIBS -ldbi -ldl"
10 +  AC_MSG_CHECKING(for libdbi)
11 +  AC_LINK_IFELSE(
12 +    [AC_LANG_PROGRAM([[#include <dbi/dbi.h>]], 
13 +                     [[dbi_initialize(NULL)]]
14 +                    )
15 +    ],[AC_DEFINE(HAVE_LIBDBI,[1],[have got libdbi installed])
16 +       AC_MSG_RESULT([yes])
17 +       have_libdbi=yes
18 +    ],[LIBS=$XXX
19 +       AC_MSG_RESULT([no])
20 +       have_libdbi=no
21 +    ]
22 +  )
23 +])
24 +AM_CONDITIONAL(BUILD_LIBDBI,[test $have_libdbi != no])
25  
26  AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no])
27  
28 @@ -759,6 +777,7 @@
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"
33  echo
34  echo "             Libraries: $ALL_LIBS"
35  echo
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
38 @@ -54,6 +59,10 @@
39         fnv.h rrd_graph.h \
40         rrd_is_thread_safe.h
41  
42 +if BUILD_LIBDBI
43 +RRD_C_FILES += rrd_fetch_libdbi.c
44 +endif
45 +
46  if BUILD_GETOPT
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
51 @@ -218,6 +218,15 @@
52              *start, *end, *step);
53  #endif
54  
55 +#ifdef HAVE_LIBDBI
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);
60 +      }
61 +    }
62 +#endif
63 +
64      rrd_file = rrd_open(filename, &rrd, RRD_READONLY);
65      if (rrd_file == NULL)
66          goto err_free;
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
69 @@ -93,6 +92,16 @@
70      char ***ds_namv,
71      rrd_value_t **data);
72  
73 +
74 +#ifdef HAVE_LIBDBI
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,
79 +                       char        ***ds_namv,
80 +                       rrd_value_t **data);
81 +#endif
82 +
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
88 @@ -0,0 +1,649 @@
89 +#include "rrd_tool.h"
90 +#include <dbi/dbi.h>
91 +#include <time.h>
92 +
93 +/* the structures */
94 +struct sql_table_helper {
95 +  dbi_conn conn;
96 +  int connected;
97 +  dbi_result result;
98 +  char const* filename;
99 +  char const* dbdriver;
100 +  char* table_start;
101 +  char* table_next;
102 +  char const* where;
103 +  char * timestamp;
104 +  char * value;
105 +};
106 +
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);
117 +
118 +/* the real code */
119 +
120 +/* helpers to get correctly converted values from DB*/
121 +long rrd_fetch_dbi_long(dbi_result *result,int idx) {
122 +  char *ptmp="";
123 +  long value=DNAN;
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 */
130 +  switch (type) {
131 +    case DBI_TYPE_STRING:
132 +      ptmp=(char*)dbi_result_get_string_idx(result,idx);
133 +      value=atoi(ptmp);
134 +      break;
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 ); }
143 +      }
144 +      break;
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 ); }
150 +      }
151 +      break;
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);
155 +      ptmp[attr-1]=0;
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");
160 +       }
161 +      }
162 +      /* convert to number */
163 +      value=atoi(ptmp);
164 +      /* free pointer */
165 +      free(ptmp);
166 +      break;
167 +    case DBI_TYPE_DATETIME:
168 +       value=dbi_result_get_datetime_idx(result,idx);
169 +       break;
170 +    default:
171 +      if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
172 +      value=DNAN;
173 +      break;
174 +  }
175 +  return value;
176 +}
177 +
178 +double rrd_fetch_dbi_double(dbi_result *result,int idx) {
179 +  char *ptmp="";
180 +  double value=DNAN;
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 */
187 +  switch (type) {
188 +    case DBI_TYPE_STRING:
189 +      ptmp=(char*)dbi_result_get_string_idx(result,idx);
190 +      value=strtod(ptmp,NULL);
191 +      break;
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 ); }
200 +      }
201 +      break;
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 ); }
207 +      }
208 +      break;
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);
212 +      ptmp[attr-1]=0;
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");
217 +       }
218 +      }
219 +      /* convert to number */
220 +      value=strtod(ptmp,NULL);
221 +      /* free pointer */
222 +      free(ptmp);
223 +      break;
224 +    case DBI_TYPE_DATETIME:
225 +       value=dbi_result_get_datetime_idx(result,idx);
226 +       break;
227 +    default:
228 +      if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: column %i unsupported type: %i with attribute %i\n",time(NULL),idx,type,attr ); }
229 +      value=DNAN;
230 +      break;
231 +  }
232 +  return value;
233 +}
234 +
235 +void _sql_close(struct sql_table_helper* th) {
236 +  /* close only if connected */
237 +  if (th->conn) {
238 +    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: close connection\n",time(NULL) ); }
239 +    /* shutdown dbi */
240 +    dbi_conn_close(th->conn);
241 +    if (getenv("RRDDEBUGSQL")) { fprintf(stderr,"RRDDEBUGSQL: %li: shutting down libdbi\n",time(NULL) ); }
242 +    dbi_shutdown();
243 +    /* and assign empty */
244 +    th->conn=NULL;
245 +    th->connected=0;
246 +  }
247 +}
248 +
249 +int _sql_setparam(struct sql_table_helper* th,char* key, char* value) {
250 +  char* dbi_errstr=NULL;
251 +  dbi_driver driver;
252 +  /* if not connected */
253 +  if (! th->conn) {
254 +    /* initialize some stuff */
255 +    th->table_next=th->table_start;
256 +    th->result=NULL;
257 +    th->connected=0;
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);
263 +    if (! driver) {
264 +      rrd_set_error( "libdbi - no such driver: %s (possibly a dynamic link problem of the driver being linked without -ldbi)",th->dbdriver); 
265 +      return -1; 
266 +    }
267 +    /* and connect to driver */
268 +    th->conn=dbi_conn_open(driver);
269 +    /* and handle errors */
270 +    if (! th->conn) { 
271 +      rrd_set_error( "libdbi - could not open connection to driver %s",th->dbdriver); 
272 +      dbi_shutdown();
273 +      return -1;
274 +    }
275 +  }
276 +  if (th->connected) {
277 +    rrd_set_error( "we are already connected - can not set parameter %s=%s",key,value);
278 +    _sql_close(th);
279 +    return -1; 
280 +  }
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);
285 +    _sql_close(th);
286 +    return -1;
287 +  }
288 +  return 0;
289 +}
290 +
291 +int _sql_fetchrow(struct sql_table_helper* th,time_t *timestamp, rrd_value_t *value,int ordered) {
292 +  char* dbi_errstr=NULL;
293 +  char sql[10240];
294 +  time_t startt=0,endt=0;
295 +  /*connect to the database if needed */
296 +  if (! th->conn) {
297 +      rrd_set_error( "libdbi no parameters set for libdbi",th->filename,dbi_errstr);
298 +      return -1;
299 +  }
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);
306 +      _sql_close(th);
307 +      return -1;
308 +    }
309 +    th->connected=1;
310 +  }
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 */
317 +      _sql_close(th);
318 +      /* and return with end of data */
319 +      return 0;
320 +    }
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 */
326 +    if (ordered) {
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);
329 +    } else {
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);
332 +    }
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);
342 +      _sql_close(th);
343 +      return -1;
344 +    }
345 +  }
346 +  /* and now fetch key and value */
347 +  if (! dbi_result_next_row(th->result)) {
348 +    /* free result */
349 +    dbi_result_free(th->result);
350 +    th->result=NULL;
351 +    /* and call recursively - this will open the next table or close connection as a whole*/
352 +    return _sql_fetchrow(th,timestamp,value,ordered);
353 +  } 
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);
357 +  return 1;
358 +}
359 +
360 +char* _find_next_separator(char* start,char separator) {
361 +  char* found=strchr(start,separator);
362 +  /* have we found it */
363 +  if (found) {
364 +    /* then 0 terminate current string */
365 +    *found=0; 
366 +    /* and return the pointer past the separator */
367 +    return (found+1);
368 +  }
369 +  /* not found, so return NULL */
370 +  return NULL;
371 +}
372 +
373 +char* _find_next_separator_twice(char*start,char separator) {
374 +  char *found=start;
375 +  /* find next separator in string*/
376 +  while (found) {
377 +    /* if found and the next one is also a separator */
378 +    if (found[1] == separator) {
379 +      /* then 0 terminate current string */
380 +      *found=0;
381 +      /* and return the pointer past the current one*/
382 +      return (found+2);
383 +    }
384 +    /* find next occurance */
385 +    found=strchr(found+1,separator);
386 +  }
387 +  /* not found, so return NULL */
388 +  return NULL;
389 +}
390 +
391 +char _hexcharhelper(char c) {
392 +  switch (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;
415 +  }
416 +  return -1;
417 +}
418 +
419 +int _inline_unescape (char* string) {
420 +  char *src=string;
421 +  char *dst=string;
422 +  char c,h1,h2;
423 +  while((c= *src)) {
424 +    src++;
425 +    if (c == '%') {
426 +      if (*src == '%') { 
427 +       /* increase src pointer by 1 skiping second % */
428 +       src+=1;
429 +      } else {
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); }
435 +       c=h2+(h1<<4);
436 +       /* increase src pointer by 2 skiping 2 chars */
437 +       src+=2;
438 +      } 
439 +    }
440 +    *dst=c;
441 +    dst++;
442 +  }
443 +  *dst=0;
444 +  return 0;
445 +}
446 +
447 +int
448 +rrd_fetch_fn_libdbi(
449 +    char           *filename,  /* name of the rrd */
450 +    enum cf_en     cf_idx,     /* which consolidation function ?*/
451 +    time_t         *start,
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 */
459 +{
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 */
469 +  int fillmissing=0;
470 +  unsigned long minstepsize=300;
471 +  /* by default assume unixtimestamp */
472 +  int isunixtime=1;
473 +  /* the result-set */
474 +  long r_timestamp,l_timestamp,d_timestamp;
475 +  double r_value,l_value,d_value;
476 +  int r_status;
477 +  int rows;
478 +  long idx;
479 +  int derive=0;
480 +  /* the libdbi connection data and the table_help structure */
481 +  struct sql_table_helper table_help;
482 +  char where[10240];
483 +  table_help.conn=NULL;
484 +  table_help.where=where;
485 +
486 +  /* some loop variables */
487 +  int i=0;
488 +
489 +  /* check header */
490 +  if (strncmp("sql",filename,3)!=0) { 
491 +    rrd_set_error( "formatstring wrong - %s",filename );return -1; 
492 +  }
493 +  if (filename[3]!=filename[4]) { 
494 +    rrd_set_error( "formatstring wrong - %s",filename );return -1; 
495 +  }
496 +
497 +  /* now make this the separator */
498 +  separator=filename[3];
499 +
500 +  /* copy filename for local modifications during parsing */
501 +  strncpy(filenameworkcopy,filename+5,sizeof(filenameworkcopy));
502 +
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);
509 +    return -1; 
510 +  }
511 +
512 +  /* now find the next double separator - this defines the args to the database */
513 +  sqlargs=_find_next_separator_twice(libdbiargs,separator);
514 +  if (!sqlargs) {
515 +    rrd_set_error( "formatstring wrong for db arguments as we did not find \"%c%c\" in \"%s\"",separator,separator,libdbiargs);
516 +    return 1;
517 +  }
518 +
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 */
521 +
522 +  /* parse the table(s) */
523 +  table_help.table_start=sqlargs;
524 +  nextptr=_find_next_separator(table_help.table_start,separator);
525 +  if (! nextptr) { 
526 +    /* error in argument */
527 +    rrd_set_error( "formatstring wrong - %s",tmpptr);
528 +    return -1; 
529 +  }
530 +  /* hex-unescape the value */
531 +  if(_inline_unescape(table_help.table_start)) { return -1; }
532 +
533 +  /* parse the unix timestamp column */
534 +  table_help.timestamp=nextptr;
535 +  nextptr=_find_next_separator(nextptr,separator);
536 +  if (! nextptr) { 
537 +    /* error in argument */
538 +    rrd_set_error( "formatstring wrong - %s",tmpptr);
539 +    return -1; 
540 +  }
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; }
545 +
546 +  /* parse the value column */
547 +  table_help.value=nextptr;
548 +  nextptr=_find_next_separator(nextptr,separator);
549 +  if (! nextptr) { 
550 +    /* error in argument */
551 +    rrd_set_error( "formatstring wrong - %s",tmpptr);
552 +    return -1; 
553 +  }
554 +  /* hex-unescape the value */
555 +  if(_inline_unescape(table_help.value)) { return -1; }
556 +  
557 +  /* now prepare WHERE clause as empty string*/
558 +  where[0]=0;
559 +
560 +  /* and the where clause */
561 +  sqlargs=nextptr;
562 +  while(sqlargs) {
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 */
567 +      derive=600;
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");
570 +      return -1;
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");
573 +      return -1;
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);
578 +    }
579 +    /* and continue loop with next pointer */
580 +    sqlargs=nextptr;
581 +  }
582 +  /* and unescape */
583 +  if(_inline_unescape(where)) { return -1; }
584 +
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,'=');
591 +    if (! tmpptr) { 
592 +      rrd_set_error( "formatstring wrong for db arguments as we did not find \"=\" in \"%s\"",libdbiargs);
593 +      _sql_close(&table_help);
594 +      return 1;
595 +    }
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; }
607 +    }
608 +    /* and continue loop with next pointer */
609 +    libdbiargs=nextptr;
610 +  }
611 +  
612 +  /* and modify step if given */
613 +  if (*step<minstepsize) {*step=minstepsize;}
614 +  *start-=(*start)%(*step);
615 +  *end-=(*end)%(*step);
616 +
617 +  /* and append the SQL WHERE Clause for the timeframe calculated above (adding AND if required) */
618 +  if (where[0]) {strcat(where," AND ");}
619 +  i=strlen(where);
620 +  if (isunixtime) {
621 +    snprintf(where+i,sizeof(where)-1-i,"%li < %s AND %s < %li",*start,table_help.timestamp,table_help.timestamp,*end);
622 +  } else {
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);
626 +  }
627 +
628 +  /* and now calculate the number of rows in the resultset... */
629 +  rows=((*end)-(*start))/(*step)+2;
630 +  
631 +  /* define the result set variables/columns returned */
632 +  *ds_cnt=5;
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 */
638 +    switch (i) {
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;
644 +    }
645 +  }
646 +
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);
652 +    return(-1);
653 +  }
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 */
661 +  }
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 */
673 +    if (derive) {
674 +      /* calc deltas */
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;
679 +      l_value=r_value;
680 +      /* assign DNAN by default for value */
681 +      r_value=DNAN;
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 */
685 +       if (d_value>0) {
686 +         /* and normalize to per second */
687 +         r_value=d_value/d_timestamp;
688 +       }
689 +      }
690 +    }
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 */
699 +      } else {
700 +       /* MIN */
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;
704 +        /* MAX */
705 +       if ((*data)[idx*(*ds_cnt)+2]<r_value) { (*data)[idx*(*ds_cnt)+2]=r_value; }
706 +        /* COUNT */
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;
710 +      }
711 +    }
712 +  }
713 +  /* and check for negativ status, pass back immediately */
714 +  if (r_status==-1) { return -1; }
715 +
716 +  /* post processing */
717 +  for(idx=0;idx<rows;idx++) {
718 +    long count=(*data)[idx*(*ds_cnt)+3];
719 +    if (count>0) {
720 +      /* calc deviation first */
721 +      if (count>2) {
722 +       r_value=count*(*data)[idx*(*ds_cnt)+4]-(*data)[idx*(*ds_cnt)+1]*(*data)[idx*(*ds_cnt)+1];
723 +       if (r_value<0) { 
724 +         r_value=DNAN; 
725 +       } else {
726 +         r_value=sqrt(r_value/(count*(count-1)));
727 +       }
728 +      }
729 +      (*data)[idx*(*ds_cnt)+4]=r_value;
730 +      /* now the average */
731 +      (*data)[idx*(*ds_cnt)+1]/=count;
732 +    }
733 +  }
734 +
735 +  /* and return OK */
736 +  return 0;
737 +}
This page took 0.130926 seconds and 4 git commands to generate.