1 /*
2 * SysDB - src/frontend/grammar.y
3 * Copyright (C) 2013 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 %{
30 #include "frontend/connection-private.h"
31 #include "frontend/parser.h"
32 #include "frontend/grammar.h"
34 #include "core/store.h"
35 #include "core/store-private.h"
36 #include "core/time.h"
38 #include "utils/error.h"
39 #include "utils/llist.h"
41 #include <assert.h>
43 #include <stdio.h>
44 #include <string.h>
46 /*
47 * private helper functions
48 */
50 static sdb_store_matcher_t *
51 name_iter_matcher(int m_type, int type, const char *cmp,
52 sdb_store_expr_t *expr);
54 /*
55 * public API
56 */
58 int
59 sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
61 sdb_fe_yyextra_t *
62 sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
64 void
65 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
67 /* quick access to the current parse tree */
68 #define pt sdb_fe_yyget_extra(scanner)->parsetree
70 /* quick access to the parser mode */
71 #define parser_mode sdb_fe_yyget_extra(scanner)->mode
73 #define MODE_TO_STRING(m) \
74 (((m) == SDB_PARSE_DEFAULT) ? "statement" \
75 : ((m) == SDB_PARSE_COND) ? "condition" \
76 : ((m) == SDB_PARSE_EXPR) ? "expression" \
77 : "UNKNOWN")
79 %}
81 %pure-parser
82 %lex-param {sdb_fe_yyscan_t scanner}
83 %parse-param {sdb_fe_yyscan_t scanner}
84 %locations
85 %error-verbose
86 %expect 0
87 %name-prefix "sdb_fe_yy"
89 %union {
90 const char *sstr; /* static string */
91 char *str;
92 int integer;
94 sdb_data_t data;
95 sdb_time_t datetime;
97 sdb_llist_t *list;
98 sdb_conn_node_t *node;
100 sdb_store_matcher_t *m;
101 sdb_store_expr_t *expr;
102 }
104 %start statements
106 %token SCANNER_ERROR
108 %token AND OR IS NOT MATCHING FILTER
109 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
110 %token CMP_LT CMP_LE CMP_GE CMP_GT ALL ANY IN
111 %token CONCAT
113 %token HOST_T HOSTS_T SERVICE_T SERVICES_T METRIC_T METRICS_T
114 %token ATTRIBUTE_T ATTRIBUTES_T
115 %token NAME_T LAST_UPDATE_T AGE_T INTERVAL_T BACKEND_T
117 %token START END
119 /* NULL token */
120 %token NULL_T
122 %token FETCH LIST LOOKUP TIMESERIES
124 %token <str> IDENTIFIER STRING
126 %token <data> INTEGER FLOAT
128 %token <datetime> DATE TIME
130 /* Precedence (lowest first): */
131 %left OR
132 %left AND
133 %right NOT
134 %left CMP_EQUAL CMP_NEQUAL
135 %left CMP_LT CMP_LE CMP_GE CMP_GT
136 %nonassoc CMP_REGEX CMP_NREGEX
137 %nonassoc IN
138 %left CONCAT
139 %nonassoc IS
140 %left '+' '-'
141 %left '*' '/' '%'
142 %left '[' ']'
143 %left '(' ')'
144 %left '.'
146 %type <list> statements
147 %type <node> statement
148 fetch_statement
149 list_statement
150 lookup_statement
151 timeseries_statement
152 matching_clause
153 filter_clause
154 condition
156 %type <m> matcher
157 compare_matcher
159 %type <expr> expression
161 %type <integer> object_type_plural
162 %type <integer> iterable
163 %type <integer> field
165 %type <sstr> cmp
167 %type <data> data
168 interval interval_elem
170 %type <datetime> datetime
171 start_clause end_clause
173 %destructor { free($$); } <str>
174 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
175 %destructor { sdb_data_free_datum(&$$); } <data>
177 %%
179 statements:
180 statements ';' statement
181 {
182 /* only accepted in default parse mode */
183 if (parser_mode != SDB_PARSE_DEFAULT) {
184 char errmsg[1024];
185 snprintf(errmsg, sizeof(errmsg),
186 YY_("syntax error, unexpected statement, "
187 "expecting %s"), MODE_TO_STRING(parser_mode));
188 sdb_fe_yyerror(&yylloc, scanner, errmsg);
189 sdb_object_deref(SDB_OBJ($3));
190 YYABORT;
191 }
193 if ($3) {
194 sdb_llist_append(pt, SDB_OBJ($3));
195 sdb_object_deref(SDB_OBJ($3));
196 }
197 }
198 |
199 statement
200 {
201 /* only accepted in default parse mode */
202 if (parser_mode != SDB_PARSE_DEFAULT) {
203 char errmsg[1024];
204 snprintf(errmsg, sizeof(errmsg),
205 YY_("syntax error, unexpected statement, "
206 "expecting %s"), MODE_TO_STRING(parser_mode));
207 sdb_fe_yyerror(&yylloc, scanner, errmsg);
208 sdb_object_deref(SDB_OBJ($1));
209 YYABORT;
210 }
212 if ($1) {
213 sdb_llist_append(pt, SDB_OBJ($1));
214 sdb_object_deref(SDB_OBJ($1));
215 }
216 }
217 |
218 condition
219 {
220 /* only accepted in condition parse mode */
221 if (! (parser_mode & SDB_PARSE_COND)) {
222 char errmsg[1024];
223 snprintf(errmsg, sizeof(errmsg),
224 YY_("syntax error, unexpected condition, "
225 "expecting %s"), MODE_TO_STRING(parser_mode));
226 sdb_fe_yyerror(&yylloc, scanner, errmsg);
227 sdb_object_deref(SDB_OBJ($1));
228 YYABORT;
229 }
231 if ($1) {
232 sdb_llist_append(pt, SDB_OBJ($1));
233 sdb_object_deref(SDB_OBJ($1));
234 }
235 }
236 |
237 expression
238 {
239 /* only accepted in expression parse mode */
240 if (! (parser_mode & SDB_PARSE_EXPR)) {
241 char errmsg[1024];
242 snprintf(errmsg, sizeof(errmsg),
243 YY_("syntax error, unexpected expression, "
244 "expecting %s"), MODE_TO_STRING(parser_mode));
245 sdb_fe_yyerror(&yylloc, scanner, errmsg);
246 sdb_object_deref(SDB_OBJ($1));
247 YYABORT;
248 }
250 if ($1) {
251 sdb_conn_node_t *n;
252 n = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
253 conn_expr_t, conn_expr_destroy));
254 n->cmd = CONNECTION_EXPR;
255 CONN_EXPR(n)->expr = $1;
257 sdb_llist_append(pt, SDB_OBJ(n));
258 sdb_object_deref(SDB_OBJ(n));
259 }
260 }
261 ;
263 statement:
264 fetch_statement
265 |
266 list_statement
267 |
268 lookup_statement
269 |
270 timeseries_statement
271 |
272 /* empty */
273 {
274 $$ = NULL;
275 }
276 ;
278 /*
279 * FETCH <type> <hostname> [FILTER <condition>];
280 *
281 * Retrieve detailed information about a single host.
282 */
283 fetch_statement:
284 FETCH HOST_T STRING filter_clause
285 {
286 /* TODO: support other types as well */
288 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
289 conn_fetch_t, conn_fetch_destroy));
290 CONN_FETCH($$)->type = SDB_HOST;
291 CONN_FETCH($$)->name = $3;
292 CONN_FETCH($$)->filter = CONN_MATCHER($4);
293 $$->cmd = CONNECTION_FETCH;
294 }
295 ;
297 /*
298 * LIST <type> [FILTER <condition>];
299 *
300 * Returns a list of all hosts in the store.
301 */
302 list_statement:
303 LIST object_type_plural filter_clause
304 {
305 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
306 conn_list_t, conn_list_destroy));
307 CONN_LIST($$)->type = $2;
308 CONN_LIST($$)->filter = CONN_MATCHER($3);
309 $$->cmd = CONNECTION_LIST;
310 }
311 ;
313 /*
314 * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
315 *
316 * Returns detailed information about <type> matching condition.
317 */
318 lookup_statement:
319 LOOKUP HOSTS_T matching_clause filter_clause
320 {
321 /* TODO: support other types as well */
323 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
324 conn_lookup_t, conn_lookup_destroy));
325 CONN_LOOKUP($$)->type = SDB_HOST;
326 CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
327 CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
328 $$->cmd = CONNECTION_LOOKUP;
329 }
330 ;
332 matching_clause:
333 MATCHING condition { $$ = $2; }
334 |
335 /* empty */ { $$ = NULL; }
337 filter_clause:
338 FILTER condition { $$ = $2; }
339 |
340 /* empty */ { $$ = NULL; }
342 /*
343 * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
344 *
345 * Returns a time-series for the specified host's metric.
346 */
347 timeseries_statement:
348 TIMESERIES STRING '.' STRING start_clause end_clause
349 {
350 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
351 conn_ts_t, conn_ts_destroy));
352 CONN_TS($$)->hostname = $2;
353 CONN_TS($$)->metric = $4;
354 CONN_TS($$)->opts.start = $5;
355 CONN_TS($$)->opts.end = $6;
356 $$->cmd = CONNECTION_TIMESERIES;
357 }
358 ;
360 start_clause:
361 START datetime { $$ = $2; }
362 |
363 /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
365 end_clause:
366 END datetime { $$ = $2; }
367 |
368 /* empty */ { $$ = sdb_gettime(); }
370 /*
371 * Basic expressions.
372 */
374 condition:
375 matcher
376 {
377 if (! $1) {
378 /* TODO: improve error reporting */
379 sdb_fe_yyerror(&yylloc, scanner,
380 YY_("syntax error, invalid condition"));
381 YYABORT;
382 }
384 $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
385 conn_matcher_t, conn_matcher_destroy));
386 $$->cmd = CONNECTION_MATCHER;
387 CONN_MATCHER($$)->matcher = $1;
388 }
389 ;
391 matcher:
392 '(' matcher ')'
393 {
394 $$ = $2;
395 }
396 |
397 matcher AND matcher
398 {
399 $$ = sdb_store_con_matcher($1, $3);
400 sdb_object_deref(SDB_OBJ($1));
401 sdb_object_deref(SDB_OBJ($3));
402 }
403 |
404 matcher OR matcher
405 {
406 $$ = sdb_store_dis_matcher($1, $3);
407 sdb_object_deref(SDB_OBJ($1));
408 sdb_object_deref(SDB_OBJ($3));
409 }
410 |
411 NOT matcher
412 {
413 $$ = sdb_store_inv_matcher($2);
414 sdb_object_deref(SDB_OBJ($2));
415 }
416 |
417 compare_matcher
418 {
419 $$ = $1;
420 }
421 ;
423 compare_matcher:
424 expression cmp expression
425 {
426 sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
427 assert(cb); /* else, the grammar accepts invalid 'cmp' */
428 $$ = cb($1, $3);
429 sdb_object_deref(SDB_OBJ($1));
430 sdb_object_deref(SDB_OBJ($3));
431 }
432 |
433 ANY iterable cmp expression
434 {
435 $$ = name_iter_matcher(MATCHER_ANY, $2, $3, $4);
436 sdb_object_deref(SDB_OBJ($4));
437 }
438 |
439 ALL iterable cmp expression
440 {
441 $$ = name_iter_matcher(MATCHER_ALL, $2, $3, $4);
442 sdb_object_deref(SDB_OBJ($4));
443 }
444 |
445 expression IS NULL_T
446 {
447 $$ = sdb_store_isnull_matcher($1);
448 sdb_object_deref(SDB_OBJ($1));
449 }
450 |
451 expression IS NOT NULL_T
452 {
453 $$ = sdb_store_isnnull_matcher($1);
454 sdb_object_deref(SDB_OBJ($1));
455 }
456 |
457 expression IN expression
458 {
459 $$ = sdb_store_in_matcher($1, $3);
460 sdb_object_deref(SDB_OBJ($1));
461 sdb_object_deref(SDB_OBJ($3));
462 }
463 ;
465 expression:
466 '(' expression ')'
467 {
468 $$ = $2;
469 }
470 |
471 expression '+' expression
472 {
473 $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
474 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
475 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
476 }
477 |
478 expression '-' expression
479 {
480 $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
481 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
482 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
483 }
484 |
485 expression '*' expression
486 {
487 $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
488 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
489 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
490 }
491 |
492 expression '/' expression
493 {
494 $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
495 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
496 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
497 }
498 |
499 expression '%' expression
500 {
501 $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
502 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
503 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
504 }
505 |
506 expression CONCAT expression
507 {
508 $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
509 sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
510 sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
511 }
512 |
513 HOST_T
514 {
515 /* TODO: this only works as long as queries
516 * are limited to hosts */
517 $$ = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
518 }
519 |
520 field
521 {
522 $$ = sdb_store_expr_fieldvalue($1);
523 }
524 |
525 ATTRIBUTE_T '[' STRING ']'
526 {
527 $$ = sdb_store_expr_attrvalue($3);
528 free($3); $3 = NULL;
529 }
530 |
531 data
532 {
533 $$ = sdb_store_expr_constvalue(&$1);
534 sdb_data_free_datum(&$1);
535 }
536 ;
538 object_type_plural:
539 HOSTS_T { $$ = SDB_HOST; }
540 |
541 SERVICES_T { $$ = SDB_SERVICE; }
542 |
543 METRICS_T { $$ = SDB_METRIC; }
544 ;
546 iterable:
547 SERVICE_T { $$ = SDB_SERVICE; }
548 |
549 METRIC_T { $$ = SDB_METRIC; }
550 |
551 ATTRIBUTE_T { $$ = SDB_ATTRIBUTE; }
552 ;
554 field:
555 NAME_T { $$ = SDB_FIELD_NAME; }
556 |
557 LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
558 |
559 AGE_T { $$ = SDB_FIELD_AGE; }
560 |
561 INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
562 |
563 BACKEND_T { $$ = SDB_FIELD_BACKEND; }
564 ;
566 cmp:
567 CMP_EQUAL { $$ = "="; }
568 |
569 CMP_NEQUAL { $$ = "!="; }
570 |
571 CMP_REGEX { $$ = "=~"; }
572 |
573 CMP_NREGEX { $$ = "!~"; }
574 |
575 CMP_LT { $$ = "<"; }
576 |
577 CMP_LE { $$ = "<="; }
578 |
579 CMP_GE { $$ = ">="; }
580 |
581 CMP_GT { $$ = ">"; }
582 ;
584 data:
585 STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
586 |
587 INTEGER { $$ = $1; }
588 |
589 FLOAT { $$ = $1; }
590 |
591 datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
592 |
593 interval { $$ = $1; }
594 ;
596 datetime:
597 DATE TIME { $$ = $1 + $2; }
598 |
599 DATE { $$ = $1; }
600 |
601 TIME { $$ = $1; }
602 ;
604 interval:
605 interval interval_elem
606 {
607 $$.data.datetime = $1.data.datetime + $2.data.datetime;
608 }
609 |
610 interval_elem { $$ = $1; }
611 ;
613 interval_elem:
614 INTEGER IDENTIFIER
615 {
616 sdb_time_t unit = 1;
618 unit = sdb_strpunit($2);
619 if (! unit) {
620 char errmsg[strlen($2) + 32];
621 snprintf(errmsg, sizeof(errmsg),
622 YY_("invalid time unit %s"), $2);
623 sdb_fe_yyerror(&yylloc, scanner, errmsg);
624 free($2); $2 = NULL;
625 YYABORT;
626 }
627 free($2); $2 = NULL;
629 $$.type = SDB_TYPE_DATETIME;
630 $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
632 if ($1.data.integer < 0) {
633 sdb_fe_yyerror(&yylloc, scanner,
634 YY_("syntax error, negative intervals not supported"));
635 YYABORT;
636 }
637 }
638 ;
640 %%
642 void
643 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
644 {
645 sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
646 } /* sdb_fe_yyerror */
648 static sdb_store_matcher_t *
649 name_iter_matcher(int m_type, int type, const char *cmp,
650 sdb_store_expr_t *expr)
651 {
652 sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
653 sdb_store_expr_t *e;
654 sdb_store_matcher_t *m, *tmp = NULL;
655 assert(cb);
657 /* TODO: this only works as long as queries
658 * are limited to hosts */
659 if (type == SDB_HOST) {
660 return NULL;
661 }
663 e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
664 m = cb(e, expr);
665 if (m_type == MATCHER_ANY)
666 tmp = sdb_store_any_matcher(type, m);
667 else if (m_type == MATCHER_ALL)
668 tmp = sdb_store_all_matcher(type, m);
669 sdb_object_deref(SDB_OBJ(m));
670 sdb_object_deref(SDB_OBJ(e));
671 return tmp;
672 } /* name_iter_matcher */
674 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */