f12dff2005a953d39910eec9ce8e026f67c4be95
1 /*
2 * SysDB - src/parser/analyzer.c
3 * Copyright (C) 2014-2015 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 #include "sysdb.h"
30 #include "parser/ast.h"
31 #include "parser/parser.h"
32 #include "utils/error.h"
33 #include "utils/strbuf.h"
35 #include <assert.h>
36 #include <stdarg.h>
37 #include <stdbool.h>
38 #include <string.h>
40 #define VALID_OBJ_TYPE(t) ((SDB_HOST <= (t)) && ((t) <= SDB_METRIC))
42 typedef struct {
43 int type;
44 bool iter;
45 } context_t;
47 #define FILTER_CONTEXT -1
48 static const context_t FILTER_CTX = { FILTER_CONTEXT, 0 };
50 static int
51 analyze_node(context_t ctx, sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
53 /*
54 * error reporting
55 */
57 static void
58 op_error(sdb_strbuf_t *errbuf, sdb_ast_op_t *op, const char *reason)
59 {
60 sdb_strbuf_sprintf(errbuf, "Invalid operation %s %s %s (%s)",
61 SDB_TYPE_TO_STRING(op->left->data_type),
62 SDB_AST_OP_TO_STRING(op->kind),
63 SDB_TYPE_TO_STRING(op->right->data_type),
64 reason);
65 } /* op_error */
67 static void
68 __attribute__((format(printf, 3, 4)))
69 iter_error(sdb_strbuf_t *errbuf, sdb_ast_iter_t *iter, const char *reason, ...)
70 {
71 char r[1024];
72 va_list ap;
74 va_start(ap, reason);
75 vsnprintf(r, sizeof(r), reason, ap);
76 va_end(ap);
78 assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
79 && (! SDB_AST_OP(iter->expr)->left));
80 sdb_strbuf_sprintf(errbuf, "Invalid iterator %s %s %s %s (%s)",
81 SDB_AST_OP_TO_STRING(iter->kind),
82 SDB_TYPE_TO_STRING(iter->iter->data_type),
83 SDB_AST_OP_TO_STRING(SDB_AST_OP(iter->expr)->kind),
84 SDB_TYPE_TO_STRING(SDB_AST_OP(iter->expr)->right->data_type),
85 r);
86 } /* iter_error */
88 /*
89 * expression nodes
90 */
92 static int
93 analyze_logical(context_t ctx, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
94 {
95 if (ctx.iter) {
96 op_error(errbuf, op, "cannot evaluate in iterator context");
97 return -1;
98 }
100 switch (op->kind) {
101 case SDB_AST_OR:
102 case SDB_AST_AND:
103 if (! SDB_AST_IS_LOGICAL(op->left)) {
104 sdb_strbuf_sprintf(errbuf, "Invalid left operand (%s) "
105 "in %s expression", SDB_AST_TYPE_TO_STRING(op->left),
106 SDB_AST_OP_TO_STRING(op->kind));
107 return -1;
108 }
109 if (analyze_node(ctx, op->left, errbuf))
110 return -1;
111 /* fallthrough */
112 case SDB_AST_NOT:
113 if (! SDB_AST_IS_LOGICAL(op->right)) {
114 sdb_strbuf_sprintf(errbuf, "Invalid right operand (%s) "
115 "in %s expression", SDB_AST_TYPE_TO_STRING(op->right),
116 SDB_AST_OP_TO_STRING(op->kind));
117 return -1;
118 }
119 if (analyze_node(ctx, op->right, errbuf))
120 return -1;
121 break;
123 case SDB_AST_LT:
124 case SDB_AST_LE:
125 case SDB_AST_EQ:
126 case SDB_AST_NE:
127 case SDB_AST_GE:
128 case SDB_AST_GT:
129 {
130 if (analyze_node(ctx, op->left, errbuf))
131 return -1;
132 if (analyze_node(ctx, op->right, errbuf))
133 return -1;
135 if ((op->left->data_type > 0) && (op->right->data_type > 0)) {
136 if (op->left->data_type == op->right->data_type)
137 return 0;
138 op_error(errbuf, op, "type mismatch");
139 return -1;
140 }
141 if ((op->left->data_type > 0) && (op->left->data_type & SDB_TYPE_ARRAY)) {
142 op_error(errbuf, op, "array not allowed");
143 return -1;
144 }
145 if ((op->right->data_type > 0) && (op->right->data_type & SDB_TYPE_ARRAY)) {
146 op_error(errbuf, op, "array not allowed");
147 return -1;
148 }
149 break;
150 }
152 case SDB_AST_REGEX:
153 case SDB_AST_NREGEX:
154 if (analyze_node(ctx, op->left, errbuf))
155 return -1;
156 if (analyze_node(ctx, op->right, errbuf))
157 return -1;
159 /* all types are supported for the left operand
160 * TODO: introduce a cast operator if it's not a string */
161 if ((op->right->data_type > 0)
162 && (op->right->data_type != SDB_TYPE_REGEX)
163 && (op->right->data_type != SDB_TYPE_STRING)) {
164 op_error(errbuf, op, "invalid regex");
165 return -1;
166 }
167 break;
169 case SDB_AST_ISNULL:
170 case SDB_AST_ISTRUE:
171 case SDB_AST_ISFALSE:
172 if (analyze_node(ctx, op->right, errbuf))
173 return -1;
174 break;
176 case SDB_AST_IN:
177 if (analyze_node(ctx, op->left, errbuf))
178 return -1;
179 if (analyze_node(ctx, op->right, errbuf))
180 return -1;
182 if ((op->right->data_type > 0) && (! (op->right->data_type & SDB_TYPE_ARRAY))) {
183 op_error(errbuf, op, "array expected");
184 return -1;
185 }
186 /* the left operand may be a scalar or an array but the element
187 * type has to match */
188 if ((op->left->data_type > 0) && (op->right->data_type > 0)
189 && ((op->left->data_type & 0xff) != (op->right->data_type & 0xff))) {
190 op_error(errbuf, op, "type mismatch");
191 return -1;
192 }
193 break;
195 default:
196 sdb_strbuf_sprintf(errbuf, "Unknown operand type %d", op->kind);
197 return -1;
198 }
199 return 0;
200 } /* analyze_logical */
202 static int
203 analyze_arith(context_t ctx, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
204 {
205 if (ctx.iter) {
206 op_error(errbuf, op, "cannot evaluate in iterator context");
207 return -1;
208 }
210 if (analyze_node(ctx, op->left, errbuf))
211 return -1;
212 if (analyze_node(ctx, op->right, errbuf))
213 return -1;
214 SDB_AST_NODE(op)->data_type = sdb_data_expr_type(SDB_AST_OP_TO_DATA_OP(op->kind),
215 op->left->data_type, op->right->data_type);
217 if ((op->left->data_type > 0) && (op->right->data_type > 0)
218 && (SDB_AST_NODE(op)->data_type <= 0)) {
219 op_error(errbuf, op, "type mismatch");
220 return -1;
221 }
223 /* TODO: replace constant arithmetic operations with a constant value */
224 return 0;
225 } /* analyze_arith */
227 static int
228 analyze_iter(context_t ctx, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
229 {
230 sdb_ast_const_t c = SDB_AST_CONST_INIT;
231 context_t iter_ctx = ctx;
232 int status;
234 if (ctx.iter) {
235 iter_error(errbuf, iter, "nested iterators are not supported");
236 return -1;
237 }
239 iter_ctx.iter = 1;
240 if (analyze_node(iter_ctx, iter->iter, errbuf))
241 return -1;
243 if (iter->iter->data_type > 0) {
244 if (! (iter->iter->data_type & SDB_TYPE_ARRAY)) {
245 iter_error(errbuf, iter, "cannot iterate values of type %s",
246 SDB_TYPE_TO_STRING(iter->iter->data_type));
247 return -1;
248 }
249 c.value.type = iter->iter->data_type & 0xff;
250 }
252 /* TODO: support other setups as well */
253 assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
254 && (! SDB_AST_OP(iter->expr)->left));
256 SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
257 status = analyze_node(ctx, iter->expr, errbuf);
258 SDB_AST_OP(iter->expr)->left = NULL;
259 if (status)
260 return -1;
261 return 0;
262 } /* analyze_iter */
264 static int
265 analyze_const(context_t __attribute__((unused)) ctx, sdb_ast_const_t *c,
266 sdb_strbuf_t __attribute__((unused)) *errbuf)
267 {
268 SDB_AST_NODE(c)->data_type = c->value.type;
269 return 0;
270 } /* analyze_const */
272 static int
273 analyze_value(context_t ctx, sdb_ast_value_t *v, sdb_strbuf_t *errbuf)
274 {
275 if (v->type != SDB_ATTRIBUTE)
276 SDB_AST_NODE(v)->data_type = SDB_FIELD_TYPE(v->type);
278 if ((v->type != SDB_ATTRIBUTE) && v->name) {
279 sdb_strbuf_sprintf(errbuf, "Invalid expression %s[%s]",
280 SDB_FIELD_TO_NAME(v->type), v->name);
281 return -1;
282 }
283 else if ((v->type == SDB_ATTRIBUTE) && (! v->name)) {
284 sdb_strbuf_sprintf(errbuf, "Invalid expression attribute[] "
285 "(missing name)");
286 return -1;
287 }
289 /* this would be caught by the type check in analyze_iter but we're able
290 * to provide a more specific error message here */
291 if (ctx.iter && (v->type != SDB_FIELD_BACKEND)) {
292 /* only backend values are iterable */
293 char value_str[64 + (v->name ? strlen(v->name) : 0)];
294 if (v->type == SDB_ATTRIBUTE)
295 snprintf(value_str, sizeof(value_str), "attribute[%s]", v->name);
296 else
297 snprintf(value_str, sizeof(value_str), "'%s'", SDB_FIELD_TO_NAME(v->type));
298 sdb_strbuf_sprintf(errbuf, "Cannot iterate %s (scalar value)", value_str);
299 return -1;
300 }
302 if ((ctx.type != SDB_ATTRIBUTE) && (v->type == SDB_FIELD_VALUE)) {
303 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.value",
304 SDB_FIELD_TO_NAME(ctx.type));
305 return -1;
306 }
307 if ((ctx.type != SDB_METRIC) && (v->type == SDB_FIELD_TIMESERIES)) {
308 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.timeseries",
309 SDB_FIELD_TO_NAME(ctx.type));
310 return -1;
311 }
312 return 0;
313 } /* analyze_value */
315 static int
316 analyze_typed(context_t ctx, sdb_ast_typed_t *t, sdb_strbuf_t *errbuf)
317 {
318 context_t child_ctx = ctx;
319 bool needs_iter = 0;
320 bool valid = 1;
322 if ((t->expr->type != SDB_AST_TYPE_VALUE)
323 && (t->expr->type != SDB_AST_TYPE_TYPED)) {
324 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s",
325 SDB_STORE_TYPE_TO_NAME(t->type),
326 SDB_AST_TYPE_TO_STRING(t->expr));
327 return -1;
328 }
329 if ((t->type != SDB_ATTRIBUTE) && (! VALID_OBJ_TYPE(t->type))) {
330 sdb_strbuf_sprintf(errbuf, "Invalid expression %#x.%s",
331 t->type, SDB_AST_TYPE_TO_STRING(t->expr));
332 return -1;
333 }
335 if (ctx.type > 0) {
336 if ((ctx.type == t->type)
337 || ((t->type == SDB_HOST) && (ctx.type != SDB_ATTRIBUTE))) {
338 /* self-references and references to the parent host are always fine */
339 }
340 else if (t->type == SDB_ATTRIBUTE) {
341 /* references to attributes are always fine */
342 needs_iter = 1;
343 }
344 else if ((ctx.type == SDB_HOST)
345 && ((t->type == SDB_SERVICE) || (t->type == SDB_METRIC))) {
346 /* only hosts may reference services and metrics */
347 needs_iter = 1;
348 }
349 else {
350 valid = 0;
351 }
352 }
353 else if (ctx.type == FILTER_CONTEXT) {
354 if (t->type == SDB_ATTRIBUTE) {
355 /* all objects have attributes */
356 needs_iter = 1;
357 }
358 else if ((t->type == SDB_SERVICE) || (t->type == SDB_METRIC)) {
359 /* these will be iterators for *some* operations;
360 * better forbid this altogether */
361 valid = 0;
362 }
363 }
365 if (needs_iter) {
366 if (! ctx.iter)
367 valid = 0;
368 else
369 child_ctx.iter = 0;
370 } /* else: push ctx.iter down to the child node */
372 if (! valid) {
373 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s in %s context",
374 SDB_STORE_TYPE_TO_NAME(t->type),
375 SDB_AST_TYPE_TO_STRING(t->expr),
376 SDB_STORE_TYPE_TO_NAME(ctx.type));
377 return -1;
378 }
380 child_ctx.type = t->type;
381 if (analyze_node(child_ctx, t->expr, errbuf))
382 return -1;
383 SDB_AST_NODE(t)->data_type = t->expr->data_type;
385 if (needs_iter && (SDB_AST_NODE(t)->data_type > 0)) {
386 if (SDB_AST_NODE(t)->data_type & SDB_TYPE_ARRAY) {
387 sdb_strbuf_sprintf(errbuf, "Cannot access array inside iterator");
388 return -1;
389 }
390 /* Tell the caller that we're accessing an iterator. */
391 SDB_AST_NODE(t)->data_type |= SDB_TYPE_ARRAY;
392 }
393 return 0;
394 } /* analyze_typed */
396 static int
397 analyze_node(context_t ctx, sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
398 {
399 if (! node) {
400 sdb_strbuf_sprintf(errbuf, "Empty AST node");
401 return -1;
402 }
404 /* unknown by default */
405 node->data_type = -1;
407 if ((node->type == SDB_AST_TYPE_OPERATOR)
408 && (SDB_AST_IS_LOGICAL(node)))
409 return analyze_logical(ctx, SDB_AST_OP(node), errbuf);
410 else if ((node->type == SDB_AST_TYPE_OPERATOR)
411 && (SDB_AST_IS_ARITHMETIC(node)))
412 return analyze_arith(ctx, SDB_AST_OP(node), errbuf);
413 else if (node->type == SDB_AST_TYPE_ITERATOR)
414 return analyze_iter(ctx, SDB_AST_ITER(node), errbuf);
415 else if (node->type == SDB_AST_TYPE_CONST)
416 return analyze_const(ctx, SDB_AST_CONST(node), errbuf);
417 else if (node->type == SDB_AST_TYPE_VALUE)
418 return analyze_value(ctx, SDB_AST_VALUE(node), errbuf);
419 else if (node->type == SDB_AST_TYPE_TYPED)
420 return analyze_typed(ctx, SDB_AST_TYPED(node), errbuf);
422 sdb_strbuf_sprintf(errbuf, "Invalid expression node "
423 "of type %#x", node->type);
424 return -1;
425 } /* analyze_node */
427 /*
428 * top level / command nodes
429 */
431 static int
432 analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf)
433 {
434 if (! VALID_OBJ_TYPE(fetch->obj_type)) {
435 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
436 "in FETCH command", fetch->obj_type);
437 return -1;
438 }
439 if (! fetch->name) {
440 sdb_strbuf_sprintf(errbuf, "Missing object name in "
441 "FETCH %s command", SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
442 return -1;
443 }
445 if ((fetch->obj_type == SDB_HOST) && fetch->hostname) {
446 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
447 "in FETCH HOST command", fetch->hostname);
448 return -1;
449 }
450 else if ((fetch->obj_type != SDB_HOST) && (! fetch->hostname)) {
451 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
452 "in FETCH %s command", fetch->name,
453 SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
454 return -1;
455 }
457 if (fetch->filter)
458 return analyze_node(FILTER_CTX, fetch->filter, errbuf);
459 return 0;
460 } /* analyze_fetch */
462 static int
463 analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf)
464 {
465 if (! VALID_OBJ_TYPE(list->obj_type)) {
466 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
467 "in LIST command", list->obj_type);
468 return -1;
469 }
470 if (list->filter)
471 return analyze_node(FILTER_CTX, list->filter, errbuf);
472 return 0;
473 } /* analyze_list */
475 static int
476 analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf)
477 {
478 if (! VALID_OBJ_TYPE(lookup->obj_type)) {
479 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
480 "in LOOKUP command", lookup->obj_type);
481 return -1;
482 }
483 if (lookup->matcher) {
484 context_t ctx = { lookup->obj_type, 0 };
485 if (analyze_node(ctx, lookup->matcher, errbuf))
486 return -1;
487 }
488 if (lookup->filter)
489 return analyze_node(FILTER_CTX, lookup->filter, errbuf);
490 return 0;
491 } /* analyze_lookup */
493 static int
494 analyze_store(sdb_ast_store_t *st, sdb_strbuf_t *errbuf)
495 {
496 if ((st->obj_type != SDB_ATTRIBUTE)
497 && (! VALID_OBJ_TYPE(st->obj_type))) {
498 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
499 "in STORE command", st->obj_type);
500 return -1;
501 }
502 if (! st->name) {
503 sdb_strbuf_sprintf(errbuf, "Missing object name in "
504 "STORE %s command", SDB_STORE_TYPE_TO_NAME(st->obj_type));
505 return -1;
506 }
508 if ((st->obj_type == SDB_HOST) && st->hostname) {
509 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
510 "in STORE HOST command", st->hostname);
511 return -1;
512 }
513 else if ((st->obj_type != SDB_HOST) && (! st->hostname)) {
514 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
515 "in STORE %s command", st->name,
516 SDB_STORE_TYPE_TO_NAME(st->obj_type));
517 return -1;
518 }
520 if (st->obj_type == SDB_ATTRIBUTE) {
521 if ((st->parent_type <= 0) && st->parent) {
522 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
523 "in STORE %s command", st->parent,
524 SDB_STORE_TYPE_TO_NAME(st->obj_type));
525 return -1;
526 }
527 else if (st->parent_type > 0) {
528 if (! VALID_OBJ_TYPE(st->parent_type)) {
529 sdb_strbuf_sprintf(errbuf, "Invalid parent type %#x "
530 "in STORE %s command", st->parent_type,
531 SDB_STORE_TYPE_TO_NAME(st->obj_type));
532 return -1;
533 }
534 if (! st->parent) {
535 sdb_strbuf_sprintf(errbuf, "Missing %s parent name "
536 "in STORE %s command",
537 SDB_STORE_TYPE_TO_NAME(st->parent_type),
538 SDB_STORE_TYPE_TO_NAME(st->obj_type));
539 return -1;
540 }
541 }
542 }
543 else if ((st->parent_type > 0) || st->parent) {
544 sdb_strbuf_sprintf(errbuf, "Unexpected %s parent name '%s' "
545 "in STORE %s command",
546 SDB_STORE_TYPE_TO_NAME(st->parent_type),
547 st->parent ? st->parent : "<unknown>",
548 SDB_STORE_TYPE_TO_NAME(st->obj_type));
549 return -1;
550 }
552 if (st->obj_type == SDB_METRIC) {
553 if ((! st->store_type) != (! st->store_id)) {
554 sdb_strbuf_sprintf(errbuf, "Incomplete metric store %s %s "
555 "in STORE METRIC command",
556 st->store_type ? st->store_type : "<unknown>",
557 st->store_id ? st->store_id : "<unknown>");
558 return -1;
559 }
560 }
561 else if (st->store_type || st->store_id) {
562 sdb_strbuf_sprintf(errbuf, "Unexpected metric store %s %s "
563 "in STORE %s command",
564 st->store_type ? st->store_type : "<unknown>",
565 st->store_id ? st->store_id : "<unknown>",
566 SDB_STORE_TYPE_TO_NAME(st->obj_type));
567 return -1;
568 }
570 if ((! (st->obj_type == SDB_ATTRIBUTE))
571 && (st->value.type != SDB_TYPE_NULL)) {
572 char v_str[sdb_data_format(&st->value, NULL, 0, SDB_DOUBLE_QUOTED) + 1];
573 sdb_data_format(&st->value, v_str, sizeof(v_str), SDB_DOUBLE_QUOTED);
574 sdb_strbuf_sprintf(errbuf, "Unexpected value %s in STORE %s command",
575 v_str, SDB_STORE_TYPE_TO_NAME(st->obj_type));
576 return -1;
577 }
578 return 0;
579 } /* analyze_store */
581 static int
582 analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf)
583 {
584 if (! ts->hostname) {
585 sdb_strbuf_sprintf(errbuf, "Missing hostname in TIMESERIES command");
586 return -1;
587 }
588 if (! ts->metric) {
589 sdb_strbuf_sprintf(errbuf, "Missing metric name in TIMESERIES command");
590 return -1;
591 }
592 if (ts->end <= ts->start) {
593 char start_str[64], end_str[64];
594 sdb_strftime(start_str, sizeof(start_str), ts->start);
595 sdb_strftime(end_str, sizeof(end_str), ts->end);
596 sdb_strbuf_sprintf(errbuf, "Start time (%s) greater than "
597 "end time (%s) in TIMESERIES command", start_str, end_str);
598 return -1;
599 }
600 return 0;
601 } /* analyze_timeseries */
603 /*
604 * public API
605 */
607 int
608 sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
609 {
610 if (! node) {
611 sdb_strbuf_sprintf(errbuf, "Empty AST node");
612 return -1;
613 }
615 /* top-level nodes don't have a type */
616 node->data_type = -1;
618 if (node->type == SDB_AST_TYPE_FETCH)
619 return analyze_fetch(SDB_AST_FETCH(node), errbuf);
620 else if (node->type == SDB_AST_TYPE_LIST)
621 return analyze_list(SDB_AST_LIST(node), errbuf);
622 else if (node->type == SDB_AST_TYPE_LOOKUP)
623 return analyze_lookup(SDB_AST_LOOKUP(node), errbuf);
624 else if (node->type == SDB_AST_TYPE_STORE)
625 return analyze_store(SDB_AST_STORE(node), errbuf);
626 else if (node->type == SDB_AST_TYPE_TIMESERIES)
627 return analyze_timeseries(SDB_AST_TIMESERIES(node), errbuf);
629 sdb_strbuf_sprintf(errbuf, "Invalid top-level AST node "
630 "of type %#x", node->type);
631 return -1;
632 } /* sdb_parser_analyze */
634 int
635 sdb_parser_analyze_conditional(int context,
636 sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
637 {
638 context_t ctx = { context, 0 };
639 if (! VALID_OBJ_TYPE(context)) {
640 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
641 return -1;
642 }
643 if (! node) {
644 sdb_strbuf_sprintf(errbuf, "Empty conditional node");
645 return -1;
646 }
647 if (! SDB_AST_IS_LOGICAL(node)) {
648 sdb_strbuf_sprintf(errbuf, "Not a conditional node (got %s)",
649 SDB_AST_TYPE_TO_STRING(node));
650 return -1;
651 }
652 return analyze_node(ctx, node, errbuf);
653 } /* sdb_parser_analyze_conditional */
655 int
656 sdb_parser_analyze_arith(int context,
657 sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
658 {
659 context_t ctx = { context, 0 };
660 if (! VALID_OBJ_TYPE(context)) {
661 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
662 return -1;
663 }
664 if (! node) {
665 sdb_strbuf_sprintf(errbuf, "Empty arithmetic node");
666 return -1;
667 }
668 if (! SDB_AST_IS_ARITHMETIC(node)) {
669 sdb_strbuf_sprintf(errbuf, "Not an arithmetic node (got %s)",
670 SDB_AST_TYPE_TO_STRING(node));
671 return -1;
672 }
673 return analyze_node(ctx, node, errbuf);
674 } /* sdb_parser_analyze_arith */
676 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */