Code

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