1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "db.h"
22 #include "log.h"
23 #include "zbxtrends.h"
24 #include "trends.h"
25
26 static char *trends_errors[ZBX_TREND_STATE_COUNT] = {
27 "unknown error",
28 NULL,
29 "not enough data",
30 "value is too large"
31 };
32
33 /******************************************************************************
34 * *
35 * Function: trends_parse_base *
36 * *
37 * Purpose: parse largest period base from function parameters *
38 * *
39 * Parameters: shift - [IN] the period shift parameter *
40 * base - [OUT] the period shift base (now/?) *
41 * error - [OUT] the error message if parsing failed *
42 * *
43 * Return value: SUCCEED - period was parsed successfully *
44 * FAIL - invalid time period was specified *
45 * *
46 ******************************************************************************/
trends_parse_base(const char * period_shift,zbx_time_unit_t * base,char ** error)47 static int trends_parse_base(const char *period_shift, zbx_time_unit_t *base, char **error)
48 {
49 zbx_time_unit_t time_unit = ZBX_TIME_UNIT_UNKNOWN;
50 const char *ptr;
51
52 for (ptr = period_shift; NULL != (ptr = strchr(ptr, '/'));)
53 {
54 zbx_time_unit_t tu;
55
56 if (ZBX_TIME_UNIT_UNKNOWN == (tu = zbx_tm_str_to_unit(++ptr)))
57 {
58 *error = zbx_strdup(*error, "invalid period shift cycle");
59 return FAIL;
60 }
61
62 if (tu > time_unit)
63 time_unit = tu;
64 }
65
66 if (ZBX_TIME_UNIT_UNKNOWN == time_unit)
67 {
68 *error = zbx_strdup(*error, "invalid period shift expression");
69 return FAIL;
70 }
71
72 *base = time_unit;
73
74 return SUCCEED;
75 }
76
77 /******************************************************************************
78 * *
79 * Function: zbx_trends_parse_base *
80 * *
81 * Purpose: parse largest period base from function parameters *
82 * *
83 * Parameters: params - [IN] the function parameters *
84 * base - [OUT] the period shift base (now/?) *
85 * error - [OUT] the error message if parsing failed *
86 * *
87 * Return value: SUCCEED - period was parsed successfully *
88 * FAIL - invalid time period was specified *
89 * *
90 ******************************************************************************/
zbx_trends_parse_base(const char * params,zbx_time_unit_t * base,char ** error)91 int zbx_trends_parse_base(const char *params, zbx_time_unit_t *base, char **error)
92 {
93 const char *period_shift;
94 int ret;
95
96 if (NULL == (period_shift = strchr(params, ':')))
97 {
98 *error = zbx_strdup(*error, "missing period shift parameter");
99 return FAIL;
100 }
101
102 ret = trends_parse_base(period_shift + 1, base, error);
103
104 return ret;
105 }
106
107 /******************************************************************************
108 * *
109 * Function: trends_parse_timeshift *
110 * *
111 * Purpose: parse timeshift *
112 * *
113 * Parameters: from - [IN] the start time *
114 * timeshift - [IN] the timeshift string *
115 * min_time_unit - [IN] minimum time unit that can be used *
116 * tm - [IN] the shifted time *
117 * error - [OUT] the error message if parsing failed *
118 * *
119 * Return value: SUCCEED - time shift was parsed successfully *
120 * FAIL - otherwise *
121 * *
122 ******************************************************************************/
trends_parse_timeshift(time_t from,const char * timeshift,zbx_time_unit_t min_time_unit,struct tm * tm,char ** error)123 static int trends_parse_timeshift(time_t from, const char *timeshift, zbx_time_unit_t min_time_unit, struct tm *tm,
124 char **error)
125 {
126 size_t len;
127 const char *p;
128
129 zabbix_log(LOG_LEVEL_DEBUG, "In %s() shift:%s", __func__, timeshift);
130
131 p = timeshift;
132
133 if (0 != strncmp(p, "now", ZBX_CONST_STRLEN("now")))
134 {
135 *error = zbx_strdup(*error, "time shift must begin with \"now\"");
136 return FAIL;
137 }
138
139 p += ZBX_CONST_STRLEN("now");
140
141 localtime_r(&from, tm);
142
143 while ('\0' != *p)
144 {
145 zbx_time_unit_t unit;
146
147 if ('/' == *p)
148 {
149 if (ZBX_TIME_UNIT_UNKNOWN == (unit = zbx_tm_str_to_unit(++p)))
150 {
151 *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", p);
152 return FAIL;
153 }
154
155 if (unit < min_time_unit)
156 {
157 *error = zbx_dsprintf(*error, "time units in time shift must be greater or equal"
158 " to period time unit");
159 return FAIL;
160 }
161
162 zbx_tm_round_down(tm, unit);
163
164 /* unit is single character */
165 p++;
166 }
167 else if ('+' == *p || '-' == *p)
168 {
169 int num;
170 char op = *(p++);
171
172 if (FAIL == zbx_tm_parse_period(p, &len, &num, &unit, error))
173 return FAIL;
174
175 if (unit < min_time_unit)
176 {
177 *error = zbx_dsprintf(*error, "time units in period shift must be greater or equal"
178 " to period time unit");
179 return FAIL;
180 }
181
182 if ('+' == op)
183 zbx_tm_add(tm, num, unit);
184 else
185 zbx_tm_sub(tm, num, unit);
186
187 p += len;
188 }
189 else
190 {
191 *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", p);
192 return FAIL;
193 }
194 }
195
196 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() %04d.%02d.%02d %02d:%02d:%02d", __func__, tm->tm_year + 1900,
197 tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
198
199 return SUCCEED;
200 }
201
202 /******************************************************************************
203 * *
204 * Function: zbx_parse_timeshift *
205 * *
206 * Purpose: parse timeshift *
207 * *
208 * Parameters: from - [IN] the start time *
209 * timeshift - [IN] the timeshift string *
210 * tm - [IN] the shifted time *
211 * error - [OUT] the error message if parsing failed *
212 * *
213 * Return value: SUCCEED - time shift was parsed successfully *
214 * FAIL - otherwise *
215 * *
216 ******************************************************************************/
zbx_parse_timeshift(time_t from,const char * timeshift,struct tm * tm,char ** error)217 int zbx_parse_timeshift(time_t from, const char *timeshift, struct tm *tm, char **error)
218 {
219 return trends_parse_timeshift(from, timeshift, ZBX_TIME_UNIT_UNKNOWN, tm, error);
220 }
221
222 /******************************************************************************
223 * *
224 * Function: zbx_trends_parse_range *
225 * *
226 * Purpose: parse trend function period arguments into time range *
227 * *
228 * Parameters: from - [IN] the time the period shift is calculated *
229 * from *
230 * param - [IN] the history period parameter: *
231 * <period>:<period_shift> *
232 * start - [OUT] the period start time in seconds since *
233 * Epoch *
234 * end - [OUT] the period end time in seconds since *
235 * Epoch *
236 * error - [OUT] the error message if parsing failed *
237 * *
238 * Return value: SUCCEED - period was parsed successfully *
239 * FAIL - invalid time period was specified *
240 * *
241 * Comments: Daylight saving changes are applied when parsing ranges with *
242 * day+ used as period base (now/?). *
243 * *
244 * Example period_shift values: *
245 * now/d *
246 * now/d-1h *
247 * now/d+1h *
248 * now/d+1h/w *
249 * now/d/w/h+1h+2h *
250 * now-1d/h *
251 * *
252 ******************************************************************************/
zbx_trends_parse_range(time_t from,const char * param,int * start,int * end,char ** error)253 int zbx_trends_parse_range(time_t from, const char *param, int *start, int *end, char **error)
254 {
255 int period_num, period_hours[ZBX_TIME_UNIT_COUNT] = {0, 0, 0, 1, 24, 24 * 7, 24 * 30, 24 * 365};
256 zbx_time_unit_t period_unit;
257 size_t len;
258 struct tm tm_end, tm_start;
259
260 zabbix_log(LOG_LEVEL_DEBUG, "In %s() param:%s", __func__, param);
261
262 /* parse period */
263
264 if (SUCCEED != zbx_tm_parse_period(param, &len, &period_num, &period_unit, error))
265 return FAIL;
266
267 if ('\0' != param[len] && ':' != param[len])
268 {
269 *error = zbx_dsprintf(*error, "unexpected character[s] in period \"%s\"", param + len);
270 return FAIL;
271 }
272
273 if (period_hours[period_unit] * period_num > 24 * 366)
274 {
275 *error = zbx_strdup(*error, "period is too large");
276 return FAIL;
277 }
278
279 /* parse period shift */
280
281 if (SUCCEED != trends_parse_timeshift(from, param + len + 1, period_unit, &tm_end, error))
282 return FAIL;
283
284 tm_start = tm_end;
285
286 /* trends clock refers to the beginning of the hourly interval - subtract */
287 /* one hour to get the trends clock for the last hourly interval */
288 zbx_tm_sub(&tm_end, 1, ZBX_TIME_UNIT_HOUR);
289
290 if (-1 == (*end = mktime(&tm_end)))
291 {
292 *error = zbx_dsprintf(*error, "cannot calculate the period end time: %s", zbx_strerror(errno));
293 return FAIL;
294 }
295
296 if (abs((int)from - *end) > SEC_PER_YEAR * 26)
297 {
298 *error = zbx_strdup(*error, "period shift is too large");
299 return FAIL;
300 }
301
302 zbx_tm_sub(&tm_start, period_num, period_unit);
303 if (-1 == (*start = mktime(&tm_start)))
304 {
305 *error = zbx_dsprintf(*error, "cannot calculate the period start time: %s", zbx_strerror(errno));
306 return FAIL;
307 }
308
309 zabbix_log(LOG_LEVEL_DEBUG, "End of %s() start:%d end:%d", __func__, *start, *end);
310
311 return SUCCEED;
312 }
313
314 /******************************************************************************
315 * *
316 * Function: zbx_trends_parse_nextcheck *
317 * *
318 * Purpose: calculate possible nextcheck based on trend function parameters *
319 * *
320 * Parameters: from - [IN] the time the period shift is calculated *
321 * from *
322 * p - [IN] the history period shift *
323 * nextcheck - [OUT] the time starting from which the period *
324 * will end in future *
325 * error - [OUT] the error message if parsing failed *
326 * *
327 * Return value: SUCCEED - period was parsed successfully *
328 * FAIL - invalid time period was specified *
329 * *
330 * Comments: Daylight saving changes are applied when parsing ranges with *
331 * day+ used as period base (now/?). *
332 * *
333 ******************************************************************************/
zbx_trends_parse_nextcheck(time_t from,const char * period_shift,time_t * nextcheck,char ** error)334 int zbx_trends_parse_nextcheck(time_t from, const char *period_shift, time_t *nextcheck, char **error)
335 {
336 struct tm tm;
337 zbx_time_unit_t base;
338
339 if (SUCCEED != trends_parse_base(period_shift, &base, error) || ZBX_TIME_UNIT_HOUR > base)
340 return FAIL;
341
342 /* parse period shift */
343
344 if (0 != strncmp(period_shift, "now", ZBX_CONST_STRLEN("now")))
345 {
346 *error = zbx_strdup(*error, "period shift must begin with \"now\"");
347 return FAIL;
348 }
349
350 period_shift += ZBX_CONST_STRLEN("now");
351
352 localtime_r(&from, &tm);
353
354 while ('\0' != *period_shift)
355 {
356 zbx_time_unit_t unit;
357
358 if ('/' == *period_shift)
359 {
360 if (ZBX_TIME_UNIT_UNKNOWN == (unit = zbx_tm_str_to_unit(++period_shift)))
361 {
362 *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", period_shift);
363 return FAIL;
364 }
365
366 zbx_tm_round_down(&tm, unit);
367
368 /* unit is single character */
369 period_shift++;
370 }
371 else if ('+' == *period_shift || '-' == *period_shift)
372 {
373 int num;
374 char op = *(period_shift++);
375 size_t len;
376
377 if (FAIL == zbx_tm_parse_period(period_shift, &len, &num, &unit, error))
378 return FAIL;
379
380 /* nextcheck calculation is based on the largest rounding unit, */
381 /* so shifting larger or equal time units will not affect it */
382 if (unit < base)
383 {
384 if ('+' == op)
385 zbx_tm_add(&tm, num, unit);
386 else
387 zbx_tm_sub(&tm, num, unit);
388 }
389
390 period_shift += len;
391 }
392 else
393 {
394 *error = zbx_dsprintf(*error, "unexpected character starting with \"%s\"", period_shift);
395 return FAIL;
396 }
397 }
398
399 /* trends clock refers to the beginning of the hourly interval - subtract */
400 /* one hour to get the trends clock for the last hourly interval */
401 zbx_tm_sub(&tm, 1, ZBX_TIME_UNIT_HOUR);
402
403 if (-1 == (*nextcheck = mktime(&tm)))
404 {
405 *error = zbx_dsprintf(*error, "cannot calculate the period start time: %s", zbx_strerror(errno));
406 return FAIL;
407 }
408
409 return SUCCEED;
410 }
411
412 /******************************************************************************
413 * *
414 * Function: trends_eval *
415 * *
416 * Purpose: evaluate expression with trends data *
417 * *
418 * Parameters: table - [IN] the trends table name *
419 * itemid - [IN] the itemid *
420 * start - [OUT] the period start time in seconds since *
421 * Epoch *
422 * end - [OUT] the period end time in seconds since *
423 * Epoch *
424 * eval_single - [IN] sql expression to evaluate for single *
425 * record *
426 * eval_multi - [IN] sql expression to evaluate for multiple *
427 * records *
428 * value - [OUT] the evaluation result *
429 * *
430 * Return value: Trend value state of the specified period and function. *
431 * *
432 ******************************************************************************/
trends_eval(const char * table,zbx_uint64_t itemid,int start,int end,const char * eval_single,const char * eval_multi,double * value)433 static zbx_trend_state_t trends_eval(const char *table, zbx_uint64_t itemid, int start, int end,
434 const char *eval_single, const char *eval_multi, double *value)
435 {
436 DB_RESULT result;
437 DB_ROW row;
438 char *sql = NULL;
439 size_t sql_alloc = 0, sql_offset = 0;
440 zbx_trend_state_t state;
441
442 if (start != end)
443 {
444 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
445 "select %s from %s"
446 " where itemid=" ZBX_FS_UI64
447 " and clock>=%d"
448 " and clock<=%d",
449 eval_multi, table, itemid, start, end);
450 }
451 else
452 {
453 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset,
454 "select %s from %s"
455 " where itemid=" ZBX_FS_UI64
456 " and clock=%d",
457 eval_single, table, itemid, start);
458 }
459
460 result = DBselect("%s", sql);
461 zbx_free(sql);
462
463 if (NULL != (row = DBfetch(result)) && SUCCEED != DBis_null(row[0]))
464 {
465 *value = atof(row[0]);
466 state = ZBX_TREND_STATE_NORMAL;
467 }
468 else
469 state = ZBX_TREND_STATE_NODATA;
470
471 DBfree_result(result);
472
473 return state;
474 }
475
476 /******************************************************************************
477 * *
478 * Function: trends_eval_avg *
479 * *
480 * Purpose: evaluate avg function with trends data *
481 * *
482 * Parameters: table - [IN] the trends table name *
483 * itemid - [IN] the itemid *
484 * start - [OUT] the period start time in seconds since *
485 * Epoch *
486 * end - [OUT] the period end time in seconds since *
487 * Epoch *
488 * value - [OUT] the evaluation result *
489 * *
490 * Return value: Trend value state of the specified period and function. *
491 * *
492 ******************************************************************************/
trends_eval_avg(const char * table,zbx_uint64_t itemid,int start,int end,double * value)493 static zbx_trend_state_t trends_eval_avg(const char *table, zbx_uint64_t itemid, int start, int end,
494 double *value)
495 {
496 DB_RESULT result;
497 DB_ROW row;
498 char *sql = NULL;
499 size_t sql_alloc = 0, sql_offset = 0;
500 zbx_trend_state_t state;
501 double avg, num, num2, avg2;
502
503 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select value_avg,num from %s where itemid=" ZBX_FS_UI64,
504 table, itemid);
505 if (start != end)
506 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " and clock>=%d and clock<=%d", start, end);
507 else
508 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " and clock=%d", start);
509
510 result = DBselect("%s", sql);
511 zbx_free(sql);
512
513 if (NULL != (row = DBfetch(result)))
514 {
515 avg = atof(row[0]);
516 num = atof(row[1]);
517
518 while (NULL != (row = DBfetch(result)))
519 {
520 avg2 = atof(row[0]);
521 num2 = atof(row[1]);
522 avg = avg / (num + num2) * num + avg2 / (num + num2) * num2;
523 num += num2;
524 }
525
526 *value = avg;
527 state = ZBX_TREND_STATE_NORMAL;
528 }
529 else
530 state = ZBX_TREND_STATE_NODATA;
531
532 DBfree_result(result);
533
534 return state;
535 }
536
537 /******************************************************************************
538 * *
539 * Function: trends_eval_sum *
540 * *
541 * Purpose: evaluate sum function with trends data *
542 * *
543 * Parameters: table - [IN] the trends table name *
544 * itemid - [IN] the itemid *
545 * start - [OUT] the period start time in seconds since *
546 * Epoch *
547 * end - [OUT] the period end time in seconds since *
548 * Epoch *
549 * value - [OUT] the evaluation result *
550 * *
551 * Return value: Trend value state of the specified period and function. *
552 * *
553 ******************************************************************************/
trends_eval_sum(const char * table,zbx_uint64_t itemid,int start,int end,double * value)554 static zbx_trend_state_t trends_eval_sum(const char *table, zbx_uint64_t itemid, int start, int end,
555 double *value)
556 {
557 DB_RESULT result;
558 DB_ROW row;
559 char *sql = NULL;
560 size_t sql_alloc = 0, sql_offset = 0;
561 double sum = 0;
562
563 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, "select value_avg,num from %s where itemid=" ZBX_FS_UI64,
564 table, itemid);
565 if (start != end)
566 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " and clock>=%d and clock<=%d", start, end);
567 else
568 zbx_snprintf_alloc(&sql, &sql_alloc, &sql_offset, " and clock=%d", start);
569
570 result = DBselect("%s", sql);
571 zbx_free(sql);
572
573 while (NULL != (row = DBfetch(result)))
574 sum += atof(row[0]) * atof(row[1]);
575
576 DBfree_result(result);
577
578 if (ZBX_INFINITY == sum)
579 return ZBX_TREND_STATE_OVERFLOW;
580
581 *value = sum;
582
583 return ZBX_TREND_STATE_NORMAL;
584 }
585
zbx_trends_eval_avg(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)586 int zbx_trends_eval_avg(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
587 {
588 zbx_trend_state_t state;
589
590 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_AVG, value, &state))
591 {
592 state = trends_eval_avg(table, itemid, start, end, value);
593 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_AVG, *value, state);
594 }
595
596 if (ZBX_TREND_STATE_NORMAL == state)
597 return SUCCEED;
598
599 if (NULL != error)
600 *error = zbx_strdup(*error, trends_errors[state]);
601
602 return FAIL;
603 }
604
zbx_trends_eval_count(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)605 int zbx_trends_eval_count(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
606 {
607 zbx_trend_state_t state;
608
609 ZBX_UNUSED(error);
610
611 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_COUNT, value, &state))
612 {
613 if (ZBX_TREND_STATE_NORMAL != (state = trends_eval(table, itemid, start, end, "num", "sum(num)", value)))
614 {
615 state = ZBX_TREND_STATE_NORMAL;
616 *value = 0;
617 }
618
619 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_COUNT, *value, state);
620 }
621
622 return SUCCEED;
623 }
624
zbx_trends_eval_delta(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)625 int zbx_trends_eval_delta(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
626 {
627 zbx_trend_state_t state;
628
629 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_DELTA, value, &state))
630 {
631 state = trends_eval(table, itemid, start, end, "value_max-value_min", "max(value_max)-min(value_min)",
632 value);
633 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_DELTA, *value, state);
634 }
635
636 if (ZBX_TREND_STATE_NORMAL == state)
637 return SUCCEED;
638
639 if (NULL != error)
640 *error = zbx_strdup(*error, trends_errors[state]);
641
642 return FAIL;
643 }
644
zbx_trends_eval_max(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)645 int zbx_trends_eval_max(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
646 {
647 zbx_trend_state_t state;
648
649 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_MAX, value, &state))
650 {
651 state = trends_eval(table, itemid, start, end, "value_max", "max(value_max)", value);
652 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_MAX, *value, state);
653 }
654
655 if (ZBX_TREND_STATE_NORMAL == state)
656 return SUCCEED;
657
658 if (NULL != error)
659 *error = zbx_strdup(*error, trends_errors[state]);
660
661 return FAIL;
662 }
663
zbx_trends_eval_min(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)664 int zbx_trends_eval_min(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
665 {
666 zbx_trend_state_t state;
667
668 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_MIN, value, &state))
669 {
670 state = trends_eval(table, itemid, start, end, "value_min", "min(value_min)", value);
671 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_MIN, *value, state);
672 }
673
674 if (ZBX_TREND_STATE_NORMAL == state)
675 return SUCCEED;
676
677 if (NULL != error)
678 *error = zbx_strdup(*error, trends_errors[state]);
679
680 return FAIL;
681 }
682
zbx_trends_eval_sum(const char * table,zbx_uint64_t itemid,int start,int end,double * value,char ** error)683 int zbx_trends_eval_sum(const char *table, zbx_uint64_t itemid, int start, int end, double *value, char **error)
684 {
685 zbx_trend_state_t state;
686
687 if (FAIL == zbx_tfc_get_value(itemid, start, end, ZBX_TREND_FUNCTION_SUM, value, &state))
688 {
689 state = trends_eval_sum(table, itemid, start, end, value);
690 zbx_tfc_put_value(itemid, start, end, ZBX_TREND_FUNCTION_SUM, *value, state);
691 }
692
693 if (ZBX_TREND_STATE_NORMAL == state)
694 return SUCCEED;
695
696 if (NULL != error)
697 *error = zbx_strdup(*error, trends_errors[state]);
698
699 return FAIL;
700 }
701