Code

Add support for fetching arbitrary objects, including attributes.
[sysdb.git] / src / parser / analyzer.c
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  * generic checks
90  */
92 typedef struct {
93         int obj_type;
94         const char *hostname;
95         int parent_type;
96         const char *parent;
97         const char *name;
98 } parent_child_t;
100 static int
101 analyze_parent_child(const char *cmd, parent_child_t *pc, sdb_strbuf_t *errbuf)
103         if ((pc->obj_type != SDB_ATTRIBUTE)
104                         && (! VALID_OBJ_TYPE(pc->obj_type))) {
105                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
106                                 "in %s command", pc->obj_type, cmd);
107                 return -1;
108         }
109         if (! pc->name) {
110                 sdb_strbuf_sprintf(errbuf, "Missing object name in "
111                                 "%s %s command", cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
112                 return -1;
113         }
115         if ((pc->obj_type == SDB_HOST) && pc->hostname) {
116                 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
117                                 "in %s HOST command", pc->hostname, cmd);
118                 return -1;
119         }
120         else if ((pc->obj_type != SDB_HOST) && (! pc->hostname)) {
121                 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
122                                 "in %s %s command", pc->name,
123                                 cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
124                 return -1;
125         }
127         if (pc->obj_type == SDB_ATTRIBUTE) {
128                 if ((pc->parent_type <= 0) && pc->parent) {
129                         sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
130                                         "in %s %s command", pc->parent,
131                                         cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
132                         return -1;
133                 }
134                 else if (pc->parent_type > 0) {
135                         if (! VALID_OBJ_TYPE(pc->parent_type)) {
136                                 sdb_strbuf_sprintf(errbuf, "Invalid parent type %#x "
137                                                 "in %s %s command", pc->parent_type,
138                                                 cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
139                                 return -1;
140                         }
141                         if (! pc->parent) {
142                                 sdb_strbuf_sprintf(errbuf, "Missing %s parent name "
143                                                 "in %s %s command",
144                                                 SDB_STORE_TYPE_TO_NAME(pc->parent_type),
145                                                 cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
146                                 return -1;
147                         }
148                 }
149         }
150         else if ((pc->parent_type > 0) || pc->parent) {
151                 sdb_strbuf_sprintf(errbuf, "Unexpected %s parent name '%s' "
152                                 "in %s %s command",
153                                 SDB_STORE_TYPE_TO_NAME(pc->parent_type),
154                                 pc->parent ? pc->parent : "<unknown>",
155                                 cmd, SDB_STORE_TYPE_TO_NAME(pc->obj_type));
156                 return -1;
157         }
158         return 0;
159 } /* analyze_parent_child */
161 /*
162  * expression nodes
163  */
165 static int
166 analyze_logical(context_t ctx, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
168         if (ctx.iter) {
169                 op_error(errbuf, op, "cannot evaluate in iterator context");
170                 return -1;
171         }
173         switch (op->kind) {
174         case SDB_AST_OR:
175         case SDB_AST_AND:
176                 if (! SDB_AST_IS_LOGICAL(op->left)) {
177                         sdb_strbuf_sprintf(errbuf, "Invalid left operand (%s) "
178                                         "in %s expression", SDB_AST_TYPE_TO_STRING(op->left),
179                                         SDB_AST_OP_TO_STRING(op->kind));
180                         return -1;
181                 }
182                 if (analyze_node(ctx, op->left, errbuf))
183                         return -1;
184                 /* fallthrough */
185         case SDB_AST_NOT:
186                 if (! SDB_AST_IS_LOGICAL(op->right)) {
187                         sdb_strbuf_sprintf(errbuf, "Invalid right operand (%s) "
188                                         "in %s expression", SDB_AST_TYPE_TO_STRING(op->right),
189                                         SDB_AST_OP_TO_STRING(op->kind));
190                         return -1;
191                 }
192                 if (analyze_node(ctx, op->right, errbuf))
193                         return -1;
194                 break;
196         case SDB_AST_LT:
197         case SDB_AST_LE:
198         case SDB_AST_EQ:
199         case SDB_AST_NE:
200         case SDB_AST_GE:
201         case SDB_AST_GT:
202         {
203                 if (analyze_node(ctx, op->left, errbuf))
204                         return -1;
205                 if (analyze_node(ctx, op->right, errbuf))
206                         return -1;
208                 if ((op->left->data_type > 0) && (op->right->data_type > 0)) {
209                         if (op->left->data_type == op->right->data_type)
210                                 return 0;
211                         op_error(errbuf, op, "type mismatch");
212                         return -1;
213                 }
214                 if ((op->left->data_type > 0) && (op->left->data_type & SDB_TYPE_ARRAY)) {
215                         op_error(errbuf, op, "array not allowed");
216                         return -1;
217                 }
218                 if ((op->right->data_type > 0) && (op->right->data_type & SDB_TYPE_ARRAY)) {
219                         op_error(errbuf, op, "array not allowed");
220                         return -1;
221                 }
222                 break;
223         }
225         case SDB_AST_REGEX:
226         case SDB_AST_NREGEX:
227                 if (analyze_node(ctx, op->left, errbuf))
228                         return -1;
229                 if (analyze_node(ctx, op->right, errbuf))
230                         return -1;
232                 /* all types are supported for the left operand
233                  * TODO: introduce a cast operator if it's not a string */
234                 if ((op->right->data_type > 0)
235                                 && (op->right->data_type != SDB_TYPE_REGEX)
236                                 && (op->right->data_type != SDB_TYPE_STRING)) {
237                         op_error(errbuf, op, "invalid regex");
238                         return -1;
239                 }
240                 break;
242         case SDB_AST_ISNULL:
243         case SDB_AST_ISTRUE:
244         case SDB_AST_ISFALSE:
245                 if (analyze_node(ctx, op->right, errbuf))
246                         return -1;
247                 break;
249         case SDB_AST_IN:
250                 if (analyze_node(ctx, op->left, errbuf))
251                         return -1;
252                 if (analyze_node(ctx, op->right, errbuf))
253                         return -1;
255                 if ((op->right->data_type > 0) && (! (op->right->data_type & SDB_TYPE_ARRAY))) {
256                         op_error(errbuf, op, "array expected");
257                         return -1;
258                 }
259                 /* the left operand may be a scalar or an array but the element
260                  * type has to match */
261                 if ((op->left->data_type > 0) && (op->right->data_type > 0)
262                                 && ((op->left->data_type & 0xff) != (op->right->data_type & 0xff))) {
263                         op_error(errbuf, op, "type mismatch");
264                         return -1;
265                 }
266                 break;
268         default:
269                 sdb_strbuf_sprintf(errbuf, "Unknown operand type %d", op->kind);
270                 return -1;
271         }
272         return 0;
273 } /* analyze_logical */
275 static int
276 analyze_arith(context_t ctx, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
278         if (analyze_node(ctx, op->left, errbuf))
279                 return -1;
280         if (analyze_node(ctx, op->right, errbuf))
281                 return -1;
282         SDB_AST_NODE(op)->data_type = sdb_data_expr_type(SDB_AST_OP_TO_DATA_OP(op->kind),
283                         op->left->data_type, op->right->data_type);
285         if ((op->left->data_type > 0) && (op->right->data_type > 0)
286                         && (SDB_AST_NODE(op)->data_type <= 0)) {
287                 op_error(errbuf, op, "type mismatch");
288                 return -1;
289         }
291         /* TODO: replace constant arithmetic operations with a constant value */
292         return 0;
293 } /* analyze_arith */
295 static int
296 analyze_iter(context_t ctx, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
298         sdb_ast_const_t c = SDB_AST_CONST_INIT;
299         context_t iter_ctx = ctx;
300         int status;
302         if (ctx.iter) {
303                 iter_error(errbuf, iter, "nested iterators are not supported");
304                 return -1;
305         }
307         iter_ctx.iter = 1;
308         if (analyze_node(iter_ctx, iter->iter, errbuf))
309                 return -1;
311         if (iter->iter->data_type > 0) {
312                 if (! (iter->iter->data_type & SDB_TYPE_ARRAY)) {
313                         iter_error(errbuf, iter, "cannot iterate values of type %s",
314                                         SDB_TYPE_TO_STRING(iter->iter->data_type));
315                         return -1;
316                 }
317                 c.value.type = iter->iter->data_type & 0xff;
318         }
320         /* TODO: support other setups as well */
321         assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
322                         && (! SDB_AST_OP(iter->expr)->left));
324         SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
325         status = analyze_node(ctx, iter->expr, errbuf);
326         SDB_AST_OP(iter->expr)->left = NULL;
327         if (status)
328                 return -1;
329         return 0;
330 } /* analyze_iter */
332 static int
333 analyze_const(context_t __attribute__((unused)) ctx, sdb_ast_const_t *c,
334                 sdb_strbuf_t __attribute__((unused)) *errbuf)
336         SDB_AST_NODE(c)->data_type = c->value.type;
337         return 0;
338 } /* analyze_const */
340 static int
341 analyze_value(context_t ctx, sdb_ast_value_t *v, sdb_strbuf_t *errbuf)
343         if (v->type != SDB_ATTRIBUTE)
344                 SDB_AST_NODE(v)->data_type = SDB_FIELD_TYPE(v->type);
346         if ((v->type != SDB_ATTRIBUTE) && v->name) {
347                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s[%s]",
348                                 SDB_FIELD_TO_NAME(v->type), v->name);
349                 return -1;
350         }
351         else if ((v->type == SDB_ATTRIBUTE) && (! v->name)) {
352                 sdb_strbuf_sprintf(errbuf, "Invalid expression attribute[] "
353                                 "(missing name)");
354                 return -1;
355         }
357         /* this would be caught by the type check in analyze_iter but we're able
358          * to provide a more specific error message here */
359         if (ctx.iter && (v->type != SDB_FIELD_BACKEND)) {
360                 /* only backend values are iterable */
361                 char value_str[64 + (v->name ? strlen(v->name) : 0)];
362                 if (v->type == SDB_ATTRIBUTE)
363                         snprintf(value_str, sizeof(value_str), "attribute[%s]", v->name);
364                 else
365                         snprintf(value_str, sizeof(value_str), "'%s'", SDB_FIELD_TO_NAME(v->type));
366                 sdb_strbuf_sprintf(errbuf, "Cannot iterate %s (scalar value)", value_str);
367                 return -1;
368         }
370         if ((ctx.type != SDB_ATTRIBUTE) && (v->type == SDB_FIELD_VALUE)) {
371                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.value",
372                                 SDB_FIELD_TO_NAME(ctx.type));
373                 return -1;
374         }
375         if ((ctx.type != SDB_METRIC) && (v->type == SDB_FIELD_TIMESERIES)) {
376                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.timeseries",
377                                 SDB_FIELD_TO_NAME(ctx.type));
378                 return -1;
379         }
380         return 0;
381 } /* analyze_value */
383 static int
384 analyze_typed(context_t ctx, sdb_ast_typed_t *t, sdb_strbuf_t *errbuf)
386         context_t child_ctx = ctx;
387         bool needs_iter = 0;
388         bool valid = 1;
390         if ((t->expr->type != SDB_AST_TYPE_VALUE)
391                         && (t->expr->type != SDB_AST_TYPE_TYPED)) {
392                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s",
393                                 SDB_STORE_TYPE_TO_NAME(t->type),
394                                 SDB_AST_TYPE_TO_STRING(t->expr));
395                 return -1;
396         }
397         if ((t->type != SDB_ATTRIBUTE) && (! VALID_OBJ_TYPE(t->type))) {
398                 sdb_strbuf_sprintf(errbuf, "Invalid expression %#x.%s",
399                                 t->type, SDB_AST_TYPE_TO_STRING(t->expr));
400                 return -1;
401         }
403         if (ctx.type > 0) {
404                 if ((ctx.type == t->type)
405                                 || ((t->type == SDB_HOST) && (ctx.type != SDB_ATTRIBUTE))) {
406                         /* self-references and references to the parent host are always fine */
407                 }
408                 else if (t->type == SDB_ATTRIBUTE) {
409                         /* references to attributes are always fine */
410                         needs_iter = 1;
411                 }
412                 else if ((ctx.type == SDB_HOST)
413                                 && ((t->type == SDB_SERVICE) || (t->type == SDB_METRIC))) {
414                         /* only hosts may reference services and metrics */
415                         needs_iter = 1;
416                 }
417                 else {
418                         valid = 0;
419                 }
420         }
421         else if (ctx.type == FILTER_CONTEXT) {
422                 if (t->type == SDB_ATTRIBUTE) {
423                         /* all objects have attributes */
424                         needs_iter = 1;
425                 }
426                 else if ((t->type == SDB_SERVICE) || (t->type == SDB_METRIC)) {
427                         /* these will be iterators for *some* operations;
428                          * better forbid this altogether */
429                         valid = 0;
430                 }
431         }
433         if (needs_iter) {
434                 if (! ctx.iter)
435                         valid = 0;
436                 else
437                         child_ctx.iter = 0;
438         } /* else: push ctx.iter down to the child node */
440         if (! valid) {
441                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s in %s context",
442                                 SDB_STORE_TYPE_TO_NAME(t->type),
443                                 SDB_AST_TYPE_TO_STRING(t->expr),
444                                 SDB_STORE_TYPE_TO_NAME(ctx.type));
445                 return -1;
446         }
448         child_ctx.type = t->type;
449         if (analyze_node(child_ctx, t->expr, errbuf))
450                 return -1;
451         SDB_AST_NODE(t)->data_type = t->expr->data_type;
453         if (needs_iter && (SDB_AST_NODE(t)->data_type > 0)) {
454                 if (SDB_AST_NODE(t)->data_type & SDB_TYPE_ARRAY) {
455                         sdb_strbuf_sprintf(errbuf, "Cannot access array inside iterator");
456                         return -1;
457                 }
458                 /* Tell the caller that we're accessing an iterator. */
459                 SDB_AST_NODE(t)->data_type |= SDB_TYPE_ARRAY;
460         }
461         return 0;
462 } /* analyze_typed */
464 static int
465 analyze_node(context_t ctx, sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
467         if (! node) {
468                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
469                 return -1;
470         }
472         /* unknown by default */
473         node->data_type = -1;
475         if ((node->type == SDB_AST_TYPE_OPERATOR)
476                         && (SDB_AST_IS_LOGICAL(node)))
477                 return analyze_logical(ctx, SDB_AST_OP(node), errbuf);
478         else if ((node->type == SDB_AST_TYPE_OPERATOR)
479                         && (SDB_AST_IS_ARITHMETIC(node)))
480                 return analyze_arith(ctx, SDB_AST_OP(node), errbuf);
481         else if (node->type == SDB_AST_TYPE_ITERATOR)
482                 return analyze_iter(ctx, SDB_AST_ITER(node), errbuf);
483         else if (node->type == SDB_AST_TYPE_CONST)
484                 return analyze_const(ctx, SDB_AST_CONST(node), errbuf);
485         else if (node->type == SDB_AST_TYPE_VALUE)
486                 return analyze_value(ctx, SDB_AST_VALUE(node), errbuf);
487         else if (node->type == SDB_AST_TYPE_TYPED)
488                 return analyze_typed(ctx, SDB_AST_TYPED(node), errbuf);
490         sdb_strbuf_sprintf(errbuf, "Invalid expression node "
491                         "of type %#x", node->type);
492         return -1;
493 } /* analyze_node */
495 /*
496  * top level / command nodes
497  */
499 static int
500 analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf)
502         parent_child_t pc = {
503                 fetch->obj_type, fetch->hostname,
504                 fetch->parent_type, fetch->parent, fetch->name,
505         };
507         if (analyze_parent_child("FETCH", &pc, errbuf))
508                 return -1;
510         if (fetch->filter)
511                 return analyze_node(FILTER_CTX, fetch->filter, errbuf);
512         return 0;
513 } /* analyze_fetch */
515 static int
516 analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf)
518         if (! VALID_OBJ_TYPE(list->obj_type)) {
519                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
520                                 "in LIST command", list->obj_type);
521                 return -1;
522         }
523         if (list->filter)
524                 return analyze_node(FILTER_CTX, list->filter, errbuf);
525         return 0;
526 } /* analyze_list */
528 static int
529 analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf)
531         if (! VALID_OBJ_TYPE(lookup->obj_type)) {
532                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
533                                 "in LOOKUP command", lookup->obj_type);
534                 return -1;
535         }
536         if (lookup->matcher) {
537                 context_t ctx = { lookup->obj_type, 0 };
538                 if (analyze_node(ctx, lookup->matcher, errbuf))
539                         return -1;
540         }
541         if (lookup->filter)
542                 return analyze_node(FILTER_CTX, lookup->filter, errbuf);
543         return 0;
544 } /* analyze_lookup */
546 static int
547 analyze_store(sdb_ast_store_t *st, sdb_strbuf_t *errbuf)
549         parent_child_t pc = {
550                 st->obj_type, st->hostname,
551                 st->parent_type, st->parent, st->name,
552         };
554         if (analyze_parent_child("STORE", &pc, errbuf))
555                 return -1;
557         if (st->obj_type == SDB_METRIC) {
558                 if ((! st->store_type) != (! st->store_id)) {
559                         sdb_strbuf_sprintf(errbuf, "Incomplete metric store %s %s "
560                                         "in STORE METRIC command",
561                                         st->store_type ? st->store_type : "<unknown>",
562                                         st->store_id ? st->store_id : "<unknown>");
563                         return -1;
564                 }
565         }
566         else if (st->store_type || st->store_id) {
567                 sdb_strbuf_sprintf(errbuf, "Unexpected metric store %s %s "
568                                 "in STORE %s command",
569                                 st->store_type ? st->store_type : "<unknown>",
570                                 st->store_id ? st->store_id : "<unknown>",
571                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
572                 return -1;
573         }
575         if ((! (st->obj_type == SDB_ATTRIBUTE))
576                         && (st->value.type != SDB_TYPE_NULL)) {
577                 char v_str[sdb_data_format(&st->value, NULL, 0, SDB_DOUBLE_QUOTED) + 1];
578                 sdb_data_format(&st->value, v_str, sizeof(v_str), SDB_DOUBLE_QUOTED);
579                 sdb_strbuf_sprintf(errbuf, "Unexpected value %s in STORE %s command",
580                                 v_str, SDB_STORE_TYPE_TO_NAME(st->obj_type));
581                 return -1;
582         }
583         return 0;
584 } /* analyze_store */
586 static int
587 analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf)
589         if (! ts->hostname) {
590                 sdb_strbuf_sprintf(errbuf, "Missing hostname in TIMESERIES command");
591                 return -1;
592         }
593         if (! ts->metric) {
594                 sdb_strbuf_sprintf(errbuf, "Missing metric name in TIMESERIES command");
595                 return -1;
596         }
597         if (ts->end <= ts->start) {
598                 char start_str[64], end_str[64];
599                 sdb_strftime(start_str, sizeof(start_str), ts->start);
600                 sdb_strftime(end_str, sizeof(end_str), ts->end);
601                 sdb_strbuf_sprintf(errbuf, "Start time (%s) greater than "
602                                 "end time (%s) in TIMESERIES command", start_str, end_str);
603                 return -1;
604         }
605         return 0;
606 } /* analyze_timeseries */
608 /*
609  * public API
610  */
612 int
613 sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
615         if (! node) {
616                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
617                 return -1;
618         }
620         /* top-level nodes don't have a type */
621         node->data_type = -1;
623         if (node->type == SDB_AST_TYPE_FETCH)
624                 return analyze_fetch(SDB_AST_FETCH(node), errbuf);
625         else if (node->type == SDB_AST_TYPE_LIST)
626                 return analyze_list(SDB_AST_LIST(node), errbuf);
627         else if (node->type == SDB_AST_TYPE_LOOKUP)
628                 return analyze_lookup(SDB_AST_LOOKUP(node), errbuf);
629         else if (node->type == SDB_AST_TYPE_STORE)
630                 return analyze_store(SDB_AST_STORE(node), errbuf);
631         else if (node->type == SDB_AST_TYPE_TIMESERIES)
632                 return analyze_timeseries(SDB_AST_TIMESERIES(node), errbuf);
634         sdb_strbuf_sprintf(errbuf, "Invalid top-level AST node "
635                         "of type %#x", node->type);
636         return -1;
637 } /* sdb_parser_analyze */
639 int
640 sdb_parser_analyze_conditional(int context,
641                 sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
643         context_t ctx = { context, 0 };
644         if (! VALID_OBJ_TYPE(context)) {
645                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
646                 return -1;
647         }
648         if (! node) {
649                 sdb_strbuf_sprintf(errbuf, "Empty conditional node");
650                 return -1;
651         }
652         if (! SDB_AST_IS_LOGICAL(node)) {
653                 sdb_strbuf_sprintf(errbuf, "Not a conditional node (got %s)",
654                                 SDB_AST_TYPE_TO_STRING(node));
655                 return -1;
656         }
657         return analyze_node(ctx, node, errbuf);
658 } /* sdb_parser_analyze_conditional */
660 int
661 sdb_parser_analyze_arith(int context,
662                 sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
664         context_t ctx = { context, 0 };
665         if (! VALID_OBJ_TYPE(context)) {
666                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x", context);
667                 return -1;
668         }
669         if (! node) {
670                 sdb_strbuf_sprintf(errbuf, "Empty arithmetic node");
671                 return -1;
672         }
673         if (! SDB_AST_IS_ARITHMETIC(node)) {
674                 sdb_strbuf_sprintf(errbuf, "Not an arithmetic node (got %s)",
675                                 SDB_AST_TYPE_TO_STRING(node));
676                 return -1;
677         }
678         return analyze_node(ctx, node, errbuf);
679 } /* sdb_parser_analyze_arith */
681 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */