master
c 244 lines 8.13 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "query-internal.h"
4
5 static inline bool group_by_is_separator(char c) {
6 return c == ',' || c == '|' || c == ' ';
7 }
8
9 static inline bool group_by_token_matches(const char *token, size_t len, const char *name, size_t name_len) {
10 if(!name)
11 return false;
12
13 return name_len == len && !strncmp(token, name, len);
14 }
15
16 RRDR_GROUP_BY group_by_parse(const char *group_by_txt) {
17 RRDR_GROUP_BY group_by = RRDR_GROUP_BY_NONE;
18
19 if(!group_by_txt || !*group_by_txt)
20 return group_by;
21
22 const char *s = group_by_txt;
23 while(*s) {
24 while(*s && group_by_is_separator(*s))
25 s++;
26
27 const char *key = s;
28 while(*s && !group_by_is_separator(*s))
29 s++;
30
31 size_t len = (size_t)(s - key);
32 if(!len) continue;
33
34 if (group_by_token_matches(key, len, "selected", sizeof("selected") - 1))
35 group_by |= RRDR_GROUP_BY_SELECTED;
36
37 if (group_by_token_matches(key, len, "dimension", sizeof("dimension") - 1))
38 group_by |= RRDR_GROUP_BY_DIMENSION;
39
40 if (group_by_token_matches(key, len, "instance", sizeof("instance") - 1))
41 group_by |= RRDR_GROUP_BY_INSTANCE;
42
43 if (group_by_token_matches(key, len, "percentage-of-instance", sizeof("percentage-of-instance") - 1))
44 group_by |= RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE;
45
46 if (group_by_token_matches(key, len, "label", sizeof("label") - 1))
47 group_by |= RRDR_GROUP_BY_LABEL;
48
49 if (group_by_token_matches(key, len, "node", sizeof("node") - 1))
50 group_by |= RRDR_GROUP_BY_NODE;
51
52 if (group_by_token_matches(key, len, "context", sizeof("context") - 1))
53 group_by |= RRDR_GROUP_BY_CONTEXT;
54
55 if (group_by_token_matches(key, len, "units", sizeof("units") - 1))
56 group_by |= RRDR_GROUP_BY_UNITS;
57 }
58
59 if((group_by & RRDR_GROUP_BY_SELECTED) && (group_by & ~RRDR_GROUP_BY_SELECTED)) {
60 internal_error(true, "group-by given by query has 'selected' together with more groupings");
61 group_by = RRDR_GROUP_BY_SELECTED; // remove all other groupings
62 }
63
64 if(group_by & RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE)
65 group_by = RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE; // remove all other groupings
66
67 return group_by;
68 }
69
70 void buffer_json_group_by_to_array(BUFFER *wb, RRDR_GROUP_BY group_by) {
71 if(group_by == RRDR_GROUP_BY_NONE)
72 buffer_json_add_array_item_string(wb, "none");
73 else {
74 if (group_by & RRDR_GROUP_BY_DIMENSION)
75 buffer_json_add_array_item_string(wb, "dimension");
76
77 if (group_by & RRDR_GROUP_BY_INSTANCE)
78 buffer_json_add_array_item_string(wb, "instance");
79
80 if (group_by & RRDR_GROUP_BY_PERCENTAGE_OF_INSTANCE)
81 buffer_json_add_array_item_string(wb, "percentage-of-instance");
82
83 if (group_by & RRDR_GROUP_BY_LABEL)
84 buffer_json_add_array_item_string(wb, "label");
85
86 if (group_by & RRDR_GROUP_BY_NODE)
87 buffer_json_add_array_item_string(wb, "node");
88
89 if (group_by & RRDR_GROUP_BY_CONTEXT)
90 buffer_json_add_array_item_string(wb, "context");
91
92 if (group_by & RRDR_GROUP_BY_UNITS)
93 buffer_json_add_array_item_string(wb, "units");
94
95 if (group_by & RRDR_GROUP_BY_SELECTED)
96 buffer_json_add_array_item_string(wb, "selected");
97 }
98 }
99
100 RRDR_GROUP_BY_FUNCTION group_by_aggregate_function_parse(const char *s) {
101 if(strcmp(s, "average") == 0)
102 return RRDR_GROUP_BY_FUNCTION_AVERAGE;
103
104 if(strcmp(s, "avg") == 0)
105 return RRDR_GROUP_BY_FUNCTION_AVERAGE;
106
107 if(strcmp(s, "min") == 0)
108 return RRDR_GROUP_BY_FUNCTION_MIN;
109
110 if(strcmp(s, "max") == 0)
111 return RRDR_GROUP_BY_FUNCTION_MAX;
112
113 if(strcmp(s, "sum") == 0)
114 return RRDR_GROUP_BY_FUNCTION_SUM;
115
116 if(strcmp(s, "percentage") == 0)
117 return RRDR_GROUP_BY_FUNCTION_PERCENTAGE;
118
119 if(strcmp(s, "extremes") == 0)
120 return RRDR_GROUP_BY_FUNCTION_EXTREMES;
121
122 return RRDR_GROUP_BY_FUNCTION_AVERAGE;
123 }
124
125 const char *group_by_aggregate_function_to_string(RRDR_GROUP_BY_FUNCTION group_by_function) {
126 switch(group_by_function) {
127 default:
128 case RRDR_GROUP_BY_FUNCTION_AVERAGE:
129 return "average";
130
131 case RRDR_GROUP_BY_FUNCTION_MIN:
132 return "min";
133
134 case RRDR_GROUP_BY_FUNCTION_MAX:
135 return "max";
136
137 case RRDR_GROUP_BY_FUNCTION_SUM:
138 return "sum";
139
140 case RRDR_GROUP_BY_FUNCTION_PERCENTAGE:
141 return "percentage";
142
143 case RRDR_GROUP_BY_FUNCTION_EXTREMES:
144 return "extremes";
145 }
146 }
147
148 // ----------------------------------------------------------------------------
149 // group by
150
151 void group_by_label_key_insert_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data) {
152 // add the key to our r->label_keys global keys dictionary
153 DICTIONARY *label_keys = data;
154 dictionary_set(label_keys, dictionary_acquired_item_name(item), NULL, 0);
155
156 // create a dictionary for the values of this key
157 struct group_by_label_key *k = value;
158 k->values = dictionary_create_advanced(DICT_OPTION_SINGLE_THREADED | DICT_OPTION_DONT_OVERWRITE_VALUE, NULL, 0);
159 }
160
161 void group_by_label_key_delete_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) {
162 struct group_by_label_key *k = value;
163 dictionary_destroy(k->values);
164 }
165
166 int rrdlabels_traversal_cb_to_group_by_label_key(const char *name, const char *value, RRDLABEL_SRC ls __maybe_unused, void *data) {
167 DICTIONARY *dl = data;
168 struct group_by_label_key *k = dictionary_set(dl, name, NULL, sizeof(struct group_by_label_key));
169 dictionary_set(k->values, value, NULL, 0);
170 return 1;
171 }
172
173 void rrdr_json_group_by_labels(BUFFER *wb, const char *key, RRDR *r, RRDR_OPTIONS options) {
174 if(!r->label_keys || !r->dl)
175 return;
176
177 buffer_json_member_add_object(wb, key);
178
179 void *t;
180 dfe_start_read(r->label_keys, t) {
181 buffer_json_member_add_array(wb, t_dfe.name);
182
183 for(size_t d = 0; d < r->d ;d++) {
184 if(!rrdr_dimension_should_be_exposed(r->od[d], options))
185 continue;
186
187 struct group_by_label_key *k = dictionary_get(r->dl[d], t_dfe.name);
188 if(k) {
189 buffer_json_add_array_item_array(wb);
190 void *tt;
191 dfe_start_read(k->values, tt) {
192 buffer_json_add_array_item_string(wb, tt_dfe.name);
193 }
194 dfe_done(tt);
195 buffer_json_array_close(wb);
196 }
197 else
198 buffer_json_add_array_item_string(wb, NULL);
199 }
200
201 buffer_json_array_close(wb);
202 }
203 dfe_done(t);
204
205 buffer_json_object_close(wb); // key
206 }
207
208 void rrd2rrdr_set_timestamps(RRDR *r) {
209 QUERY_TARGET *qt = r->internal.qt;
210
211 internal_fatal(qt->window.points != r->n, "QUERY: mismatch to the number of points in qt and r");
212
213 r->view.group = qt->window.group;
214 r->view.update_every = (int) query_view_update_every(qt);
215 r->view.before = qt->window.before;
216 r->view.after = qt->window.after;
217
218 r->time_grouping.points_wanted = qt->window.points;
219 r->time_grouping.resampling_group = qt->window.resampling_group;
220 r->time_grouping.resampling_divisor = qt->window.resampling_divisor;
221
222 r->rows = qt->window.points;
223
224 size_t points_wanted = qt->window.points;
225 time_t after_wanted = qt->window.after;
226 time_t before_wanted = qt->window.before; (void)before_wanted;
227
228 time_t view_update_every = r->view.update_every;
229 time_t query_granularity = (time_t)(r->view.update_every / r->view.group);
230
231 size_t rrdr_line = 0;
232 time_t first_point_end_time = after_wanted + view_update_every - query_granularity;
233 time_t now_end_time = first_point_end_time;
234
235 while (rrdr_line < points_wanted) {
236 r->t[rrdr_line++] = now_end_time;
237 now_end_time += view_update_every;
238 }
239
240 internal_fatal(r->t[0] != first_point_end_time, "QUERY: wrong first timestamp in the query");
241 internal_error(r->t[points_wanted - 1] != before_wanted,
242 "QUERY: wrong last timestamp in the query, expected %ld, found %ld",
243 before_wanted, r->t[points_wanted - 1]);
244 }