Code

parser/grammar: Fix the hostname in a 'FETCH host' node.
[sysdb.git] / src / parser / grammar.y
1 /*
2  * SysDB - src/parser/grammar.y
3  * Copyright (C) 2013-2015 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 /*
29  * Grammar for the SysDB Query Language (SysQL).
30  */
32 %{
34 #include "core/store.h"
35 #include "core/time.h"
37 #include "parser/ast.h"
38 #include "parser/parser.h"
39 #include "parser/grammar.h"
41 #include "utils/error.h"
42 #include "utils/llist.h"
44 #include <assert.h>
46 #include <stdio.h>
47 #include <string.h>
49 /*
50  * public API
51  */
53 int
54 sdb_parser_yylex(YYSTYPE *yylval, YYLTYPE *yylloc, sdb_parser_yyscan_t yyscanner);
56 sdb_parser_yyextra_t *
57 sdb_parser_yyget_extra(sdb_parser_yyscan_t scanner);
59 void
60 sdb_parser_yyerror(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *msg);
61 void
62 sdb_parser_yyerrorf(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *fmt, ...);
64 /* quick access to the current parse tree */
65 #define pt sdb_parser_yyget_extra(scanner)->parsetree
67 /* quick access to the parser mode */
68 #define parser_mode sdb_parser_yyget_extra(scanner)->mode
70 /* quick access to the parser's error buffer */
71 #define errbuf sdb_parser_yyget_extra(scanner)->errbuf
73 #define CK_OOM(p) \
74         do { \
75                 if (! (p)) { \
76                         sdb_parser_yyerror(&yylloc, scanner, YY_("out of memory")); \
77                         YYABORT; \
78                 } \
79         } while (0)
81 #define MODE_TO_STRING(m) \
82         (((m) == SDB_PARSE_DEFAULT) ? "statement" \
83                 : ((m) == SDB_PARSE_COND) ? "condition" \
84                 : ((m) == SDB_PARSE_ARITH) ? "arithmetic expression" \
85                 : "UNKNOWN")
87 %}
89 %pure-parser
90 %lex-param {sdb_parser_yyscan_t scanner}
91 %parse-param {sdb_parser_yyscan_t scanner}
92 %locations
93 %error-verbose
94 %expect 0
95 %name-prefix "sdb_parser_yy"
97 %union {
98         char *str;
99         int integer;
101         sdb_data_t data;
102         sdb_time_t datetime;
104         sdb_llist_t    *list;
105         sdb_ast_node_t *node;
107         struct { char *type; char *id; } metric_store;
110 %start statements
112 %token SCANNER_ERROR
114 %token AND OR IS NOT MATCHING FILTER
115 %token CMP_EQUAL CMP_NEQUAL CMP_REGEX CMP_NREGEX
116 %token CMP_LT CMP_LE CMP_GE CMP_GT ALL ANY IN
117 %token CONCAT
119 %token HOST_T HOSTS_T SERVICE_T SERVICES_T METRIC_T METRICS_T
120 %token ATTRIBUTE_T ATTRIBUTES_T
121 %token NAME_T LAST_UPDATE_T AGE_T INTERVAL_T BACKEND_T VALUE_T
123 %token LAST UPDATE
125 %token START END
127 /* NULL token */
128 %token NULL_T
130 %token FETCH LIST LOOKUP STORE TIMESERIES
132 %token <str> IDENTIFIER STRING
134 %token <data> INTEGER FLOAT
136 %token <datetime> DATE TIME
138 /* Precedence (lowest first): */
139 %left OR
140 %left AND
141 %right NOT
142 %left CMP_EQUAL CMP_NEQUAL
143 %left CMP_LT CMP_LE CMP_GE CMP_GT
144 %nonassoc CMP_REGEX CMP_NREGEX
145 %nonassoc IN
146 %left CONCAT
147 %nonassoc IS
148 %left '+' '-'
149 %left '*' '/' '%'
150 %left '[' ']'
151 %left '(' ')'
152 %left '.'
154 %type <list> statements
155 %type <node> statement
156         fetch_statement
157         list_statement
158         lookup_statement
159         store_statement
160         timeseries_statement
161         matching_clause
162         filter_clause
163         condition comparison
164         expression object_expression
166 %type <integer> object_type object_type_plural
167 %type <integer> field
168 %type <integer> cmp
170 %type <data> data
171         interval interval_elem
172         array array_elem_list
174 %type <datetime> datetime
175         start_clause end_clause
176         last_update_clause
178 %type <metric_store> metric_store_clause
180 %destructor { free($$); } <str>
181 %destructor { sdb_object_deref(SDB_OBJ($$)); } <node>
182 %destructor { sdb_data_free_datum(&$$); } <data>
184 %%
186 statements:
187         statements ';' statement
188                 {
189                         /* only accepted in default parse mode */
190                         if (parser_mode != SDB_PARSE_DEFAULT) {
191                                 sdb_parser_yyerrorf(&yylloc, scanner,
192                                                 YY_("syntax error, unexpected statement, "
193                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
194                                 sdb_object_deref(SDB_OBJ($3));
195                                 YYABORT;
196                         }
198                         if ($3) {
199                                 sdb_llist_append(pt, SDB_OBJ($3));
200                                 sdb_object_deref(SDB_OBJ($3));
201                         }
202                 }
203         |
204         statement
205                 {
206                         /* only accepted in default parse mode */
207                         if (parser_mode != SDB_PARSE_DEFAULT) {
208                                 sdb_parser_yyerrorf(&yylloc, scanner,
209                                                 YY_("syntax error, unexpected statement, "
210                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
211                                 sdb_object_deref(SDB_OBJ($1));
212                                 YYABORT;
213                         }
215                         if ($1) {
216                                 sdb_llist_append(pt, SDB_OBJ($1));
217                                 sdb_object_deref(SDB_OBJ($1));
218                         }
219                 }
220         |
221         condition
222                 {
223                         /* only accepted in condition parse mode */
224                         if (! (parser_mode & SDB_PARSE_COND)) {
225                                 sdb_parser_yyerrorf(&yylloc, scanner,
226                                                 YY_("syntax error, unexpected condition, "
227                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
228                                 sdb_object_deref(SDB_OBJ($1));
229                                 YYABORT;
230                         }
232                         if ($1) {
233                                 sdb_llist_append(pt, SDB_OBJ($1));
234                                 sdb_object_deref(SDB_OBJ($1));
235                         }
236                 }
237         |
238         expression
239                 {
240                         /* only accepted in expression parse mode */
241                         if (! (parser_mode & SDB_PARSE_ARITH)) {
242                                 sdb_parser_yyerrorf(&yylloc, scanner,
243                                                 YY_("syntax error, unexpected expression, "
244                                                         "expecting %s"), MODE_TO_STRING(parser_mode));
245                                 sdb_object_deref(SDB_OBJ($1));
246                                 YYABORT;
247                         }
249                         if ($1) {
250                                 sdb_llist_append(pt, SDB_OBJ($1));
251                                 sdb_object_deref(SDB_OBJ($1));
252                         }
253                 }
254         ;
256 statement:
257         fetch_statement
258         |
259         list_statement
260         |
261         lookup_statement
262         |
263         store_statement
264         |
265         timeseries_statement
266         |
267         /* empty */
268                 {
269                         $$ = NULL;
270                 }
271         ;
273 /*
274  * FETCH host <hostname> [FILTER <condition>];
275  * FETCH <type> <hostname>.<name> [FILTER <condition>];
276  *
277  * Retrieve detailed information about a single object.
278  */
279 fetch_statement:
280         FETCH object_type STRING filter_clause
281                 {
282                         $$ = sdb_ast_fetch_create($2, NULL, $3, $4);
283                         CK_OOM($$);
284                 }
285         |
286         FETCH object_type STRING '.' STRING filter_clause
287                 {
288                         $$ = sdb_ast_fetch_create($2, $3, $5, $6);
289                         CK_OOM($$);
290                 }
291         ;
293 /*
294  * LIST <type> [FILTER <condition>];
295  *
296  * Returns a list of all objects in the store.
297  */
298 list_statement:
299         LIST object_type_plural filter_clause
300                 {
301                         $$ = sdb_ast_list_create($2, $3);
302                         CK_OOM($$);
303                 }
304         ;
306 /*
307  * LOOKUP <type> [MATCHING <condition>] [FILTER <condition>];
308  *
309  * Returns detailed information about objects matching a condition.
310  */
311 lookup_statement:
312         LOOKUP object_type_plural matching_clause filter_clause
313                 {
314                         $$ = sdb_ast_lookup_create($2, $3, $4);
315                         CK_OOM($$);
316                 }
317         ;
319 matching_clause:
320         MATCHING condition { $$ = $2; }
321         |
322         /* empty */ { $$ = NULL; }
324 filter_clause:
325         FILTER condition { $$ = $2; }
326         |
327         /* empty */ { $$ = NULL; }
329 /*
330  * STORE <type> <name>|<host>.<name> [LAST UPDATE <datetime>];
331  * STORE METRIC <host>.<name> STORE <type> <id> [LAST UPDATE <datetime>];
332  * STORE <type> ATTRIBUTE <parent>.<key> <datum> [LAST UPDATE <datetime>];
333  *
334  * Store or update an object in the database.
335  */
336 store_statement:
337         STORE HOST_T STRING last_update_clause
338                 {
339                         $$ = sdb_ast_store_create(SDB_HOST, NULL, 0, NULL,
340                                         $3, $4, NULL, NULL, SDB_DATA_NULL);
341                         CK_OOM($$);
342                 }
343         |
344         STORE SERVICE_T STRING '.' STRING last_update_clause
345                 {
346                         $$ = sdb_ast_store_create(SDB_SERVICE, $3, 0, NULL,
347                                         $5, $6, NULL, NULL, SDB_DATA_NULL);
348                         CK_OOM($$);
349                 }
350         |
351         STORE METRIC_T STRING '.' STRING metric_store_clause last_update_clause
352                 {
353                         $$ = sdb_ast_store_create(SDB_METRIC, $3, 0, NULL,
354                                         $5, $7, $6.type, $6.id, SDB_DATA_NULL);
355                         CK_OOM($$);
356                 }
357         |
358         STORE HOST_T ATTRIBUTE_T STRING '.' STRING data last_update_clause
359                 {
360                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, 0, NULL,
361                                         $6, $8, NULL, NULL, $7);
362                         CK_OOM($$);
363                 }
364         |
365         STORE SERVICE_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
366                 {
367                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, SDB_SERVICE, $6,
368                                         $8, $10, NULL, NULL, $9);
369                         CK_OOM($$);
370                 }
371         |
372         STORE METRIC_T ATTRIBUTE_T STRING '.' STRING '.' STRING data last_update_clause
373                 {
374                         $$ = sdb_ast_store_create(SDB_ATTRIBUTE, $4, SDB_METRIC, $6,
375                                         $8, $10, NULL, NULL, $9);
376                         CK_OOM($$);
377                 }
378         ;
380 last_update_clause:
381         LAST UPDATE datetime { $$ = $3; }
382         |
383         /* empty */ { $$ = sdb_gettime(); }
385 metric_store_clause:
386         STORE STRING STRING { $$.type = $2; $$.id = $3; }
387         |
388         /* empty */ { $$.type = $$.id = NULL; }
390 /*
391  * TIMESERIES <host>.<metric> [START <datetime>] [END <datetime>];
392  *
393  * Returns a time-series for the specified host's metric.
394  */
395 timeseries_statement:
396         TIMESERIES STRING '.' STRING start_clause end_clause
397                 {
398                         $$ = sdb_ast_timeseries_create($2, $4, $5, $6);
399                         CK_OOM($$);
400                 }
401         ;
403 start_clause:
404         START datetime { $$ = $2; }
405         |
406         /* empty */ { $$ = sdb_gettime() - SDB_INTERVAL_HOUR; }
408 end_clause:
409         END datetime { $$ = $2; }
410         |
411         /* empty */ { $$ = sdb_gettime(); }
413 /*
414  * Basic expressions.
415  */
417 condition:
418         '(' condition ')'
419                 {
420                         $$ = $2;
421                 }
422         |
423         condition AND condition
424                 {
425                         $$ = sdb_ast_op_create(SDB_AST_AND, $1, $3);
426                         CK_OOM($$);
427                 }
428         |
429         condition OR condition
430                 {
431                         $$ = sdb_ast_op_create(SDB_AST_OR, $1, $3);
432                         CK_OOM($$);
433                 }
434         |
435         NOT condition
436                 {
437                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $2);
438                         CK_OOM($$);
439                 }
440         |
441         comparison
442                 {
443                         $$ = $1;
444                 }
445         ;
447 comparison:
448         expression cmp expression
449                 {
450                         $$ = sdb_ast_op_create($2, $1, $3);
451                         CK_OOM($$);
452                 }
453         |
454         ANY expression cmp expression
455                 {
456                         $$ = sdb_ast_iter_create(SDB_AST_ANY, $3, $2, $4);
457                         CK_OOM($$);
458                 }
459         |
460         ALL expression cmp expression
461                 {
462                         $$ = sdb_ast_iter_create(SDB_AST_ALL, $3, $2, $4);
463                         CK_OOM($$);
464                 }
465         |
466         expression IS NULL_T
467                 {
468                         $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
469                         CK_OOM($$);
470                 }
471         |
472         expression IS NOT NULL_T
473                 {
474                         $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
475                         CK_OOM($$);
476                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
477                         CK_OOM($$);
478                 }
479         |
480         expression IN expression
481                 {
482                         $$ = sdb_ast_op_create(SDB_AST_IN, $1, $3);
483                         CK_OOM($$);
484                 }
485         |
486         expression NOT IN expression
487                 {
488                         $$ = sdb_ast_op_create(SDB_AST_IN, $1, $4);
489                         CK_OOM($$);
490                         $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
491                         CK_OOM($$);
492                 }
493         ;
495 expression:
496         '(' expression ')'
497                 {
498                         $$ = $2;
499                 }
500         |
501         expression '+' expression
502                 {
503                         $$ = sdb_ast_op_create(SDB_AST_ADD, $1, $3);
504                         CK_OOM($$);
505                 }
506         |
507         expression '-' expression
508                 {
509                         $$ = sdb_ast_op_create(SDB_AST_SUB, $1, $3);
510                         CK_OOM($$);
511                 }
512         |
513         expression '*' expression
514                 {
515                         $$ = sdb_ast_op_create(SDB_AST_MUL, $1, $3);
516                         CK_OOM($$);
517                 }
518         |
519         expression '/' expression
520                 {
521                         $$ = sdb_ast_op_create(SDB_AST_DIV, $1, $3);
522                         CK_OOM($$);
523                 }
524         |
525         expression '%' expression
526                 {
527                         $$ = sdb_ast_op_create(SDB_AST_MOD, $1, $3);
528                         CK_OOM($$);
529                 }
530         |
531         expression CONCAT expression
532                 {
533                         $$ = sdb_ast_op_create(SDB_AST_CONCAT, $1, $3);
534                         CK_OOM($$);
535                 }
536         |
537         object_expression
538                 {
539                         $$ = $1;
540                 }
541         |
542         data
543                 {
544                         $$ = sdb_ast_const_create($1);
545                         CK_OOM($$);
546                 }
547         ;
549 object_expression:
550         object_type '.' object_expression
551                 {
552                         $$ = sdb_ast_typed_create($1, $3);
553                         CK_OOM($$);
554                 }
555         |
556         ATTRIBUTE_T '.' object_expression
557                 {
558                         $$ = sdb_ast_typed_create(SDB_ATTRIBUTE, $3);
559                         CK_OOM($$);
560                 }
561         |
562         field
563                 {
564                         $$ = sdb_ast_value_create($1, NULL);
565                         CK_OOM($$);
566                 }
567         |
568         ATTRIBUTE_T '[' STRING ']'
569                 {
570                         $$ = sdb_ast_value_create(SDB_ATTRIBUTE, $3);
571                         CK_OOM($$);
572                 }
573         ;
575 object_type:
576         HOST_T { $$ = SDB_HOST; }
577         |
578         SERVICE_T { $$ = SDB_SERVICE; }
579         |
580         METRIC_T { $$ = SDB_METRIC; }
581         ;
583 object_type_plural:
584         HOSTS_T { $$ = SDB_HOST; }
585         |
586         SERVICES_T { $$ = SDB_SERVICE; }
587         |
588         METRICS_T { $$ = SDB_METRIC; }
589         ;
591 field:
592         NAME_T { $$ = SDB_FIELD_NAME; }
593         |
594         LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
595         |
596         AGE_T { $$ = SDB_FIELD_AGE; }
597         |
598         INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
599         |
600         BACKEND_T { $$ = SDB_FIELD_BACKEND; }
601         |
602         VALUE_T { $$ = SDB_FIELD_VALUE; }
603         ;
605 cmp:
606         CMP_EQUAL { $$ = SDB_AST_EQ; }
607         |
608         CMP_NEQUAL { $$ = SDB_AST_NE; }
609         |
610         CMP_REGEX { $$ = SDB_AST_REGEX; }
611         |
612         CMP_NREGEX { $$ = SDB_AST_NREGEX; }
613         |
614         CMP_LT { $$ = SDB_AST_LT; }
615         |
616         CMP_LE { $$ = SDB_AST_LE; }
617         |
618         CMP_GE { $$ = SDB_AST_GE; }
619         |
620         CMP_GT { $$ = SDB_AST_GT; }
621         ;
623 data:
624         STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
625         |
626         INTEGER { $$ = $1; }
627         |
628         FLOAT { $$ = $1; }
629         |
630         datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
631         |
632         interval { $$ = $1; }
633         |
634         array { $$ = $1; }
635         ;
637 datetime:
638         DATE TIME { $$ = $1 + $2; }
639         |
640         DATE { $$ = $1; }
641         |
642         TIME { $$ = $1; }
643         ;
645 interval:
646         interval interval_elem
647                 {
648                         $$.data.datetime = $1.data.datetime + $2.data.datetime;
649                 }
650         |
651         interval_elem { $$ = $1; }
652         ;
654 interval_elem:
655         INTEGER IDENTIFIER
656                 {
657                         sdb_time_t unit = sdb_strpunit($2);
658                         if (! unit) {
659                                 sdb_parser_yyerrorf(&yylloc, scanner,
660                                                 YY_("syntax error, invalid time unit %s"), $2);
661                                 free($2); $2 = NULL;
662                                 YYABORT;
663                         }
664                         free($2); $2 = NULL;
666                         if ($1.data.integer < 0) {
667                                 sdb_parser_yyerror(&yylloc, scanner,
668                                                 YY_("syntax error, negative intervals not supported"));
669                                 YYABORT;
670                         }
672                         $$.type = SDB_TYPE_DATETIME;
673                         $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
674                 }
675         ;
677 array:
678         '[' array_elem_list ']'
679                 {
680                         $$ = $2;
681                 }
682         ;
684 array_elem_list:
685         array_elem_list ',' data
686                 {
687                         size_t elem_size = sdb_data_sizeof($3.type);
689                         if (($3.type & SDB_TYPE_ARRAY) || (($1.type & 0xff) != $3.type)) {
690                                 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
691                                                 "cannot use element of type %s in array of type %s"),
692                                                 SDB_TYPE_TO_STRING($3.type),
693                                                 SDB_TYPE_TO_STRING($1.type));
694                                 sdb_data_free_datum(&$1);
695                                 sdb_data_free_datum(&$3);
696                                 YYABORT;
697                         }
699                         $$ = $1;
700                         $$.data.array.values = realloc($$.data.array.values,
701                                         ($$.data.array.length + 1) * elem_size);
702                         CK_OOM($$.data.array.values);
704                         memcpy((char *)$$.data.array.values + $$.data.array.length * elem_size,
705                                         &$3.data, elem_size);
706                         ++$$.data.array.length;
707                 }
708         |
709         data
710                 {
711                         size_t elem_size = sdb_data_sizeof($1.type);
713                         if ($1.type & SDB_TYPE_ARRAY) {
714                                 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
715                                                 "cannot construct array of type %s"),
716                                                 SDB_TYPE_TO_STRING($1.type));
717                                 sdb_data_free_datum(&$1);
718                                 YYABORT;
719                         }
721                         $$ = $1;
722                         $$.type |= SDB_TYPE_ARRAY;
723                         $$.data.array.values = malloc(elem_size);
724                         CK_OOM($$.data.array.values);
726                         memcpy($$.data.array.values, &$1.data, elem_size);
727                         $$.data.array.length = 1;
728                 }
729         ;
731 %%
733 void
734 sdb_parser_yyerror(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *msg)
736         sdb_log(SDB_LOG_ERR, "parser: parse error: %s", msg);
737         sdb_strbuf_sprintf(errbuf, "%s", msg);
738 } /* sdb_parser_yyerror */
740 void
741 sdb_parser_yyerrorf(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *fmt, ...)
743         va_list ap, aq;
744         va_start(ap, fmt);
745         va_copy(aq, ap);
746         sdb_vlog(SDB_LOG_ERR, fmt, ap);
747         sdb_strbuf_vsprintf(errbuf, fmt, aq);
748         va_end(ap);
749 } /* sdb_parser_yyerrorf */
751 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */