bdba4ea4e453022c19e8a8eaa5f7b80852fa53d4
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, $3, NULL, $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)
735 {
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, ...)
742 {
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 : */