Code

parser: Convert AST op types to data op types when necessary.
[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>
37 #define VALID_OBJ_TYPE(t) ((SDB_HOST <= (t)) && ((t) <= SDB_METRIC))
39 static int
40 analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf);
42 /*
43  * expression nodes
44  */
46 static int
47 analyze_logical(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
48 {
49         switch (op->kind) {
50         case SDB_AST_OR:
51         case SDB_AST_AND:
52                 if (analyze_node(context, op->left, errbuf))
53                         return -1;
54                 /* fallthrough */
55         case SDB_AST_NOT:
56                 if (analyze_node(context, op->right, errbuf))
57                         return -1;
58                 break;
60         case SDB_AST_LT:
61         case SDB_AST_LE:
62         case SDB_AST_EQ:
63         case SDB_AST_NE:
64         case SDB_AST_GE:
65         case SDB_AST_GT:
66         {
67                 if (analyze_node(context, op->left, errbuf))
68                         return -1;
69                 if (analyze_node(context, op->right, errbuf))
70                         return -1;
71                 break;
72         }
74         case SDB_AST_REGEX:
75         case SDB_AST_NREGEX:
76                 if (analyze_node(context, op->left, errbuf))
77                         return -1;
78                 if (analyze_node(context, op->right, errbuf))
79                         return -1;
80                 break;
82         case SDB_AST_ISNULL:
83                 if (analyze_node(context, op->right, errbuf))
84                         return -1;
85                 break;
87         case SDB_AST_IN:
88                 if (analyze_node(context, op->left, errbuf))
89                         return -1;
90                 if (analyze_node(context, op->right, errbuf))
91                         return -1;
92                 break;
94         default:
95                 sdb_strbuf_sprintf(errbuf, "Unknown matcher type %d", op->kind);
96                 return -1;
97         }
98         return 0;
99 } /* analyze_logical */
101 static int
102 analyze_arith(int context, sdb_ast_op_t *op, sdb_strbuf_t *errbuf)
104         if (analyze_node(context, op->left, errbuf))
105                 return -1;
106         if (analyze_node(context, op->right, errbuf))
107                 return -1;
108         SDB_AST_NODE(op)->data_type = sdb_data_expr_type(SDB_AST_OP_TO_DATA_OP(op->kind),
109                         op->left->data_type, op->right->data_type);
111         /* TODO: replace constant arithmetic operations with a constant value */
112         return 0;
113 } /* analyze_arith */
115 static int
116 analyze_iter(int context, sdb_ast_iter_t *iter, sdb_strbuf_t *errbuf)
118         sdb_ast_const_t c = SDB_AST_CONST_INIT;
119         int status;
121         if (analyze_node(context, iter->iter, errbuf))
122                 return -1;
123         c.super.data_type = iter->iter->data_type;
125         /* TODO: support other setups as well */
126         assert((iter->expr->type == SDB_AST_TYPE_OPERATOR)
127                         && (! SDB_AST_OP(iter->expr)->left));
128         SDB_AST_OP(iter->expr)->left = SDB_AST_NODE(&c);
129         status = analyze_node(context, iter->expr, errbuf);
130         SDB_AST_OP(iter->expr)->left = NULL;
131         if (status)
132                 return -1;
133         return 0;
134 } /* analyze_iter */
136 static int
137 analyze_const(int __attribute__((unused)) context, sdb_ast_const_t *c,
138                 sdb_strbuf_t __attribute__((unused)) *errbuf)
140         SDB_AST_NODE(c)->data_type = c->value.type;
141         return 0;
142 } /* analyze_const */
144 static int
145 analyze_value(int __attribute__((unused)) context, sdb_ast_value_t *v,
146                 sdb_strbuf_t __attribute__((unused)) *errbuf)
148         if (v->type != SDB_ATTRIBUTE)
149                 SDB_AST_NODE(v)->data_type = SDB_FIELD_TYPE(v->type);
150         return 0;
151 } /* analyze_value */
153 static int
154 analyze_typed(int __attribute__((unused)) context, sdb_ast_typed_t *t,
155                 sdb_strbuf_t *errbuf)
157         if (analyze_node(t->type, t->expr, errbuf))
158                 return -1;
159         SDB_AST_NODE(t)->data_type = t->expr->data_type;
160         return 0;
161 } /* analyze_typed */
163 static int
164 analyze_node(int context, sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
166         if (! node) {
167                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
168                 return -1;
169         }
171         /* unknown by default */
172         node->data_type = -1;
174         if ((node->type == SDB_AST_TYPE_OPERATOR)
175                         && (SDB_AST_IS_LOGICAL(node)))
176                 return analyze_logical(context, SDB_AST_OP(node), errbuf);
177         else if ((node->type == SDB_AST_TYPE_OPERATOR)
178                         && (SDB_AST_IS_ARITHMETIC(node)))
179                 return analyze_arith(context, SDB_AST_OP(node), errbuf);
180         else if (node->type == SDB_AST_TYPE_ITERATOR)
181                 return analyze_iter(context, SDB_AST_ITER(node), errbuf);
182         else if (node->type == SDB_AST_TYPE_CONST)
183                 return analyze_const(context, SDB_AST_CONST(node), errbuf);
184         else if (node->type == SDB_AST_TYPE_VALUE)
185                 return analyze_value(context, SDB_AST_VALUE(node), errbuf);
186         else if (node->type == SDB_AST_TYPE_TYPED)
187                 return analyze_typed(context, SDB_AST_TYPED(node), errbuf);
189         sdb_strbuf_sprintf(errbuf, "Invalid expression node "
190                         "of type %#x", node->type);
191         return -1;
192 } /* analyze_node */
194 /*
195  * top level / command nodes
196  */
198 static int
199 analyze_fetch(sdb_ast_fetch_t *fetch, sdb_strbuf_t *errbuf)
201         if (! VALID_OBJ_TYPE(fetch->obj_type)) {
202                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
203                                 "in FETCH command", fetch->obj_type);
204                 return -1;
205         }
206         if (! fetch->name) {
207                 sdb_strbuf_sprintf(errbuf, "Missing object name in "
208                                 "FETCH %s command", SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
209                 return -1;
210         }
212         if ((fetch->obj_type == SDB_HOST) && fetch->hostname) {
213                 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
214                                 "in FETCH HOST command", fetch->hostname);
215                 return -1;
216         }
217         else if ((fetch->obj_type != SDB_HOST) && (! fetch->hostname)) {
218                 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
219                                 "in FETCH %s command", fetch->name,
220                                 SDB_STORE_TYPE_TO_NAME(fetch->obj_type));
221                 return -1;
222         }
224         if (fetch->filter)
225                 return analyze_node(-1, fetch->filter, errbuf);
226         return 0;
227 } /* analyze_fetch */
229 static int
230 analyze_list(sdb_ast_list_t *list, sdb_strbuf_t *errbuf)
232         if (! VALID_OBJ_TYPE(list->obj_type)) {
233                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
234                                 "in LIST command", list->obj_type);
235                 return -1;
236         }
237         if (list->filter)
238                 return analyze_node(-1, list->filter, errbuf);
239         return 0;
240 } /* analyze_list */
242 static int
243 analyze_lookup(sdb_ast_lookup_t *lookup, sdb_strbuf_t *errbuf)
245         if (! VALID_OBJ_TYPE(lookup->obj_type)) {
246                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
247                                 "in LOOKUP command", lookup->obj_type);
248                 return -1;
249         }
250         if (lookup->matcher)
251                 if (analyze_node(lookup->obj_type, lookup->matcher, errbuf))
252                         return -1;
253         if (lookup->filter)
254                 return analyze_node(-1, lookup->filter, errbuf);
255         return 0;
256 } /* analyze_lookup */
258 static int
259 analyze_store(sdb_ast_store_t *st, sdb_strbuf_t *errbuf)
261         if ((st->obj_type != SDB_ATTRIBUTE)
262                         && (! VALID_OBJ_TYPE(st->obj_type))) {
263                 sdb_strbuf_sprintf(errbuf, "Invalid object type %#x "
264                                 "in STORE command", st->obj_type);
265                 return -1;
266         }
267         if (! st->name) {
268                 sdb_strbuf_sprintf(errbuf, "Missing object name in "
269                                 "STORE %s command", SDB_STORE_TYPE_TO_NAME(st->obj_type));
270                 return -1;
271         }
273         if ((st->obj_type == SDB_HOST) && st->hostname) {
274                 sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
275                                 "in STORE HOST command", st->hostname);
276                 return -1;
277         }
278         else if ((st->obj_type != SDB_HOST) && (! st->hostname)) {
279                 sdb_strbuf_sprintf(errbuf, "Missing parent hostname for '%s' "
280                                 "in STORE %s command", st->name,
281                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
282                 return -1;
283         }
285         if (st->obj_type == SDB_ATTRIBUTE) {
286                 if ((st->parent_type <= 0) && st->parent) {
287                         sdb_strbuf_sprintf(errbuf, "Unexpected parent hostname '%s' "
288                                         "in STORE %s command", st->parent,
289                                         SDB_STORE_TYPE_TO_NAME(st->obj_type));
290                         return -1;
291                 }
292                 else if (st->parent_type > 0) {
293                         if (! VALID_OBJ_TYPE(st->parent_type)) {
294                                 sdb_strbuf_sprintf(errbuf, "Invalid parent type %#x "
295                                                 "in STORE %s command", st->parent_type,
296                                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
297                                 return -1;
298                         }
299                         if (! st->parent) {
300                                 sdb_strbuf_sprintf(errbuf, "Missing %s parent name "
301                                                 "in STORE %s command",
302                                                 SDB_STORE_TYPE_TO_NAME(st->parent_type),
303                                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
304                                 return -1;
305                         }
306                 }
307         }
308         else if ((st->parent_type > 0) || st->parent) {
309                 sdb_strbuf_sprintf(errbuf, "Unexpected %s parent name '%s' "
310                                 "in STORE %s command",
311                                 SDB_STORE_TYPE_TO_NAME(st->parent_type),
312                                 st->parent ? st->parent : "<unknown>",
313                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
314                 return -1;
315         }
317         if (st->obj_type == SDB_METRIC) {
318                 if ((! st->store_type) != (! st->store_id)) {
319                         sdb_strbuf_sprintf(errbuf, "Incomplete metric store %s %s "
320                                         "in STORE METRIC command",
321                                         st->store_type ? st->store_type : "<unknown>",
322                                         st->store_id ? st->store_id : "<unknown>");
323                         return -1;
324                 }
325         }
326         else if (st->store_type || st->store_id) {
327                 sdb_strbuf_sprintf(errbuf, "Unexpected metric store %s %s "
328                                 "in STORE %s command",
329                                 st->store_type ? st->store_type : "<unknown>",
330                                 st->store_id ? st->store_id : "<unknown>",
331                                 SDB_STORE_TYPE_TO_NAME(st->obj_type));
332                 return -1;
333         }
335         if ((! (st->obj_type == SDB_ATTRIBUTE))
336                         && (st->value.type != SDB_TYPE_NULL)) {
337                 char v_str[sdb_data_format(&st->value, NULL, 0, SDB_DOUBLE_QUOTED) + 1];
338                 sdb_data_format(&st->value, v_str, sizeof(v_str), SDB_DOUBLE_QUOTED);
339                 sdb_strbuf_sprintf(errbuf, "Unexpected value %s in STORE %s command",
340                                 v_str, SDB_STORE_TYPE_TO_NAME(st->obj_type));
341                 return -1;
342         }
343         return 0;
344 } /* analyze_store */
346 static int
347 analyze_timeseries(sdb_ast_timeseries_t *ts, sdb_strbuf_t *errbuf)
349         if (! ts->hostname) {
350                 sdb_strbuf_sprintf(errbuf, "Missing hostname in STORE command");
351                 return -1;
352         }
353         if (! ts->metric) {
354                 sdb_strbuf_sprintf(errbuf, "Missing metric name in STORE command");
355                 return -1;
356         }
357         if (ts->end <= ts->start) {
358                 char start_str[64], end_str[64];
359                 sdb_strftime(start_str, sizeof(start_str), "%F %T Tz", ts->start);
360                 sdb_strftime(end_str, sizeof(end_str), "%F %T Tz", ts->end);
361                 sdb_strbuf_sprintf(errbuf, "Start time (%s) greater than "
362                                 "end time (%s) in STORE command", start_str, end_str);
363                 return -1;
364         }
365         return 0;
366 } /* analyze_timeseries */
368 /*
369  * public API
370  */
372 int
373 sdb_parser_analyze(sdb_ast_node_t *node, sdb_strbuf_t *errbuf)
375         if (! node) {
376                 sdb_strbuf_sprintf(errbuf, "Empty AST node");
377                 return -1;
378         }
380         /* top-level nodes don't have a type */
381         node->data_type = -1;
383         if (node->type == SDB_AST_TYPE_FETCH)
384                 return analyze_fetch(SDB_AST_FETCH(node), errbuf);
385         else if (node->type == SDB_AST_TYPE_LIST)
386                 return analyze_list(SDB_AST_LIST(node), errbuf);
387         else if (node->type == SDB_AST_TYPE_LOOKUP)
388                 return analyze_lookup(SDB_AST_LOOKUP(node), errbuf);
389         else if (node->type == SDB_AST_TYPE_STORE)
390                 return analyze_store(SDB_AST_STORE(node), errbuf);
391         else if (node->type == SDB_AST_TYPE_TIMESERIES)
392                 return analyze_timeseries(SDB_AST_TIMESERIES(node), errbuf);
394         sdb_strbuf_sprintf(errbuf, "Invalid top-level AST node "
395                         "of type %#x", node->type);
396         return -1;
397 } /* sdb_parser_analyze */
399 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */