Code

97be55d0c7dfa5b397fade235888bafe4f505a27
[sysdb.git] / src / frontend / grammar.y
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;
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 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 object_type STRING filter_clause
285                 {
286                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
287                                                 conn_fetch_t, conn_fetch_destroy));
288                         CONN_FETCH($$)->type = $2;
289                         CONN_FETCH($$)->host = $3;
290                         CONN_FETCH($$)->name = NULL;
291                         CONN_FETCH($$)->filter = CONN_MATCHER($4);
292                         $$->cmd = CONNECTION_FETCH;
293                 }
294         |
295         FETCH object_type STRING '.' STRING filter_clause
296                 {
297                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
298                                                 conn_fetch_t, conn_fetch_destroy));
299                         CONN_FETCH($$)->type = $2;
300                         CONN_FETCH($$)->host = $3;
301                         CONN_FETCH($$)->name = $5;
302                         CONN_FETCH($$)->filter = CONN_MATCHER($6);
303                         $$->cmd = CONNECTION_FETCH;
304                 }
305         ;
307 /*
308  * LIST <type> [FILTER <condition>];
309  *
310  * Returns a list of all hosts in the store.
311  */
312 list_statement:
313         LIST object_type_plural filter_clause
314                 {
315                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
316                                                 conn_list_t, conn_list_destroy));
317                         CONN_LIST($$)->type = $2;
318                         CONN_LIST($$)->filter = CONN_MATCHER($3);
319                         $$->cmd = CONNECTION_LIST;
320                 }
321         ;
323 /*
324  * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
325  *
326  * Returns detailed information about <type> matching condition.
327  */
328 lookup_statement:
329         LOOKUP HOSTS_T matching_clause filter_clause
330                 {
331                         /* TODO: support other types as well */
333                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
334                                                 conn_lookup_t, conn_lookup_destroy));
335                         CONN_LOOKUP($$)->type = SDB_HOST;
336                         CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
337                         CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
338                         $$->cmd = CONNECTION_LOOKUP;
339                 }
340         ;
342 matching_clause:
343         MATCHING condition { $$ = $2; }
344         |
345         /* empty */ { $$ = NULL; }
347 filter_clause:
348         FILTER condition { $$ = $2; }
349         |
350         /* empty */ { $$ = NULL; }
352 /*
353  * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
354  *
355  * Returns a time-series for the specified host's metric.
356  */
357 timeseries_statement:
358         TIMESERIES STRING '.' STRING start_clause end_clause
359                 {
360                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
361                                                 conn_ts_t, conn_ts_destroy));
362                         CONN_TS($$)->hostname = $2;
363                         CONN_TS($$)->metric = $4;
364                         CONN_TS($$)->opts.start = $5;
365                         CONN_TS($$)->opts.end = $6;
366                         $$->cmd = CONNECTION_TIMESERIES;
367                 }
368         ;
370 start_clause:
371         START datetime { $$ = $2; }
372         |
373         /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
375 end_clause:
376         END datetime { $$ = $2; }
377         |
378         /* empty */ { $$ = sdb_gettime(); }
380 /*
381  * Basic expressions.
382  */
384 condition:
385         matcher
386                 {
387                         if (! $1) {
388                                 /* TODO: improve error reporting */
389                                 sdb_fe_yyerror(&yylloc, scanner,
390                                                 YY_("syntax error, invalid condition"));
391                                 YYABORT;
392                         }
394                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
395                                                 conn_matcher_t, conn_matcher_destroy));
396                         $$->cmd = CONNECTION_MATCHER;
397                         CONN_MATCHER($$)->matcher = $1;
398                 }
399         ;
401 matcher:
402         '(' matcher ')'
403                 {
404                         $$ = $2;
405                 }
406         |
407         matcher AND matcher
408                 {
409                         $$ = sdb_store_con_matcher($1, $3);
410                         sdb_object_deref(SDB_OBJ($1));
411                         sdb_object_deref(SDB_OBJ($3));
412                 }
413         |
414         matcher OR matcher
415                 {
416                         $$ = sdb_store_dis_matcher($1, $3);
417                         sdb_object_deref(SDB_OBJ($1));
418                         sdb_object_deref(SDB_OBJ($3));
419                 }
420         |
421         NOT matcher
422                 {
423                         $$ = sdb_store_inv_matcher($2);
424                         sdb_object_deref(SDB_OBJ($2));
425                 }
426         |
427         compare_matcher
428                 {
429                         $$ = $1;
430                 }
431         ;
433 compare_matcher:
434         expression cmp expression
435                 {
436                         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
437                         assert(cb); /* else, the grammar accepts invalid 'cmp' */
438                         $$ = cb($1, $3);
439                         sdb_object_deref(SDB_OBJ($1));
440                         sdb_object_deref(SDB_OBJ($3));
441                 }
442         |
443         ANY iterable cmp expression
444                 {
445                         $$ = name_iter_matcher(MATCHER_ANY, $2, $3, $4);
446                         sdb_object_deref(SDB_OBJ($4));
447                 }
448         |
449         ALL iterable cmp expression
450                 {
451                         $$ = name_iter_matcher(MATCHER_ALL, $2, $3, $4);
452                         sdb_object_deref(SDB_OBJ($4));
453                 }
454         |
455         expression IS NULL_T
456                 {
457                         $$ = sdb_store_isnull_matcher($1);
458                         sdb_object_deref(SDB_OBJ($1));
459                 }
460         |
461         expression IS NOT NULL_T
462                 {
463                         $$ = sdb_store_isnnull_matcher($1);
464                         sdb_object_deref(SDB_OBJ($1));
465                 }
466         |
467         expression IN expression
468                 {
469                         $$ = sdb_store_in_matcher($1, $3);
470                         sdb_object_deref(SDB_OBJ($1));
471                         sdb_object_deref(SDB_OBJ($3));
472                 }
473         ;
475 expression:
476         '(' expression ')'
477                 {
478                         $$ = $2;
479                 }
480         |
481         expression '+' expression
482                 {
483                         $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
484                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
485                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
486                 }
487         |
488         expression '-' expression
489                 {
490                         $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
491                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
492                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
493                 }
494         |
495         expression '*' expression
496                 {
497                         $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
498                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
499                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
500                 }
501         |
502         expression '/' expression
503                 {
504                         $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
505                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
506                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
507                 }
508         |
509         expression '%' expression
510                 {
511                         $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
512                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
513                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
514                 }
515         |
516         expression CONCAT expression
517                 {
518                         $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
519                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
520                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
521                 }
522         |
523         HOST_T
524                 {
525                         /* TODO: this only works as long as queries
526                          * are limited to hosts */
527                         $$ = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
528                 }
529         |
530         field
531                 {
532                         $$ = sdb_store_expr_fieldvalue($1);
533                 }
534         |
535         ATTRIBUTE_T '[' STRING ']'
536                 {
537                         $$ = sdb_store_expr_attrvalue($3);
538                         free($3); $3 = NULL;
539                 }
540         |
541         data
542                 {
543                         $$ = sdb_store_expr_constvalue(&$1);
544                         sdb_data_free_datum(&$1);
545                 }
546         ;
548 object_type:
549         HOST_T { $$ = SDB_HOST; }
550         |
551         SERVICE_T { $$ = SDB_SERVICE; }
552         |
553         METRIC_T { $$ = SDB_METRIC; }
554         ;
556 object_type_plural:
557         HOSTS_T { $$ = SDB_HOST; }
558         |
559         SERVICES_T { $$ = SDB_SERVICE; }
560         |
561         METRICS_T { $$ = SDB_METRIC; }
562         ;
564 iterable:
565         SERVICE_T { $$ = SDB_SERVICE; }
566         |
567         METRIC_T { $$ = SDB_METRIC; }
568         |
569         ATTRIBUTE_T { $$ = SDB_ATTRIBUTE; }
570         ;
572 field:
573         NAME_T { $$ = SDB_FIELD_NAME; }
574         |
575         LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
576         |
577         AGE_T { $$ = SDB_FIELD_AGE; }
578         |
579         INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
580         |
581         BACKEND_T { $$ = SDB_FIELD_BACKEND; }
582         ;
584 cmp:
585         CMP_EQUAL { $$ = "="; }
586         |
587         CMP_NEQUAL { $$ = "!="; }
588         |
589         CMP_REGEX { $$ = "=~"; }
590         |
591         CMP_NREGEX { $$ = "!~"; }
592         |
593         CMP_LT { $$ = "<"; }
594         |
595         CMP_LE { $$ = "<="; }
596         |
597         CMP_GE { $$ = ">="; }
598         |
599         CMP_GT { $$ = ">"; }
600         ;
602 data:
603         STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
604         |
605         INTEGER { $$ = $1; }
606         |
607         FLOAT { $$ = $1; }
608         |
609         datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
610         |
611         interval { $$ = $1; }
612         ;
614 datetime:
615         DATE TIME { $$ = $1 + $2; }
616         |
617         DATE { $$ = $1; }
618         |
619         TIME { $$ = $1; }
620         ;
622 interval:
623         interval interval_elem
624                 {
625                         $$.data.datetime = $1.data.datetime + $2.data.datetime;
626                 }
627         |
628         interval_elem { $$ = $1; }
629         ;
631 interval_elem:
632         INTEGER IDENTIFIER
633                 {
634                         sdb_time_t unit = 1;
636                         unit = sdb_strpunit($2);
637                         if (! unit) {
638                                 char errmsg[strlen($2) + 32];
639                                 snprintf(errmsg, sizeof(errmsg),
640                                                 YY_("invalid time unit %s"), $2);
641                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
642                                 free($2); $2 = NULL;
643                                 YYABORT;
644                         }
645                         free($2); $2 = NULL;
647                         $$.type = SDB_TYPE_DATETIME;
648                         $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
650                         if ($1.data.integer < 0) {
651                                 sdb_fe_yyerror(&yylloc, scanner,
652                                                 YY_("syntax error, negative intervals not supported"));
653                                 YYABORT;
654                         }
655                 }
656         ;
658 %%
660 void
661 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
663         sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
664 } /* sdb_fe_yyerror */
666 static sdb_store_matcher_t *
667 name_iter_matcher(int m_type, int type, const char *cmp,
668                 sdb_store_expr_t *expr)
670         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
671         sdb_store_expr_t *e;
672         sdb_store_matcher_t *m, *tmp = NULL;
673         assert(cb);
675         /* TODO: this only works as long as queries
676          * are limited to hosts */
677         if (type == SDB_HOST) {
678                 return NULL;
679         }
681         e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
682         m = cb(e, expr);
683         if (m_type == MATCHER_ANY)
684                 tmp = sdb_store_any_matcher(type, m);
685         else if (m_type == MATCHER_ALL)
686                 tmp = sdb_store_all_matcher(type, m);
687         sdb_object_deref(SDB_OBJ(m));
688         sdb_object_deref(SDB_OBJ(e));
689         return tmp;
690 } /* name_iter_matcher */
692 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */