Code

4702562e992ebcb7ae9058360a4e94c17f3205e7
[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_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)
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)
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 : */