Code

analyzer: Support more flexible iterator expressions.
[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  * 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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */