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;
108 }
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_node_t *n = sdb_ast_op_create($3, NULL, $4);
457 CK_OOM(n);
458 $$ = sdb_ast_iter_create(SDB_AST_ANY, $2, n);
459 CK_OOM($$);
460 }
461 |
462 ALL expression cmp expression
463 {
464 sdb_ast_node_t *n = sdb_ast_op_create($3, NULL, $4);
465 CK_OOM(n);
466 $$ = sdb_ast_iter_create(SDB_AST_ALL, $2, n);
467 CK_OOM($$);
468 }
469 |
470 expression IS NULL_T
471 {
472 $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
473 CK_OOM($$);
474 }
475 |
476 expression IS NOT NULL_T
477 {
478 $$ = sdb_ast_op_create(SDB_AST_ISNULL, NULL, $1);
479 CK_OOM($$);
480 $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
481 CK_OOM($$);
482 }
483 |
484 expression IN expression
485 {
486 $$ = sdb_ast_op_create(SDB_AST_IN, $1, $3);
487 CK_OOM($$);
488 }
489 |
490 expression NOT IN expression
491 {
492 $$ = sdb_ast_op_create(SDB_AST_IN, $1, $4);
493 CK_OOM($$);
494 $$ = sdb_ast_op_create(SDB_AST_NOT, NULL, $$);
495 CK_OOM($$);
496 }
497 ;
499 expression:
500 '(' expression ')'
501 {
502 $$ = $2;
503 }
504 |
505 expression '+' expression
506 {
507 $$ = sdb_ast_op_create(SDB_AST_ADD, $1, $3);
508 CK_OOM($$);
509 }
510 |
511 expression '-' expression
512 {
513 $$ = sdb_ast_op_create(SDB_AST_SUB, $1, $3);
514 CK_OOM($$);
515 }
516 |
517 expression '*' expression
518 {
519 $$ = sdb_ast_op_create(SDB_AST_MUL, $1, $3);
520 CK_OOM($$);
521 }
522 |
523 expression '/' expression
524 {
525 $$ = sdb_ast_op_create(SDB_AST_DIV, $1, $3);
526 CK_OOM($$);
527 }
528 |
529 expression '%' expression
530 {
531 $$ = sdb_ast_op_create(SDB_AST_MOD, $1, $3);
532 CK_OOM($$);
533 }
534 |
535 expression CONCAT expression
536 {
537 $$ = sdb_ast_op_create(SDB_AST_CONCAT, $1, $3);
538 CK_OOM($$);
539 }
540 |
541 object_expression
542 {
543 $$ = $1;
544 }
545 |
546 data
547 {
548 $$ = sdb_ast_const_create($1);
549 CK_OOM($$);
550 }
551 ;
553 object_expression:
554 object_type '.' object_expression
555 {
556 $$ = sdb_ast_typed_create($1, $3);
557 CK_OOM($$);
558 }
559 |
560 ATTRIBUTE_T '.' object_expression
561 {
562 $$ = sdb_ast_typed_create(SDB_ATTRIBUTE, $3);
563 CK_OOM($$);
564 }
565 |
566 field
567 {
568 $$ = sdb_ast_value_create($1, NULL);
569 CK_OOM($$);
570 }
571 |
572 ATTRIBUTE_T '[' STRING ']'
573 {
574 $$ = sdb_ast_value_create(SDB_ATTRIBUTE, $3);
575 CK_OOM($$);
576 }
577 ;
579 object_type:
580 HOST_T { $$ = SDB_HOST; }
581 |
582 SERVICE_T { $$ = SDB_SERVICE; }
583 |
584 METRIC_T { $$ = SDB_METRIC; }
585 ;
587 object_type_plural:
588 HOSTS_T { $$ = SDB_HOST; }
589 |
590 SERVICES_T { $$ = SDB_SERVICE; }
591 |
592 METRICS_T { $$ = SDB_METRIC; }
593 ;
595 field:
596 NAME_T { $$ = SDB_FIELD_NAME; }
597 |
598 LAST_UPDATE_T { $$ = SDB_FIELD_LAST_UPDATE; }
599 |
600 AGE_T { $$ = SDB_FIELD_AGE; }
601 |
602 INTERVAL_T { $$ = SDB_FIELD_INTERVAL; }
603 |
604 BACKEND_T { $$ = SDB_FIELD_BACKEND; }
605 |
606 VALUE_T { $$ = SDB_FIELD_VALUE; }
607 ;
609 cmp:
610 CMP_EQUAL { $$ = SDB_AST_EQ; }
611 |
612 CMP_NEQUAL { $$ = SDB_AST_NE; }
613 |
614 CMP_REGEX { $$ = SDB_AST_REGEX; }
615 |
616 CMP_NREGEX { $$ = SDB_AST_NREGEX; }
617 |
618 CMP_LT { $$ = SDB_AST_LT; }
619 |
620 CMP_LE { $$ = SDB_AST_LE; }
621 |
622 CMP_GE { $$ = SDB_AST_GE; }
623 |
624 CMP_GT { $$ = SDB_AST_GT; }
625 ;
627 data:
628 STRING { $$.type = SDB_TYPE_STRING; $$.data.string = $1; }
629 |
630 INTEGER { $$ = $1; }
631 |
632 FLOAT { $$ = $1; }
633 |
634 datetime { $$.type = SDB_TYPE_DATETIME; $$.data.datetime = $1; }
635 |
636 interval { $$ = $1; }
637 |
638 array { $$ = $1; }
639 ;
641 datetime:
642 DATE TIME { $$ = $1 + $2; }
643 |
644 DATE { $$ = $1; }
645 |
646 TIME { $$ = $1; }
647 ;
649 interval:
650 interval interval_elem
651 {
652 $$.data.datetime = $1.data.datetime + $2.data.datetime;
653 }
654 |
655 interval_elem { $$ = $1; }
656 ;
658 interval_elem:
659 INTEGER IDENTIFIER
660 {
661 sdb_time_t unit = sdb_strpunit($2);
662 if (! unit) {
663 sdb_parser_yyerrorf(&yylloc, scanner,
664 YY_("syntax error, invalid time unit %s"), $2);
665 free($2); $2 = NULL;
666 YYABORT;
667 }
668 free($2); $2 = NULL;
670 if ($1.data.integer < 0) {
671 sdb_parser_yyerror(&yylloc, scanner,
672 YY_("syntax error, negative intervals not supported"));
673 YYABORT;
674 }
676 $$.type = SDB_TYPE_DATETIME;
677 $$.data.datetime = (sdb_time_t)$1.data.integer * unit;
678 }
679 ;
681 array:
682 '[' array_elem_list ']'
683 {
684 $$ = $2;
685 }
686 ;
688 array_elem_list:
689 array_elem_list ',' data
690 {
691 size_t elem_size = sdb_data_sizeof($3.type);
693 if (($3.type & SDB_TYPE_ARRAY) || (($1.type & 0xff) != $3.type)) {
694 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
695 "cannot use element of type %s in array of type %s"),
696 SDB_TYPE_TO_STRING($3.type),
697 SDB_TYPE_TO_STRING($1.type));
698 sdb_data_free_datum(&$1);
699 sdb_data_free_datum(&$3);
700 YYABORT;
701 }
703 $$ = $1;
704 $$.data.array.values = realloc($$.data.array.values,
705 ($$.data.array.length + 1) * elem_size);
706 CK_OOM($$.data.array.values);
708 memcpy((char *)$$.data.array.values + $$.data.array.length * elem_size,
709 &$3.data, elem_size);
710 ++$$.data.array.length;
711 }
712 |
713 data
714 {
715 size_t elem_size = sdb_data_sizeof($1.type);
717 if ($1.type & SDB_TYPE_ARRAY) {
718 sdb_parser_yyerrorf(&yylloc, scanner, YY_("syntax error, "
719 "cannot construct array of type %s"),
720 SDB_TYPE_TO_STRING($1.type));
721 sdb_data_free_datum(&$1);
722 YYABORT;
723 }
725 $$ = $1;
726 $$.type |= SDB_TYPE_ARRAY;
727 $$.data.array.values = malloc(elem_size);
728 CK_OOM($$.data.array.values);
730 memcpy($$.data.array.values, &$1.data, elem_size);
731 $$.data.array.length = 1;
732 }
733 ;
735 %%
737 void
738 sdb_parser_yyerror(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *msg)
739 {
740 sdb_log(SDB_LOG_ERR, "parser: parse error: %s", msg);
741 sdb_strbuf_sprintf(errbuf, "%s", msg);
742 } /* sdb_parser_yyerror */
744 void
745 sdb_parser_yyerrorf(YYLTYPE *lval, sdb_parser_yyscan_t scanner, const char *fmt, ...)
746 {
747 va_list ap, aq;
748 va_start(ap, fmt);
749 va_copy(aq, ap);
750 sdb_vlog(SDB_LOG_ERR, fmt, ap);
751 sdb_strbuf_vsprintf(errbuf, fmt, aq);
752 va_end(ap);
753 } /* sdb_parser_yyerrorf */
755 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */