Code

dea83d7e0c828d8966fbffe9e91a2895c466fdf7
[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>
38 #define VALID_OBJ_TYPE(t) ((SDB_HOST <= (t)) && ((t) <= SDB_METRIC))
40 #define FILTER_CONTEXT -1
41 #define UNSPEC_CONTEXT -2
43 static int
44 analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
46 /*
47  * error reporting
48  */
50 static void
51 op_error(sdb_strbuf_t *errbuf, sdb_ast_op_t *op, const char *reason)
52 {
53         sdb_strbuf_sprintf(errbuf, "Invalid operation %s %s %s (%s)",
54                         SDB_TYPE_TO_STRING(op->left->data_type),
55                         SDB_AST_OP_TO_STRING(op->kind),
56                         SDB_TYPE_TO_STRING(op->right->data_type),
57                         reason);
58 } /* op_error */
60 static void
61 __attribute__((format(printf, 3, 4)))
62 iter_error(sdb_strbuf_t *errbuf, sdb_ast_iter_t *iter, const char *reason, ...)
63 {
64         char r[1024];
65         va_list ap;
67         va_start(ap, reason);
68         vsnprintf(r, sizeof(r), reason, ap);
69         va_end(ap);
71         assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
72                         && (! SDB_AST_OP(iter->expr)->left));
73         sdb_strbuf_sprintf(errbuf, "Invalid iterator %s %s %s %s (%s)",
74                         SDB_AST_OP_TO_STRING(iter->kind),
75                         SDB_TYPE_TO_STRING(iter->iter->data_type),
76                         SDB_AST_OP_TO_STRING(SDB_AST_OP(iter->expr)->kind),
77                         SDB_TYPE_TO_STRING(SDB_AST_OP(iter->expr)->right->data_type),
78                         r);
79 } /* iter_error */
81 /*
82  * expression nodes
83  */
85 static int
86 analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
87 {
88         switch (op->kind) {
89         case SDB_AST_OR:
90         case SDB_AST_AND:
91                 if (! SDB_AST_IS_LOGICAL(op->left)) {
92                         sdb_strbuf_sprintf(errbuf, "Invalid left operand (%s) "
93                                         "in %s expression", SDB_AST_TYPE_TO_STRING(op->left),
94                                         SDB_AST_OP_TO_STRING(op->kind));
95                         return -1;
96                 }
97                 if (analyze_node(context, op->left, errbuf))
98                         return -1;
99                 /* fallthrough */
100         case SDB_AST_NOT:
101                 if (! SDB_AST_IS_LOGICAL(op->right)) {
102                         sdb_strbuf_sprintf(errbuf, "Invalid right operand (%s) "
103                                         "in %s expression", SDB_AST_TYPE_TO_STRING(op->right),
104                                         SDB_AST_OP_TO_STRING(op->kind));
105                         return -1;
106                 }
107                 if (analyze_node(context, op->right, errbuf))
108                         return -1;
109                 break;
111         case SDB_AST_LT:
112         case SDB_AST_LE:
113         case SDB_AST_EQ:
114         case SDB_AST_NE:
115         case SDB_AST_GE:
116         case SDB_AST_GT:
117         {
118                 if (analyze_node(context, op->left, errbuf))
119                         return -1;
120                 if (analyze_node(context, op->right, errbuf))
121                         return -1;
123                 if ((op->left->data_type > 0) && (op->right->data_type > 0)) {
124                         if (op->left->data_type == op->right->data_type)
125                                 return 0;
126                         op_error(errbuf, op, "type mismatch");
127                         return -1;
128                 }
129                 if ((op->left->data_type > 0) && (op->left->data_type & SDB_TYPE_ARRAY)) {
130                         op_error(errbuf, op, "array not allowed");
131                         return -1;
132                 }
133                 if ((op->right->data_type > 0) && (op->right->data_type & SDB_TYPE_ARRAY)) {
134                         op_error(errbuf, op, "array not allowed");
135                         return -1;
136                 }
137                 break;
138         }
140         case SDB_AST_REGEX:
141         case SDB_AST_NREGEX:
142                 if (analyze_node(context, op->left, errbuf))
143                         return -1;
144                 if (analyze_node(context, op->right, errbuf))
145                         return -1;
147                 /* all types are supported for the left operand
148                  * TODO: introduce a cast operator if it's not a string */
149                 if ((op->right->data_type > 0)
150                                 && (op->right->data_type != SDB_TYPE_REGEX)
151                                 && (op->right->data_type != SDB_TYPE_STRING)) {
152                         op_error(errbuf, op, "invalid regex");
153                         return -1;
154                 }
155                 break;
157         case SDB_AST_ISNULL:
158         case SDB_AST_ISTRUE:
159         case SDB_AST_ISFALSE:
160                 if (analyze_node(context, op->right, errbuf))
161                         return -1;
162                 break;
164         case SDB_AST_IN:
165                 if (analyze_node(context, op->left, errbuf))
166                         return -1;
167                 if (analyze_node(context, op->right, errbuf))
168                         return -1;
170                 if ((op->right->data_type > 0) && (! (op->right->data_type & SDB_TYPE_ARRAY))) {
171                         op_error(errbuf, op, "array expected");
172                         return -1;
173                 }
174                 /* the left operand may be a scalar or an array but the element
175                  * type has to match */
176                 if ((op->left->data_type > 0) && (op->right->data_type > 0)
177                                 && ((op->left->data_type & 0xff) != (op->right->data_type & 0xff))) {
178                         op_error(errbuf, op, "type mismatch");
179                         return -1;
180                 }
181                 break;
183         default:
184                 sdb_strbuf_sprintf(errbuf, "Unknown operand type %d", op->kind);
185                 return -1;
186         }
187         return 0;
188 } /* analyze_logical */
190 static int
191 analyze_arith(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
193         if (analyze_node(context, op->left, errbuf))
194                 return -1;
195         if (analyze_node(context, op->right, errbuf))
196                 return -1;
197         SDB_AST_NODE(op)->data_type = sdb_data_expr_type(SDB_AST_OP_TO_DATA_OP(op->kind),
198                         op->left->data_type, op->right->data_type);
200         if ((op->left->data_type > 0) && (op->right->data_type > 0)
201                         && (SDB_AST_NODE(op)->data_type <= 0)) {
202                 op_error(errbuf, op, "type mismatch");
203                 return -1;
204         }
206         /* TODO: replace constant arithmetic operations with a constant value */
207         return 0;
208 } /* analyze_arith */
210 static int
211 analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
213         sdb_ast_const_t c = SDB_AST_CONST_INIT;
214         int iter_context = context;
215         int status;
217         if (iter->iter->type == SDB_AST_TYPE_TYPED)
218                 iter_context = SDB_AST_TYPED(iter->iter)->type;
220         if (analyze_node(iter_context, iter->iter, errbuf))
221                 return -1;
222         /* TODO: support other setups as well */
223         assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
224                         && (! SDB_AST_OP(iter->expr)->left));
225         /* determine the data-type for better error messages */
226         analyze_node(iter_context, SDB_AST_OP(iter->expr)->right, NULL);
228         if (iter->iter->type == SDB_AST_TYPE_TYPED) {
229                 int iter_type = SDB_AST_TYPED(iter->iter)->type;
231                 c.value.type = iter->iter->data_type;
233                 if (iter_type == SDB_ATTRIBUTE) {
234                         /* attributes are always iterable */
235                 }
236                 else if ((context != SDB_HOST) && (context != SDB_SERVICE)
237                                 && (context != SDB_METRIC) && (context != UNSPEC_CONTEXT)) {
238                         iter_error(errbuf, iter, "%s not iterable in %s context",
239                                         SDB_STORE_TYPE_TO_NAME(iter_type),
240                                         SDB_STORE_TYPE_TO_NAME(context));
241                         return -1;
242                 }
244                 if ((context == iter_type)
245                                 || ((iter_type != SDB_SERVICE)
246                                         && (iter_type != SDB_METRIC)
247                                         && (iter_type != SDB_ATTRIBUTE))
248                                 || ((context == SDB_SERVICE)
249                                         && (iter_type == SDB_METRIC))
250                                 || ((context == SDB_METRIC)
251                                         && (iter_type == SDB_SERVICE))) {
252                         iter_error(errbuf, iter, "%s not iterable in %s context",
253                                         SDB_STORE_TYPE_TO_NAME(iter_type),
254                                         SDB_STORE_TYPE_TO_NAME(context));
255                         return -1;
256                 }
257         }
258         else if (iter->iter->type == SDB_AST_TYPE_VALUE) {
259                 int iter_type = SDB_AST_VALUE(iter->iter)->type;
261                 c.value.type = iter->iter->data_type & 0xff;
263                 if (iter_type != SDB_FIELD_BACKEND) {
264                         iter_error(errbuf, iter, "%s not iterable in %s context",
265                                         (iter_type == SDB_ATTRIBUTE)
266                                                 ? "attribute"
267                                                 : SDB_FIELD_TO_NAME(iter_type),
268                                         SDB_STORE_TYPE_TO_NAME(context));
269                         return -1;
270                 }
271         }
272         else if (iter->iter->type == SDB_AST_TYPE_CONST) {
273                 c.value.type = iter->iter->data_type & 0xff;
275                 if (! (SDB_AST_CONST(iter->iter)->value.type & SDB_TYPE_ARRAY)) {
276                         iter_error(errbuf, iter, "%s not iterable",
277                                         SDB_TYPE_TO_STRING(SDB_AST_CONST(iter->iter)->value.type));
278                         return -1;
279                 }
280         }
281         else {
282                 /* TODO: if we know the data-type of iter->iter and it's an array,
283                  * we should support an iterator for it as well */
284                 iter_error(errbuf, iter, "%s expression not iterable",
285                                 SDB_AST_TYPE_TO_STRING(iter->iter));
286                 return -1;
287         }
289         SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
290         status = analyze_node(context, iter->expr, errbuf);
291         SDB_AST_OP(iter->expr)->left = NULL;
292         if (status)
293                 return -1;
294         return 0;
295 } /* analyze_iter */
297 static int
298 analyze_const(int __attribute__((unused)) context, sdb_ast_const_t *c,
299                 sdb_strbuf_t __attribute__((unused)) *errbuf)
301         SDB_AST_NODE(c)->data_type = c->value.type;
302         return 0;
303 } /* analyze_const */
305 static int
306 analyze_value(int context, sdb_ast_value_t *v, sdb_strbuf_t *errbuf)
308         if (v->type != SDB_ATTRIBUTE)
309                 SDB_AST_NODE(v)->data_type = SDB_FIELD_TYPE(v->type);
311         if ((v->type != SDB_ATTRIBUTE) && v->name) {
312                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s[%s]",
313                                 SDB_FIELD_TO_NAME(v->type), v->name);
314                 return -1;
315         }
316         else if ((v->type == SDB_ATTRIBUTE) && (! v->name)) {
317                 sdb_strbuf_sprintf(errbuf, "Invalid expression attribute[] "
318                                 "(missing name)");
319                 return -1;
320         }
322         if (context != UNSPEC_CONTEXT) {
323                 /* skip these checks if we don't know the context; it's up to the
324                  * caller to check again once the right context information is
325                  * available */
326                 if ((context != SDB_ATTRIBUTE) && (v->type == SDB_FIELD_VALUE)) {
327                         sdb_strbuf_sprintf(errbuf, "Invalid expression %s.value",
328                                         SDB_FIELD_TO_NAME(context));
329                         return -1;
330                 }
331                 if ((context != SDB_METRIC) && (v->type == SDB_FIELD_TIMESERIES)) {
332                         sdb_strbuf_sprintf(errbuf, "Invalid expression %s.timeseries",
333                                         SDB_FIELD_TO_NAME(context));
334                         return -1;
335                 }
336         }
337         return 0;
338 } /* analyze_value */
340 static int
341 analyze_typed(int context, sdb_ast_typed_t *t, sdb_strbuf_t *errbuf)
343         if ((t->expr->type != SDB_AST_TYPE_VALUE)
344                         && (t->expr->type != SDB_AST_TYPE_TYPED)) {
345                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s",
346                                 SDB_STORE_TYPE_TO_NAME(t->type),
347                                 SDB_AST_TYPE_TO_STRING(t->expr));
348                 return -1;
349         }
350         if (analyze_node(t->type, t->expr, errbuf))
351                 return -1;
352         SDB_AST_NODE(t)->data_type = t->expr->data_type;
354         if ((t->type != SDB_ATTRIBUTE) && (! VALID_OBJ_TYPE(t->type))) {
355                 sdb_strbuf_sprintf(errbuf, "Invalid expression %#x.%s",
356                                 t->type, SDB_AST_TYPE_TO_STRING(t->expr));
357                 return -1;
358         }
360         /* self-references are allowed and services and metrics may reference
361          * their parent host; everything may reference attributes */
362         if ((context != t->type) && (context > 0)
363                         && (((context != SDB_SERVICE) && (context != SDB_METRIC))
364                                 || (t->type != SDB_HOST))
365                         && (t->type != SDB_ATTRIBUTE)) {
366                 sdb_strbuf_sprintf(errbuf, "Invalid expression %s.%s in %s context",
367                                 SDB_STORE_TYPE_TO_NAME(t->type),
368                                 SDB_AST_TYPE_TO_STRING(t->expr),
369                                 context == -1 ? "generic" : SDB_STORE_TYPE_TO_NAME(context));
370                 return -1;
371         }
372         return 0;
373 } /* analyze_typed */
375 static int
376 analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
378         if (! node) {
379                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
380                 return -1;
381         }
383         /* unknown by default */
384         node->data_type = -1;
386         if ((node->type == SDB_AST_TYPE_OPERATOR)
387                         && (SDB_AST_IS_LOGICAL(node)))
388                 return analyze_logical(context, SDB_AST_OP(node), errbuf);
389         else if ((node->type == SDB_AST_TYPE_OPERATOR)
390                         && (SDB_AST_IS_ARITHMETIC(node)))
391                 return analyze_arith(context, SDB_AST_OP(node), errbuf);
392         else if (node->type == SDB_AST_TYPE_ITERATOR)
393                 return analyze_iter(context, SDB_AST_ITER(node), errbuf);
394         else if (node->type == SDB_AST_TYPE_CONST)
395                 return analyze_const(context, SDB_AST_CONST(node), errbuf);
396         else if (node->type == SDB_AST_TYPE_VALUE)
397                 return analyze_value(context, SDB_AST_VALUE(node), errbuf);
398         else if (node->type == SDB_AST_TYPE_TYPED)
399                 return analyze_typed(context, SDB_AST_TYPED(node), errbuf);
401         sdb_strbuf_sprintf(errbuf, "Invalid expression node "
402                         "of type %#x", node->type);
403         return -1;
404 } /* analyze_node */
406 /*
407  * top level / command nodes
408  */
410 static int
411 analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf)
413         if (! VALID_OBJ_TYPE(fetch->obj_type)) {
414                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
415                                 "in FETCH command", fetch->obj_type);
416                 return -1;
417         }
418         if (! fetch->name) {
419                 sdb_strbuf_sprintf(errbuf, "Missing object name in "
420                                 "FETCH %s command", SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
421                 return -1;
422         }
424         if ((fetch->obj_type == SDB_HOST) && fetch->hostname) {
425                 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
426                                 "in FETCH HOST command", fetch->hostname);
427                 return -1;
428         }
429         else if ((fetch->obj_type != SDB_HOST) && (! fetch->hostname)) {
430                 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
431                                 "in FETCH %s command", fetch->name,
432                                 SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
433                 return -1;
434         }
436         if (fetch->filter)
437                 return analyze_node(FILTER_CONTEXT, fetch->filter, errbuf);
438         return 0;
439 } /* analyze_fetch */
441 static int
442 analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf)
444         if (! VALID_OBJ_TYPE(list->obj_type)) {
445                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
446                                 "in LIST command", list->obj_type);
447                 return -1;
448         }
449         if (list->filter)
450                 return analyze_node(FILTER_CONTEXT, list->filter, errbuf);
451         return 0;
452 } /* analyze_list */
454 static int
455 analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf)
457         if (! VALID_OBJ_TYPE(lookup->obj_type)) {
458                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
459                                 "in LOOKUP command", lookup->obj_type);
460                 return -1;
461         }
462         if (lookup->matcher)
463                 if (analyze_node(lookup->obj_type, lookup->matcher, errbuf))
464                         return -1;
465         if (lookup->filter)
466                 return analyze_node(FILTER_CONTEXT, lookup->filter, errbuf);
467         return 0;
468 } /* analyze_lookup */
470 static int
471 analyze_store(sdb_ast_store_t *st, sdb_strbuf_t *errbuf)
473         if ((st->obj_type != SDB_ATTRIBUTE)
474                         && (! VALID_OBJ_TYPE(st->obj_type))) {
475                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
476                                 "in STORE command", st->obj_type);
477                 return -1;
478         }
479         if (! st->name) {
480                 sdb_strbuf_sprintf(errbuf, "Missing object name in "
481                                 "STORE %s command", SDB_STORE_TYPE_TO_NAME(st->obj_type));
482                 return -1;
483         }
485         if ((st->obj_type == SDB_HOST) && st->hostname) {
486                 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
487                                 "in STORE HOST command", st->hostname);
488                 return -1;
489         }
490         else if ((st->obj_type != SDB_HOST) && (! st->hostname)) {
491                 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
492                                 "in STORE %s command", st->name,
493                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
494                 return -1;
495         }
497         if (st->obj_type == SDB_ATTRIBUTE) {
498                 if ((st->parent_type <= 0) && st->parent) {
499                         sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
500                                         "in STORE %s command", st->parent,
501                                         SDB_STORE_TYPE_TO_NAME(st->obj_type));
502                         return -1;
503                 }
504                 else if (st->parent_type > 0) {
505                         if (! VALID_OBJ_TYPE(st->parent_type)) {
506                                 sdb_strbuf_sprintf(errbuf, "Invalid parent type %#x "
507                                                 "in STORE %s command", st->parent_type,
508                                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
509                                 return -1;
510                         }
511                         if (! st->parent) {
512                                 sdb_strbuf_sprintf(errbuf, "Missing %s parent name "
513                                                 "in STORE %s command",
514                                                 SDB_STORE_TYPE_TO_NAME(st->parent_type),
515                                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
516                                 return -1;
517                         }
518                 }
519         }
520         else if ((st->parent_type > 0) || st->parent) {
521                 sdb_strbuf_sprintf(errbuf, "Unexpected %s parent name '%s' "
522                                 "in STORE %s command",
523                                 SDB_STORE_TYPE_TO_NAME(st->parent_type),
524                                 st->parent ? st->parent : "<unknown>",
525                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
526                 return -1;
527         }
529         if (st->obj_type == SDB_METRIC) {
530                 if ((! st->store_type) != (! st->store_id)) {
531                         sdb_strbuf_sprintf(errbuf, "Incomplete metric store %s %s "
532                                         "in STORE METRIC command",
533                                         st->store_type ? st->store_type : "<unknown>",
534                                         st->store_id ? st->store_id : "<unknown>");
535                         return -1;
536                 }
537         }
538         else if (st->store_type || st->store_id) {
539                 sdb_strbuf_sprintf(errbuf, "Unexpected metric store %s %s "
540                                 "in STORE %s command",
541                                 st->store_type ? st->store_type : "<unknown>",
542                                 st->store_id ? st->store_id : "<unknown>",
543                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
544                 return -1;
545         }
547         if ((! (st->obj_type == SDB_ATTRIBUTE))
548                         && (st->value.type != SDB_TYPE_NULL)) {
549                 char v_str[sdb_data_format(&st->value, NULL, 0, SDB_DOUBLE_QUOTED) + 1];
550                 sdb_data_format(&st->value, v_str, sizeof(v_str), SDB_DOUBLE_QUOTED);
551                 sdb_strbuf_sprintf(errbuf, "Unexpected value %s in STORE %s command",
552                                 v_str, SDB_STORE_TYPE_TO_NAME(st->obj_type));
553                 return -1;
554         }
555         return 0;
556 } /* analyze_store */
558 static int
559 analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf)
561         if (! ts->hostname) {
562                 sdb_strbuf_sprintf(errbuf, "Missing hostname in STORE command");
563                 return -1;
564         }
565         if (! ts->metric) {
566                 sdb_strbuf_sprintf(errbuf, "Missing metric name in STORE command");
567                 return -1;
568         }
569         if (ts->end <= ts->start) {
570                 char start_str[64], end_str[64];
571                 sdb_strftime(start_str, sizeof(start_str), "%F %T Tz", ts->start);
572                 sdb_strftime(end_str, sizeof(end_str), "%F %T Tz", ts->end);
573                 sdb_strbuf_sprintf(errbuf, "Start time (%s) greater than "
574                                 "end time (%s) in STORE command", start_str, end_str);
575                 return -1;
576         }
577         return 0;
578 } /* analyze_timeseries */
580 /*
581  * public API
582  */
584 int
585 sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
587         if (! node) {
588                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
589                 return -1;
590         }
592         /* top-level nodes don't have a type */
593         node->data_type = -1;
595         if (node->type == SDB_AST_TYPE_FETCH)
596                 return analyze_fetch(SDB_AST_FETCH(node), errbuf);
597         else if (node->type == SDB_AST_TYPE_LIST)
598                 return analyze_list(SDB_AST_LIST(node), errbuf);
599         else if (node->type == SDB_AST_TYPE_LOOKUP)
600                 return analyze_lookup(SDB_AST_LOOKUP(node), errbuf);
601         else if (node->type == SDB_AST_TYPE_STORE)
602                 return analyze_store(SDB_AST_STORE(node), errbuf);
603         else if (node->type == SDB_AST_TYPE_TIMESERIES)
604                 return analyze_timeseries(SDB_AST_TIMESERIES(node), errbuf);
606         sdb_strbuf_sprintf(errbuf, "Invalid top-level AST node "
607                         "of type %#x", node->type);
608         return -1;
609 } /* sdb_parser_analyze */
611 int
612 sdb_parser_analyze_conditional(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
614         if (! node) {
615                 sdb_strbuf_sprintf(errbuf, "Empty conditional node");
616                 return -1;
617         }
618         if (! SDB_AST_IS_LOGICAL(node)) {
619                 sdb_strbuf_sprintf(errbuf, "Not a conditional node (got %s)",
620                                 SDB_AST_TYPE_TO_STRING(node));
621                 return -1;
622         }
623         return analyze_node(UNSPEC_CONTEXT, node, errbuf);
624 } /* sdb_parser_analyze_conditional */
626 int
627 sdb_parser_analyze_arith(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
629         if (! node) {
630                 sdb_strbuf_sprintf(errbuf, "Empty arithmetic node");
631                 return -1;
632         }
633         if (! SDB_AST_IS_ARITHMETIC(node)) {
634                 sdb_strbuf_sprintf(errbuf, "Not an arithmetic node (got %s)",
635                                 SDB_AST_TYPE_TO_STRING(node));
636                 return -1;
637         }
638         return analyze_node(UNSPEC_CONTEXT, node, errbuf);
639 } /* sdb_parser_analyze_arith */
641 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */