1 /*
2 * SysDB - src/core/memstore_expr.c
3 * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
28 /*
29 * This module implements arithmetic and logical expressions for in-memory
30 * stores.
31 */
33 #if HAVE_CONFIG_H
34 # include "config.h"
35 #endif /* HAVE_CONFIG_H */
37 #include "sysdb.h"
38 #include "core/memstore-private.h"
39 #include "core/data.h"
40 #include "core/object.h"
42 #include <assert.h>
43 #include <stdbool.h>
44 #include <stdlib.h>
45 #include <string.h>
47 /*
48 * private data types
49 */
51 /* iterate through either a list of child nodes or arrays */
52 struct sdb_memstore_expr_iter {
53 sdb_memstore_obj_t *obj;
54 sdb_memstore_expr_t *expr;
56 sdb_avltree_iter_t *tree;
58 sdb_data_t array;
59 size_t array_idx;
60 bool free_array;
62 sdb_memstore_matcher_t *filter;
63 };
65 /*
66 * private types
67 */
69 static int
70 expr_init(sdb_object_t *obj, va_list ap)
71 {
72 int type = va_arg(ap, int);
73 sdb_memstore_expr_t *left = va_arg(ap, sdb_memstore_expr_t *);
74 sdb_memstore_expr_t *right = va_arg(ap, sdb_memstore_expr_t *);
75 const sdb_data_t *value = va_arg(ap, const sdb_data_t *);
77 sdb_memstore_expr_t *expr = SDB_MEMSTORE_EXPR(obj);
79 if (type <= 0) {
80 if (! value)
81 return -1;
82 if ((type == TYPED_EXPR) && (! left))
83 return -1;
84 } else {
85 if (value)
86 return -1;
87 if ((! left) || (! right))
88 return -1;
89 }
91 if (value)
92 expr->data = *value;
94 sdb_object_ref(SDB_OBJ(left));
95 sdb_object_ref(SDB_OBJ(right));
97 expr->type = type;
98 expr->left = left;
99 expr->right = right;
101 /* unknown for now */
102 expr->data_type = -1;
103 return 0;
104 } /* expr_init */
106 static void
107 expr_destroy(sdb_object_t *obj)
108 {
109 sdb_memstore_expr_t *expr = SDB_MEMSTORE_EXPR(obj);
110 sdb_object_deref(SDB_OBJ(expr->left));
111 sdb_object_deref(SDB_OBJ(expr->right));
113 if (expr->data.type)
114 sdb_data_free_datum(&expr->data);
115 } /* expr_destroy */
117 static sdb_type_t expr_type = {
118 /* size = */ sizeof(sdb_memstore_expr_t),
119 /* init = */ expr_init,
120 /* destroy = */ expr_destroy,
121 };
123 /*
124 * public API
125 */
127 sdb_memstore_expr_t *
128 sdb_memstore_expr_create(int op, sdb_memstore_expr_t *left, sdb_memstore_expr_t *right)
129 {
130 sdb_data_t value = SDB_DATA_INIT;
131 sdb_memstore_expr_t *e;
133 if ((op < 0) || (SDB_DATA_CONCAT < op) || (! left) || (! right))
134 return NULL;
136 if (left->type || right->type) {
137 e = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-expr", expr_type,
138 op, left, right, NULL));
139 e->data_type = sdb_data_expr_type(op, left->type, right->type);
140 return e;
141 }
142 /* else: both expressions are constant values; evaluate now */
144 if (sdb_data_expr_eval(op, &left->data, &right->data, &value))
145 return NULL;
146 e = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-constvalue", expr_type,
147 0, NULL, NULL, &value));
148 e->data_type = value.type;
149 return e;
150 } /* sdb_memstore_expr_create */
152 sdb_memstore_expr_t *
153 sdb_memstore_expr_typed(int typ, sdb_memstore_expr_t *expr)
154 {
155 sdb_data_t value = { SDB_TYPE_INTEGER, { .integer = typ } };
156 sdb_memstore_expr_t *e;
158 if ((typ < SDB_HOST) || (SDB_ATTRIBUTE < typ))
159 return NULL;
161 e = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-typedexpr", expr_type,
162 TYPED_EXPR, expr, NULL, &value));
163 e->data_type = expr->data_type;
164 return e;
165 } /* sdb_memstore_expr_typed */
167 sdb_memstore_expr_t *
168 sdb_memstore_expr_fieldvalue(int field)
169 {
170 sdb_data_t value = { SDB_TYPE_INTEGER, { .integer = field } };
171 sdb_memstore_expr_t *e;
173 if ((field < SDB_FIELD_NAME) || (SDB_FIELD_TIMESERIES < field))
174 return NULL;
175 e = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-fieldvalue", expr_type,
176 FIELD_VALUE, NULL, NULL, &value));
177 e->data_type = SDB_FIELD_TYPE(field);
178 return e;
179 } /* sdb_memstore_expr_fieldvalue */
181 sdb_memstore_expr_t *
182 sdb_memstore_expr_attrvalue(const char *name)
183 {
184 sdb_data_t value = { SDB_TYPE_STRING, { .string = NULL} };
185 sdb_memstore_expr_t *expr;
187 value.data.string = strdup(name);
188 if (! value.data.string)
189 return NULL;
191 expr = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-attrvalue", expr_type,
192 ATTR_VALUE, NULL, NULL, &value));
193 if (! expr)
194 free(value.data.string);
195 expr->data_type = -1;
196 return expr;
197 } /* sdb_memstore_expr_attrvalue */
199 sdb_memstore_expr_t *
200 sdb_memstore_expr_constvalue(const sdb_data_t *value)
201 {
202 sdb_data_t data = SDB_DATA_INIT;
203 sdb_memstore_expr_t *e;
205 if (sdb_data_copy(&data, value))
206 return NULL;
207 e = SDB_MEMSTORE_EXPR(sdb_object_create("memstore-constvalue", expr_type,
208 0, NULL, NULL, &data));
209 e->data_type = data.type;
210 return e;
211 } /* sdb_memstore_expr_constvalue */
213 int
214 sdb_memstore_expr_eval(sdb_memstore_expr_t *expr, sdb_memstore_obj_t *obj,
215 sdb_data_t *res, sdb_memstore_matcher_t *filter)
216 {
217 sdb_data_t v1 = SDB_DATA_INIT, v2 = SDB_DATA_INIT;
218 int status = 0;
220 if ((! expr) || (! res))
221 return -1;
223 if (filter && obj && (! sdb_memstore_matcher_matches(filter, obj, NULL)))
224 obj = NULL; /* this object does not exist */
226 if (! expr->type)
227 return sdb_data_copy(res, &expr->data);
228 else if (expr->type == FIELD_VALUE)
229 return sdb_memstore_get_field(obj, (int)expr->data.data.integer, res);
230 else if (expr->type == ATTR_VALUE) {
231 status = sdb_memstore_get_attr(obj, expr->data.data.string, res, filter);
232 if ((status < 0) && obj) {
233 /* attribute does not exist => NULL */
234 status = 0;
235 res->type = SDB_TYPE_STRING;
236 res->data.string = NULL;
237 }
238 return status;
239 }
240 else if (expr->type == TYPED_EXPR) {
241 int typ = (int)expr->data.data.integer;
242 if (typ != obj->type) {
243 /* we support self-references and { service, metric } -> host */
244 if ((typ != SDB_HOST)
245 || ((obj->type != SDB_SERVICE)
246 && (obj->type != SDB_METRIC)))
247 return -1;
248 obj = obj->parent;
249 }
250 return sdb_memstore_expr_eval(expr->left, obj, res, filter);
251 }
253 if (sdb_memstore_expr_eval(expr->left, obj, &v1, filter))
254 return -1;
255 if (sdb_memstore_expr_eval(expr->right, obj, &v2, filter)) {
256 sdb_data_free_datum(&v1);
257 return -1;
258 }
260 if (sdb_data_expr_eval(expr->type, &v1, &v2, res))
261 status = -1;
262 sdb_data_free_datum(&v1);
263 sdb_data_free_datum(&v2);
264 return status;
265 } /* sdb_memstore_expr_eval */
267 sdb_memstore_expr_iter_t *
268 sdb_memstore_expr_iter(sdb_memstore_expr_t *expr, sdb_memstore_obj_t *obj,
269 sdb_memstore_matcher_t *filter)
270 {
271 sdb_memstore_expr_iter_t *iter;
272 sdb_avltree_iter_t *tree = NULL;
273 sdb_data_t array = SDB_DATA_INIT;
274 bool free_array = 0;
276 if (! expr)
277 return NULL;
279 while (expr->type == TYPED_EXPR) {
280 int type = (int)expr->data.data.integer;
282 if (obj->type == type) {
283 /* self reference */
284 }
285 else if ((type == SDB_HOST)
286 && ((obj->type == SDB_SERVICE)
287 || (obj->type == SDB_METRIC))) {
288 /* reference to parent host */
289 obj = obj->parent;
290 }
291 else
292 break;
293 expr = expr->left;
294 }
296 if (expr->type == TYPED_EXPR) {
297 if (! obj)
298 return NULL;
299 if (obj->type == SDB_HOST) {
300 if (expr->data.data.integer == SDB_SERVICE)
301 tree = sdb_avltree_get_iter(HOST(obj)->services);
302 else if (expr->data.data.integer == SDB_METRIC)
303 tree = sdb_avltree_get_iter(HOST(obj)->metrics);
304 else if (expr->data.data.integer == SDB_ATTRIBUTE)
305 tree = sdb_avltree_get_iter(HOST(obj)->attributes);
306 }
307 else if (obj->type == SDB_SERVICE) {
308 if (expr->data.data.integer == SDB_ATTRIBUTE)
309 tree = sdb_avltree_get_iter(SVC(obj)->attributes);
310 }
311 else if (obj->type == SDB_METRIC) {
312 if (expr->data.data.integer == SDB_ATTRIBUTE)
313 tree = sdb_avltree_get_iter(METRIC(obj)->attributes);
314 }
315 }
316 else if (expr->type == FIELD_VALUE) {
317 if (! obj)
318 return NULL;
319 if (expr->data.data.integer == SDB_FIELD_BACKEND) {
320 /* while scanning the store, we hold a read lock, so it's safe to
321 * access the data without copying */
322 array.type = SDB_TYPE_ARRAY | SDB_TYPE_STRING;
323 array.data.array.length = obj->backends_num;
324 array.data.array.values = obj->backends;
325 }
326 }
327 else if (! expr->type) {
328 if (expr->data.type & SDB_TYPE_ARRAY)
329 array = expr->data;
330 }
331 else {
332 sdb_data_t value = SDB_DATA_INIT;
333 if (sdb_memstore_expr_eval(expr, obj, &value, filter))
334 return NULL;
335 if (! (value.type & SDB_TYPE_ARRAY)) {
336 sdb_data_free_datum(&value);
337 return NULL;
338 }
339 array = value;
340 free_array = 1;
341 }
343 if ((! tree) && (array.type == SDB_TYPE_NULL))
344 return NULL;
346 iter = calloc(1, sizeof(*iter));
347 if (! iter) {
348 if (free_array)
349 sdb_data_free_datum(&array);
350 return NULL;
351 }
353 sdb_object_ref(SDB_OBJ(obj));
354 sdb_object_ref(SDB_OBJ(expr));
355 sdb_object_ref(SDB_OBJ(filter));
357 iter->obj = obj;
358 iter->expr = expr;
359 iter->tree = tree;
360 iter->array = array;
361 iter->free_array = free_array;
362 iter->filter = filter;
363 return iter;
364 } /* sdb_memstore_expr_iter */
366 void
367 sdb_memstore_expr_iter_destroy(sdb_memstore_expr_iter_t *iter)
368 {
369 sdb_data_t null = SDB_DATA_INIT;
371 if (! iter)
372 return;
374 if (iter->tree)
375 sdb_avltree_iter_destroy(iter->tree);
376 iter->tree = NULL;
378 if (iter->free_array)
379 sdb_data_free_datum(&iter->array);
380 iter->array = null;
381 iter->array_idx = 0;
383 sdb_object_deref(SDB_OBJ(iter->obj));
384 sdb_object_deref(SDB_OBJ(iter->expr));
385 sdb_object_deref(SDB_OBJ(iter->filter));
386 free(iter);
387 } /* sdb_memstore_expr_iter_destroy */
389 bool
390 sdb_memstore_expr_iter_has_next(sdb_memstore_expr_iter_t *iter)
391 {
392 if (! iter)
393 return 0;
395 if (iter->tree) {
396 /* this function may be called before get_next,
397 * so we'll have to apply filters here as well */
398 if (iter->filter) {
399 sdb_memstore_obj_t *child;
400 while ((child = STORE_OBJ(sdb_avltree_iter_peek_next(iter->tree)))) {
401 if (sdb_memstore_matcher_matches(iter->filter, child, NULL))
402 break;
403 (void)sdb_avltree_iter_get_next(iter->tree);
404 }
405 }
407 return sdb_avltree_iter_has_next(iter->tree);
408 }
410 return iter->array_idx < iter->array.data.array.length;
411 } /* sdb_memstore_expr_iter_has_next */
413 sdb_data_t
414 sdb_memstore_expr_iter_get_next(sdb_memstore_expr_iter_t *iter)
415 {
416 sdb_data_t null = SDB_DATA_INIT;
417 sdb_data_t ret = SDB_DATA_INIT;
418 sdb_data_t tmp = SDB_DATA_INIT;
420 if (! iter)
421 return null;
423 if (iter->tree) {
424 sdb_memstore_obj_t *child;
426 while (42) {
427 child = STORE_OBJ(sdb_avltree_iter_get_next(iter->tree));
428 if (! child)
429 break;
430 if (iter->filter
431 && (! sdb_memstore_matcher_matches(iter->filter, child, NULL)))
432 continue;
434 if (sdb_memstore_expr_eval(iter->expr, child, &ret, iter->filter))
435 return null;
436 break;
437 }
439 /* Skip over any filtered objects */
440 if (iter->filter) {
441 while ((child = STORE_OBJ(sdb_avltree_iter_peek_next(iter->tree)))) {
442 if (sdb_memstore_matcher_matches(iter->filter, child, NULL))
443 break;
444 (void)sdb_avltree_iter_get_next(iter->tree);
445 }
446 }
448 return ret;
449 }
451 if (iter->array_idx >= iter->array.data.array.length)
452 return null;
454 ++iter->array_idx;
455 if (sdb_data_array_get(&iter->array, iter->array_idx - 1, &ret))
456 return null;
457 if (sdb_data_copy(&tmp, &ret))
458 return null;
459 ret = tmp;
460 return ret;
461 } /* sdb_memstore_expr_iter_get_next */
463 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */