Code

8057710c76851d4819a11e9d24ad91cc18562f88
[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_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr);
53 static sdb_store_matcher_t *
54 name_iter_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr);
56 /*
57  * public API
58  */
60 int
61 sdb_fe_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_fe_yyscan_t yyscanner);
63 sdb_fe_yyextra_t *
64 sdb_fe_yyget_extra(sdb_fe_yyscan_t scanner);
66 void
67 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg);
69 /* quick access to the current parse tree */
70 #define pt sdb_fe_yyget_extra(scanner)->parsetree
72 /* quick access to the parser mode */
73 #define parser_mode sdb_fe_yyget_extra(scanner)->mode
75 #define MODE_TO_STRING(m) \
76         (((m) == SDB_PARSE_DEFAULT) ? "statement" \
77                 : ((m) == SDB_PARSE_COND) ? "condition" \
78                 : ((m) == SDB_PARSE_EXPR) ? "expression" \
79                 : "UNKNOWN")
81 %}
83 %pure-parser
84 %lex-param {sdb_fe_yyscan_t scanner}
85 %parse-param {sdb_fe_yyscan_t scanner}
86 %locations
87 %error-verbose
88 %expect 0
89 %name-prefix "sdb_fe_yy"
91 %union {
92         const char *sstr; /* static string */
93         char *str;
95         sdb_data_t data;
96         sdb_time_t datetime;
98         sdb_llist_t     *list;
99         sdb_conn_node_t *node;
101         sdb_store_matcher_t *m;
102         sdb_store_expr_t *expr;
105 %start statements
107 %token SCANNER_ERROR
109 %token AND OR IS NOT MATCHING FILTER
110 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
111 %token CMP_LT CMP_LE CMP_GE CMP_GT ANY IN
112 %token CONCAT
114 %token START END
116 /* NULL token */
117 %token NULL_T
119 %token FETCH LIST LOOKUP TIMESERIES
121 %token <str> IDENTIFIER STRING
123 %token <data> INTEGER FLOAT
125 %token <datetime> DATE TIME
127 /* Precedence (lowest first): */
128 %left OR
129 %left AND
130 %right NOT
131 %left CMP_EQUAL CMP_NEQUAL
132 %left CMP_LT CMP_LE CMP_GE CMP_GT
133 %nonassoc CMP_REGEX CMP_NREGEX
134 %nonassoc IN
135 %left CONCAT
136 %nonassoc IS
137 %left '+' '-'
138 %left '*' '/' '%'
139 %left '[' ']'
140 %left '(' ')'
141 %left '.'
143 %type <list> statements
144 %type <node> statement
145         fetch_statement
146         list_statement
147         lookup_statement
148         timeseries_statement
149         matching_clause
150         filter_clause
151         condition
153 %type <m> matcher
154         compare_matcher
156 %type <expr> expression
158 %type <sstr> cmp
160 %type <data> data
161         interval interval_elem
163 %type <datetime> datetime
164         start_clause end_clause
166 %destructor { free($$); } <str>
167 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node> <m> <expr>
168 %destructor { sdb_data_free_datum(&$$); } <data>
170 %%
172 statements:
173         statements ';' statement
174                 {
175                         /* only accepted in default parse mode */
176                         if (parser_mode != SDB_PARSE_DEFAULT) {
177                                 char errmsg[1024];
178                                 snprintf(errmsg, sizeof(errmsg),
179                                                 YY_("syntax error, unexpected statement, "
180                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
181                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
182                                 sdb_object_deref(SDB_OBJ($3));
183                                 YYABORT;
184                         }
186                         if ($3) {
187                                 sdb_llist_append(pt, SDB_OBJ($3));
188                                 sdb_object_deref(SDB_OBJ($3));
189                         }
190                 }
191         |
192         statement
193                 {
194                         /* only accepted in default parse mode */
195                         if (parser_mode != SDB_PARSE_DEFAULT) {
196                                 char errmsg[1024];
197                                 snprintf(errmsg, sizeof(errmsg),
198                                                 YY_("syntax error, unexpected statement, "
199                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
200                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
201                                 sdb_object_deref(SDB_OBJ($1));
202                                 YYABORT;
203                         }
205                         if ($1) {
206                                 sdb_llist_append(pt, SDB_OBJ($1));
207                                 sdb_object_deref(SDB_OBJ($1));
208                         }
209                 }
210         |
211         condition
212                 {
213                         /* only accepted in condition parse mode */
214                         if (! (parser_mode & SDB_PARSE_COND)) {
215                                 char errmsg[1024];
216                                 snprintf(errmsg, sizeof(errmsg),
217                                                 YY_("syntax error, unexpected condition, "
218                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
219                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
220                                 sdb_object_deref(SDB_OBJ($1));
221                                 YYABORT;
222                         }
224                         if ($1) {
225                                 sdb_llist_append(pt, SDB_OBJ($1));
226                                 sdb_object_deref(SDB_OBJ($1));
227                         }
228                 }
229         |
230         expression
231                 {
232                         /* only accepted in expression parse mode */
233                         if (! (parser_mode & SDB_PARSE_EXPR)) {
234                                 char errmsg[1024];
235                                 snprintf(errmsg, sizeof(errmsg),
236                                                 YY_("syntax error, unexpected expression, "
237                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
238                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
239                                 sdb_object_deref(SDB_OBJ($1));
240                                 YYABORT;
241                         }
243                         if ($1) {
244                                 sdb_conn_node_t *n;
245                                 n = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
246                                                         conn_expr_t, conn_expr_destroy));
247                                 n->cmd = CONNECTION_EXPR;
248                                 CONN_EXPR(n)->expr = $1;
250                                 sdb_llist_append(pt, SDB_OBJ(n));
251                                 sdb_object_deref(SDB_OBJ(n));
252                         }
253                 }
254         ;
256 statement:
257         fetch_statement
258         |
259         list_statement
260         |
261         lookup_statement
262         |
263         timeseries_statement
264         |
265         /* empty */
266                 {
267                         $$ = NULL;
268                 }
269         ;
271 /*
272  * FETCH <type> <hostname> [FILTER <condition>];
273  *
274  * Retrieve detailed information about a single host.
275  */
276 fetch_statement:
277         FETCH IDENTIFIER STRING filter_clause
278                 {
279                         /* TODO: support other types as well */
280                         if (strcasecmp($2, "host")) {
281                                 char errmsg[strlen($2) + 32];
282                                 snprintf(errmsg, sizeof(errmsg),
283                                                 YY_("unknown data-source %s"), $2);
284                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
285                                 free($2); $2 = NULL;
286                                 free($3); $3 = NULL;
287                                 sdb_object_deref(SDB_OBJ($4));
288                                 YYABORT;
289                         }
291                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
292                                                 conn_fetch_t, conn_fetch_destroy));
293                         CONN_FETCH($$)->type = SDB_HOST;
294                         CONN_FETCH($$)->name = $3;
295                         CONN_FETCH($$)->filter = CONN_MATCHER($4);
296                         $$->cmd = CONNECTION_FETCH;
297                         free($2); $2 = NULL;
298                 }
299         ;
301 /*
302  * LIST <type> [FILTER <condition>];
303  *
304  * Returns a list of all hosts in the store.
305  */
306 list_statement:
307         LIST IDENTIFIER filter_clause
308                 {
309                         int type = sdb_store_parse_object_type_plural($2);
310                         if ((type < 0) || (type == SDB_ATTRIBUTE)) {
311                                 char errmsg[strlen($2) + 32];
312                                 snprintf(errmsg, sizeof(errmsg),
313                                                 YY_("unknown data-source %s"), $2);
314                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
315                                 free($2); $2 = NULL;
316                                 sdb_object_deref(SDB_OBJ($3));
317                                 YYABORT;
318                         }
320                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
321                                                 conn_list_t, conn_list_destroy));
322                         CONN_LIST($$)->type = type;
323                         CONN_LIST($$)->filter = CONN_MATCHER($3);
324                         $$->cmd = CONNECTION_LIST;
325                         free($2); $2 = NULL;
326                 }
327         ;
329 /*
330  * LOOKUP <type> MATCHING <condition> [FILTER <condition>];
331  *
332  * Returns detailed information about <type> matching condition.
333  */
334 lookup_statement:
335         LOOKUP IDENTIFIER matching_clause filter_clause
336                 {
337                         /* TODO: support other types as well */
338                         if (strcasecmp($2, "hosts")) {
339                                 char errmsg[strlen($2) + 32];
340                                 snprintf(errmsg, sizeof(errmsg),
341                                                 YY_("unknown data-source %s"), $2);
342                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
343                                 free($2); $2 = NULL;
344                                 sdb_object_deref(SDB_OBJ($3));
345                                 sdb_object_deref(SDB_OBJ($4));
346                                 YYABORT;
347                         }
349                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
350                                                 conn_lookup_t, conn_lookup_destroy));
351                         CONN_LOOKUP($$)->type = SDB_HOST;
352                         CONN_LOOKUP($$)->matcher = CONN_MATCHER($3);
353                         CONN_LOOKUP($$)->filter = CONN_MATCHER($4);
354                         $$->cmd = CONNECTION_LOOKUP;
355                         free($2); $2 = NULL;
356                 }
357         ;
359 matching_clause:
360         MATCHING condition { $$ = $2; }
361         |
362         /* empty */ { $$ = NULL; }
364 filter_clause:
365         FILTER condition { $$ = $2; }
366         |
367         /* empty */ { $$ = NULL; }
369 /*
370  * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
371  *
372  * Returns a time-series for the specified host's metric.
373  */
374 timeseries_statement:
375         TIMESERIES STRING '.' STRING start_clause end_clause
376                 {
377                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
378                                                 conn_ts_t, conn_ts_destroy));
379                         CONN_TS($$)->hostname = $2;
380                         CONN_TS($$)->metric = $4;
381                         CONN_TS($$)->opts.start = $5;
382                         CONN_TS($$)->opts.end = $6;
383                         $$->cmd = CONNECTION_TIMESERIES;
384                 }
385         ;
387 start_clause:
388         START datetime { $$ = $2; }
389         |
390         /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
392 end_clause:
393         END datetime { $$ = $2; }
394         |
395         /* empty */ { $$ = sdb_gettime(); }
397 /*
398  * Basic expressions.
399  */
401 condition:
402         matcher
403                 {
404                         if (! $1) {
405                                 /* TODO: improve error reporting */
406                                 sdb_fe_yyerror(&yylloc, scanner,
407                                                 YY_("syntax error, invalid condition"));
408                                 YYABORT;
409                         }
411                         $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
412                                                 conn_matcher_t, conn_matcher_destroy));
413                         $$->cmd = CONNECTION_MATCHER;
414                         CONN_MATCHER($$)->matcher = $1;
415                 }
416         ;
418 matcher:
419         '(' matcher ')'
420                 {
421                         $$ = $2;
422                 }
423         |
424         matcher AND matcher
425                 {
426                         $$ = sdb_store_con_matcher($1, $3);
427                         sdb_object_deref(SDB_OBJ($1));
428                         sdb_object_deref(SDB_OBJ($3));
429                 }
430         |
431         matcher OR matcher
432                 {
433                         $$ = sdb_store_dis_matcher($1, $3);
434                         sdb_object_deref(SDB_OBJ($1));
435                         sdb_object_deref(SDB_OBJ($3));
436                 }
437         |
438         NOT matcher
439                 {
440                         $$ = sdb_store_inv_matcher($2);
441                         sdb_object_deref(SDB_OBJ($2));
442                 }
443         |
444         compare_matcher
445                 {
446                         $$ = $1;
447                 }
448         ;
450 compare_matcher:
451         expression cmp expression
452                 {
453                         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op($2);
454                         assert(cb); /* else, the grammar accepts invalid 'cmp' */
455                         $$ = cb($1, $3);
456                         sdb_object_deref(SDB_OBJ($1));
457                         sdb_object_deref(SDB_OBJ($3));
458                 }
459         |
460         IDENTIFIER cmp expression
461                 {
462                         $$ = name_matcher($1, $2, $3);
463                         free($1); $1 = NULL;
464                         sdb_object_deref(SDB_OBJ($3));
465                 }
466         |
467         ANY IDENTIFIER cmp expression
468                 {
469                         $$ = name_iter_matcher($2, $3, $4);
470                         free($2); $2 = NULL;
471                         sdb_object_deref(SDB_OBJ($4));
472                 }
473         |
474         expression IS NULL_T
475                 {
476                         $$ = sdb_store_isnull_matcher($1);
477                         sdb_object_deref(SDB_OBJ($1));
478                 }
479         |
480         expression IS NOT NULL_T
481                 {
482                         $$ = sdb_store_isnnull_matcher($1);
483                         sdb_object_deref(SDB_OBJ($1));
484                 }
485         |
486         expression IN expression
487                 {
488                         $$ = sdb_store_in_matcher($1, $3);
489                         sdb_object_deref(SDB_OBJ($1));
490                         sdb_object_deref(SDB_OBJ($3));
491                 }
492         ;
494 expression:
495         '(' expression ')'
496                 {
497                         $$ = $2;
498                 }
499         |
500         expression '+' expression
501                 {
502                         $$ = sdb_store_expr_create(SDB_DATA_ADD, $1, $3);
503                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
504                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
505                 }
506         |
507         expression '-' expression
508                 {
509                         $$ = sdb_store_expr_create(SDB_DATA_SUB, $1, $3);
510                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
511                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
512                 }
513         |
514         expression '*' expression
515                 {
516                         $$ = sdb_store_expr_create(SDB_DATA_MUL, $1, $3);
517                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
518                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
519                 }
520         |
521         expression '/' expression
522                 {
523                         $$ = sdb_store_expr_create(SDB_DATA_DIV, $1, $3);
524                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
525                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
526                 }
527         |
528         expression '%' expression
529                 {
530                         $$ = sdb_store_expr_create(SDB_DATA_MOD, $1, $3);
531                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
532                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
533                 }
534         |
535         expression CONCAT expression
536                 {
537                         $$ = sdb_store_expr_create(SDB_DATA_CONCAT, $1, $3);
538                         sdb_object_deref(SDB_OBJ($1)); $1 = NULL;
539                         sdb_object_deref(SDB_OBJ($3)); $3 = NULL;
540                 }
541         |
542         '.' IDENTIFIER
543                 {
544                         int field = sdb_store_parse_field_name($2);
545                         free($2); $2 = NULL;
546                         $$ = sdb_store_expr_fieldvalue(field);
547                 }
548         |
549         IDENTIFIER '[' STRING ']'
550                 {
551                         if (strcasecmp($1, "attribute")) {
552                                 char errmsg[strlen($1) + strlen($3) + 32];
553                                 snprintf(errmsg, sizeof(errmsg),
554                                                 YY_("unknown value %s[%s]"), $1, $3);
555                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
556                                 free($1); $1 = NULL;
557                                 free($3); $3 = NULL;
558                                 YYABORT;
559                         }
560                         $$ = sdb_store_expr_attrvalue($3);
561                         free($1); $1 = NULL;
562                         free($3); $3 = NULL;
563                 }
564         |
565         data
566                 {
567                         $$ = sdb_store_expr_constvalue(&$1);
568                         sdb_data_free_datum(&$1);
569                 }
570         ;
572 cmp:
573         CMP_EQUAL { $$ = "="; }
574         |
575         CMP_NEQUAL { $$ = "!="; }
576         |
577         CMP_REGEX { $$ = "=~"; }
578         |
579         CMP_NREGEX { $$ = "!~"; }
580         |
581         CMP_LT { $$ = "<"; }
582         |
583         CMP_LE { $$ = "<="; }
584         |
585         CMP_GE { $$ = ">="; }
586         |
587         CMP_GT { $$ = ">"; }
588         ;
590 data:
591         STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
592         |
593         INTEGER { $$ = $1; }
594         |
595         FLOAT { $$ = $1; }
596         |
597         datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
598         |
599         interval { $$ = $1; }
600         ;
602 datetime:
603         DATE TIME { $$ = $1 + $2; }
604         |
605         DATE { $$ = $1; }
606         |
607         TIME { $$ = $1; }
608         ;
610 interval:
611         interval interval_elem
612                 {
613                         $$.data.datetime = $1.data.datetime + $2.data.datetime;
614                 }
615         |
616         interval_elem { $$ = $1; }
617         ;
619 interval_elem:
620         INTEGER IDENTIFIER
621                 {
622                         sdb_time_t unit = 1;
624                         unit = sdb_strpunit($2);
625                         if (! unit) {
626                                 char errmsg[strlen($2) + 32];
627                                 snprintf(errmsg, sizeof(errmsg),
628                                                 YY_("invalid time unit %s"), $2);
629                                 sdb_fe_yyerror(&yylloc, scanner, errmsg);
630                                 free($2); $2 = NULL;
631                                 YYABORT;
632                         }
633                         free($2); $2 = NULL;
635                         $$.type = SDB_TYPE_DATETIME;
636                         $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
638                         if ($1.data.integer < 0) {
639                                 sdb_fe_yyerror(&yylloc, scanner,
640                                                 YY_("syntax error, negative intervals not supported"));
641                                 YYABORT;
642                         }
643                 }
644         ;
646 %%
648 void
649 sdb_fe_yyerror(YYLTYPE *lval, sdb_fe_yyscan_t scanner, const char *msg)
651         sdb_log(SDB_LOG_ERR, "frontend: parse error: %s", msg);
652 } /* sdb_fe_yyerror */
654 static sdb_store_matcher_t *
655 name_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr)
657         int type = sdb_store_parse_object_type(type_name);
658         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
659         sdb_store_expr_t *e;
660         sdb_store_matcher_t *m;
661         assert(cb);
663         /* TODO: this only works as long as queries
664          * are limited to hosts */
665         if (type != SDB_HOST)
666                 return NULL;
668         e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
669         m = cb(e, expr);
670         sdb_object_deref(SDB_OBJ(e));
671         return m;
672 } /* name_matcher */
674 static sdb_store_matcher_t *
675 name_iter_matcher(char *type_name, char *cmp, sdb_store_expr_t *expr)
677         int type = sdb_store_parse_object_type(type_name);
678         sdb_store_matcher_op_cb cb = sdb_store_parse_matcher_op(cmp);
679         sdb_store_expr_t *e;
680         sdb_store_matcher_t *m, *tmp;
681         assert(cb);
683         /* TODO: this only works as long as queries
684          * are limited to hosts */
685         if (type == SDB_HOST) {
686                 return NULL;
687         }
689         e = sdb_store_expr_fieldvalue(SDB_FIELD_NAME);
690         m = cb(e, expr);
691         tmp = sdb_store_any_matcher(type, m);
692         sdb_object_deref(SDB_OBJ(m));
693         sdb_object_deref(SDB_OBJ(e));
694         return tmp;
695 } /* name_iter_matcher */
697 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */