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)
103 {
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)
117 {
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)
139 {
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)
147 {
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)
156 {
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)
165 {
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)
200 {
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)
231 {
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)
244 {
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)
260 {
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)
348 {
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)
374 {
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 : */