influxdb/parser/query.yacc

774 lines
20 KiB
Plaintext

%{
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "query_types.h"
value *create_value(char *name, int type, char is_case_insensitive, value_array *args) {
value *v = malloc(sizeof(value));
v->name = name;
v->value_type = type;
v->is_case_insensitive = is_case_insensitive;
v->args = args;
v->alias = NULL;
return v;
}
value *create_expression_value(char *operator, size_t size, ...) {
value *v = malloc(sizeof(value));
v->name = operator;
v->alias = NULL;
v->value_type = VALUE_EXPRESSION;
v->is_case_insensitive = FALSE;
v->args = malloc(sizeof(value_array));
v->args->size = size;
v->args->elems = malloc(sizeof(value*) * size);
va_list ap;
va_start(ap, size);
int i;
for (i = 0; i < size; i++) {
value *x = va_arg(ap, value*);
v->args->elems[i] = x;
}
va_end(ap);
return v;
}
%}
%union {
char character;
char* string;
int integer;
condition* condition;
value_array* value_array;
value* v;
from_clause* from_clause;
into_clause* into_clause;
query* query;
select_query* select_query;
delete_query* delete_query;
drop_series_query* drop_series_query;
drop_query* drop_query;
groupby_clause* groupby_clause;
table_name_array* table_name_array;
struct {
int limit;
char ascending;
} limit_and_order;
}
%debug
// better error/location reporting
%locations
%error-verbose
// declare that we want a reentrant parser
%define api.pure
%parse-param {queries *q}
%parse-param {void *scanner}
%lex-param {void *scanner}
// define types of tokens (terminals)
%token SELECT DELETE FROM WHERE EQUAL GROUP BY LIMIT ORDER ASC DESC MERGE INNER JOIN AS LIST SERIES INTO CONTINUOUS_QUERIES CONTINUOUS_QUERY DROP DROP_SERIES EXPLAIN UNKNOWN INCLUDE SPACES
%token <string> STRING_VALUE INT_VALUE FLOAT_VALUE BOOLEAN_VALUE TABLE_NAME SIMPLE_NAME INTO_NAME REGEX_OP
%token <string> NEGATION_REGEX_OP REGEX_STRING INSENSITIVE_REGEX_STRING DURATION
// define the precedence of these operators
%left OR
%left AND
%nonassoc <string> OPERATION_EQUAL OPERATION_NE OPERATION_GT OPERATION_LT OPERATION_LE OPERATION_GE OPERATION_IN
%left <character> '+' '-'
%left <character> '*' '/'
// define the types of the non-terminals
%type <from_clause> FROM_CLAUSE
%type <condition> WHERE_CLAUSE
%type <value_array> COLUMN_NAMES
%type <string> BOOL_OPERATION ALIAS_CLAUSE
%type <condition> CONDITION
%type <v> BOOL_EXPRESSION
%type <value_array> VALUES
%type <v> VALUE TABLE_VALUE SIMPLE_TABLE_VALUE TABLE_NAME_VALUE SIMPLE_NAME_VALUE INTO_VALUE INTO_NAME_VALUE
%type <table_name_array> SIMPLE_TABLE_VALUES
%type <v> WILDCARD REGEX_VALUE DURATION_VALUE FUNCTION_CALL
%type <groupby_clause> GROUP_BY_CLAUSE
%type <integer> LIMIT_CLAUSE
%type <character> ORDER_CLAUSE
%type <into_clause> INTO_CLAUSE
%type <limit_and_order> LIMIT_AND_ORDER_CLAUSES
%type <query> QUERY
%type <delete_query> DELETE_QUERY
%type <drop_series_query> DROP_SERIES_QUERY
%type <select_query> SELECT_QUERY
%type <drop_query> DROP_QUERY
%type <select_query> EXPLAIN_QUERY
// the initial token
%start ALL_QUERIES
// destructors are used to free up memory in case of an error
%destructor { free_value($$); } <v>
%destructor { free_from_clause($$); } <from_clause>
%destructor { if ($$) free_condition($$); } <condition>
%destructor { free($$); } <string>
%destructor { free_expression($$); } <expression>
%destructor { if ($$) free_value_array($$); } <value_array>
%destructor { free_groupby_clause($$); } <groupby_clause>
%destructor { close_query($$); free($$); } <query>
// grammar
%%
ALL_QUERIES:
QUERY
{
q->qs = realloc(q->qs, (q->size + 1) * sizeof(query));
q->qs[q->size++] = $1;
}
|
QUERY ';'
{
q->qs = realloc(q->qs, (q->size + 1) * sizeof(query));
q->qs[q->size++] = $1;
}
|
QUERY ';' ALL_QUERIES
{
q->qs = realloc(q->qs, (q->size + 1) * sizeof(query));
q->qs[q->size++] = $1;
}
QUERY:
SELECT_QUERY
{
$$ = calloc(1, sizeof(query));
$$->select_query = $1;
}
|
DELETE_QUERY
{
$$ = calloc(1, sizeof(query));
$$->delete_query = $1;
}
|
DROP_QUERY
{
$$ = calloc(1, sizeof(query));
$$->drop_query = $1;
}
|
LIST SERIES INCLUDE SPACES
{
$$ = calloc(1, sizeof(query));
$$->list_series_query = calloc(1, sizeof(list_series_query));
$$->list_series_query->include_spaces = TRUE;
}
|
LIST SERIES REGEX_VALUE INCLUDE SPACES
{
$$ = calloc(1, sizeof(query));
$$->list_series_query = calloc(1, sizeof(list_series_query));
$$->list_series_query->has_regex = TRUE;
$$->list_series_query->include_spaces = TRUE;
$$->list_series_query->regex = $3;
}
|
LIST SERIES
{
$$ = calloc(1, sizeof(query));
$$->list_series_query = calloc(1, sizeof(list_series_query));
}
|
LIST SERIES REGEX_VALUE
{
$$ = calloc(1, sizeof(query));
$$->list_series_query = calloc(1, sizeof(list_series_query));
$$->list_series_query->has_regex = TRUE;
$$->list_series_query->regex = $3;
}
|
DROP_SERIES_QUERY
{
$$ = calloc(1, sizeof(query));
$$->drop_series_query = $1;
}
|
LIST CONTINUOUS_QUERIES
{
$$ = calloc(1, sizeof(query));
$$->list_continuous_queries_query = TRUE;
}
|
EXPLAIN_QUERY
{
$$ = calloc(1, sizeof(query));
$$->select_query = $1;
}
DROP_QUERY:
DROP CONTINUOUS_QUERY INT_VALUE
{
$$ = calloc(1, sizeof(drop_query));
$$->id = atoi($3);
free($3);
}
DELETE_QUERY:
DELETE FROM_CLAUSE WHERE_CLAUSE
{
$$ = calloc(1, sizeof(delete_query));
$$->from_clause = $2;
$$->where_condition = $3;
}
DROP_SERIES_QUERY:
DROP_SERIES SIMPLE_TABLE_VALUE
{
$$ = malloc(sizeof(drop_series_query));
$$->name = $2;
}
EXPLAIN_QUERY:
EXPLAIN SELECT_QUERY
{
$$ = $2;
$$->explain = TRUE;
}
SELECT_QUERY:
SELECT COLUMN_NAMES FROM_CLAUSE GROUP_BY_CLAUSE WHERE_CLAUSE LIMIT_AND_ORDER_CLAUSES INTO_CLAUSE
{
$$ = calloc(1, sizeof(select_query));
$$->c = $2;
$$->from_clause = $3;
$$->group_by = $4;
$$->where_condition = $5;
$$->limit = $6.limit;
$$->ascending = $6.ascending;
$$->into_clause = $7;
$$->explain = FALSE;
}
|
SELECT COLUMN_NAMES FROM_CLAUSE WHERE_CLAUSE GROUP_BY_CLAUSE LIMIT_AND_ORDER_CLAUSES INTO_CLAUSE
{
$$ = calloc(1, sizeof(select_query));
$$->c = $2;
$$->from_clause = $3;
$$->where_condition = $4;
$$->group_by = $5;
$$->limit = $6.limit;
$$->ascending = $6.ascending;
$$->into_clause = $7;
$$->explain = FALSE;
}
LIMIT_AND_ORDER_CLAUSES:
ORDER_CLAUSE LIMIT_CLAUSE
{
$$.limit = $2;
$$.ascending = $1;
}
|
LIMIT_CLAUSE ORDER_CLAUSE
{
$$.limit = $1;
$$.ascending = $2;
}
ORDER_CLAUSE:
ORDER ASC
{
$$ = TRUE;
}
|
ORDER DESC
{
$$ = FALSE;
}
|
{
$$ = FALSE;
}
LIMIT_CLAUSE:
LIMIT INT_VALUE
{
$$ = atoi($2);
free($2);
}
|
{
$$ = -1;
}
VALUES:
VALUE
{
$$ = malloc(sizeof(value_array));
$$->size = 1;
$$->elems = malloc(sizeof(value*));
$$->elems[0] = $1;
}
|
VALUES ',' VALUE
{
size_t new_size = $1->size + 1;
$1->elems = realloc($$->elems, sizeof(value*) * new_size);
$1->elems[$1->size] = $3;
$1->size = new_size;
$$ = $1;
}
GROUP_BY_CLAUSE:
GROUP BY VALUES
{
$$ = malloc(sizeof(groupby_clause));
$$->elems = $3;
$$->fill_function = NULL;
}
|
GROUP BY VALUES FUNCTION_CALL
{
$$ = malloc(sizeof(groupby_clause));
$$->elems = $3;
$$->fill_function = $4;
}
|
{
$$ = NULL;
}
INTO_CLAUSE:
INTO INTO_VALUE
{
$$ = malloc(sizeof(into_clause));
$$->target = $2;
$$->backfill_function = NULL;
}
|
INTO INTO_VALUE FUNCTION_CALL
{
$$ = malloc(sizeof(into_clause));
$$->target = $2;
$$->backfill_function = $3;
}
|
{
$$ = NULL;
}
COLUMN_NAMES:
VALUES
ALIAS_CLAUSE:
AS SIMPLE_TABLE_VALUE
{
$$ = $2->name;
free($2);
}
|
{
$$ = NULL;
}
FROM_CLAUSE:
FROM TABLE_VALUE
{
$$ = calloc(1, sizeof(from_clause));
$$->names = malloc(sizeof(table_name_array));
$$->names->elems = malloc(sizeof(table_name*));
$$->names->size = 1;
$$->names->elems[0] = malloc(sizeof(table_name));
$$->names->elems[0]->name = $2;
$$->names->elems[0]->alias = NULL;
$$->from_clause_type = FROM_ARRAY;
}
|
FROM SIMPLE_TABLE_VALUES
{
$$ = calloc(1, sizeof(from_clause));
$$->names = $2;
$$->from_clause_type = FROM_ARRAY;
}
|
FROM SIMPLE_TABLE_VALUE
{
$$ = calloc(1, sizeof(from_clause));
$$->names = malloc(sizeof(table_name_array));
$$->names->elems = malloc(sizeof(table_name*));
$$->names->size = 1;
$$->names->elems[0] = malloc(sizeof(table_name));
$$->names->elems[0]->name = $2;
$$->names->elems[0]->alias = NULL;
$$->from_clause_type = FROM_ARRAY;
}
|
FROM SIMPLE_TABLE_VALUE MERGE SIMPLE_TABLE_VALUE
{
$$ = calloc(1, sizeof(from_clause));
$$->names = malloc(sizeof(table_name_array));
$$->names->elems = malloc(2 * sizeof(table_name*));
$$->names->size = 2;
$$->names->elems[0] = malloc(sizeof(table_name));
$$->names->elems[0]->name = $2;
$$->names->elems[0]->alias = NULL;
$$->names->elems[1] = malloc(sizeof(table_name));
$$->names->elems[1]->name = $4;
$$->names->elems[1]->alias = NULL;
$$->from_clause_type = FROM_MERGE;
}
|
FROM MERGE '(' SIMPLE_TABLE_VALUES ')'
{
$$ = calloc(1, sizeof(from_clause));
$$->names = $4;
$$->from_clause_type = FROM_MERGE;
}
|
FROM MERGE '(' REGEX_VALUE ')'
{
$$ = calloc(1, sizeof(from_clause));
$$->from_clause_type = FROM_MERGE_REGEX;
$$->regex_value = $4;
}
|
FROM SIMPLE_TABLE_VALUE ALIAS_CLAUSE INNER JOIN SIMPLE_TABLE_VALUE ALIAS_CLAUSE
{
$$ = calloc(1, sizeof(from_clause));
$$->regex_value = NULL;
$$->names = malloc(sizeof(table_name_array));
$$->names->elems = malloc(2 * sizeof(value*));
$$->names->size = 2;
$$->names->elems[0] = malloc(sizeof(table_name));
$$->names->elems[0]->name = $2;
$$->names->elems[0]->alias = $3;
$$->names->elems[1] = malloc(sizeof(table_name));
$$->names->elems[1]->name = $6;
$$->names->elems[1]->alias = $7;
$$->from_clause_type = FROM_JOIN;
}
|
FROM JOIN '(' SIMPLE_TABLE_VALUES ')'
{
$$ = calloc(1, sizeof(from_clause));
$$->names = $4;
$$->from_clause_type = FROM_JOIN;
}
|
FROM JOIN '(' REGEX_VALUE ')'
{
$$ = calloc(1, sizeof(from_clause));
$$->from_clause_type = FROM_JOIN_REGEX;
$$->regex_value = $4;
}
WHERE_CLAUSE:
WHERE CONDITION
{
$$ = $2;
}
|
{
$$ = NULL;
}
FUNCTION_CALL:
SIMPLE_NAME '(' ')'
{
$$ = create_value($1, VALUE_FUNCTION_CALL, FALSE, NULL);
}
|
SIMPLE_NAME '(' VALUES ')'
{
$$ = create_value($1, VALUE_FUNCTION_CALL, FALSE, $3);
}
VALUE:
STRING_VALUE
{
$$ = create_value($1, VALUE_STRING, FALSE, NULL);
}
|
INT_VALUE
{
$$ = create_value($1, VALUE_INT, FALSE, NULL);
}
|
'-' INT_VALUE
{
size_t len = strlen($2) + 2;
char *new_value = malloc(len);
new_value[0] = '-';
strncpy(new_value+1, $2, len-1);
free($2);
$$ = create_value(new_value, VALUE_INT, FALSE, NULL);
}
|
FLOAT_VALUE
{
$$ = create_value($1, VALUE_FLOAT, FALSE, NULL);
}
|
'-' FLOAT_VALUE
{
size_t len = strlen($2) + 2;
char *new_value = malloc(len);
new_value[0] = '-';
strncpy(new_value+1, $2, len-1);
free($2);
$$ = create_value(new_value, VALUE_FLOAT, FALSE, NULL);
}
|
BOOLEAN_VALUE
{
$$ = create_value($1, VALUE_BOOLEAN, FALSE, NULL);
}
|
DURATION_VALUE
{
$$ = $1;
$$->alias = NULL;
}
|
'-' DURATION_VALUE
{
char *name = calloc(1, strlen($2->name) + 2);
strcat(name, "-");
strcat(name, $2->name);
free($2->name);
$2->name = name;
$$ = $2;
$$->alias = NULL;
}
|
SIMPLE_NAME_VALUE
{
$$ = $1;
$$->alias = NULL;
}
|
WILDCARD
{
$$ = $1;
$$->alias = NULL;
}
|
TABLE_NAME_VALUE
{
$$ = $1;
$$->alias = NULL;
}
|
FUNCTION_CALL
{
$$ = $1;
$$->alias = NULL;
}
|
'(' VALUE ')'
{
$$ = $2;
}
|
'(' VALUE ')' AS SIMPLE_NAME
{
$$ = $2;
$$->alias = $5;
}
|
FUNCTION_CALL AS SIMPLE_NAME
{
$$ = $1;
$$->alias = $3;
}
|
VALUE '*' VALUE { $$ = create_expression_value(strdup("*"), 2, $1, $3); }
|
VALUE '/' VALUE { $$ = create_expression_value(strdup("/"), 2, $1, $3); }
|
VALUE '+' VALUE { $$ = create_expression_value(strdup("+"), 2, $1, $3); }
|
VALUE '-' VALUE { $$ = create_expression_value(strdup("-"), 2, $1, $3); }
TABLE_VALUE:
SIMPLE_NAME_VALUE | TABLE_NAME_VALUE | REGEX_VALUE
SIMPLE_TABLE_VALUE:
SIMPLE_NAME_VALUE | TABLE_NAME_VALUE
SIMPLE_TABLE_VALUES:
SIMPLE_TABLE_VALUE
{
$$ = malloc(sizeof(table_name_array));
$$->size = 1;
$$->elems = malloc(sizeof(table_name*));
$$->elems[0] = malloc(sizeof(table_name));
$$->elems[0]->name = $1;
$$->elems[0]->alias = NULL;
}
|
SIMPLE_TABLE_VALUES ',' SIMPLE_TABLE_VALUE
{
size_t new_size = $1->size + 1;
$1->elems = realloc($$->elems, sizeof(table_name*) * new_size);
$1->elems[$1->size] = malloc(sizeof(table_name));
$1->elems[$1->size]->name = $3;
$1->elems[$1->size]->alias = NULL;
$1->size = new_size;
$$ = $1;
}
INTO_VALUE:
SIMPLE_NAME_VALUE | TABLE_NAME_VALUE | INTO_NAME_VALUE
DURATION_VALUE:
DURATION
{
$$ = create_value($1, VALUE_DURATION, FALSE, NULL);
}
SIMPLE_NAME_VALUE:
SIMPLE_NAME
{
$$ = create_value($1, VALUE_SIMPLE_NAME, FALSE, NULL);
}
WILDCARD:
'*'
{
char *name = strdup("*");
$$ = create_value(name, VALUE_WILDCARD, FALSE, NULL);
}
INTO_NAME_VALUE:
INTO_NAME
{
$$ = create_value($1, VALUE_INTO_NAME, FALSE, NULL);
}
TABLE_NAME_VALUE:
TABLE_NAME
{
$$ = create_value($1, VALUE_TABLE_NAME, FALSE, NULL);
}
REGEX_VALUE:
REGEX_STRING
{
$$ = create_value($1, VALUE_REGEX, FALSE, NULL);
}
|
INSENSITIVE_REGEX_STRING
{
$$ = create_value($1, VALUE_REGEX, TRUE, NULL);
}
BOOL_EXPRESSION:
VALUE
|
VALUE BOOL_OPERATION VALUE
{
$$ = create_expression_value($2, 2, $1, $3);
}
|
VALUE OPERATION_IN '(' VALUES ')'
{
$$ = create_expression_value($2, 1, $1);
$$->args->elems = realloc($$->args->elems, sizeof(value*) * ($4->size + 1));
memcpy($$->args->elems + 1, $4->elems, $4->size * sizeof(value*));
$$->args->size = $4->size + 1;
free($4->elems);
free($4);
}
|
VALUE REGEX_OP REGEX_VALUE
{
$$ = create_expression_value($2, 2, $1, $3);
}
|
VALUE NEGATION_REGEX_OP REGEX_VALUE
{
$$ = create_expression_value($2, 2, $1, $3);
}
CONDITION:
BOOL_EXPRESSION
{
$$ = malloc(sizeof(condition));
$$->is_bool_expression = TRUE;
$$->left = $1;
$$->op = NULL;
$$->right = NULL;
}
|
'(' CONDITION ')'
{
$$ = $2;
}
|
CONDITION AND CONDITION
{
$$ = malloc(sizeof(condition));
$$->is_bool_expression = FALSE;
$$->left = $1;
$$->op = "AND";
$$->right = $3;
}
|
CONDITION OR CONDITION
{
$$ = malloc(sizeof(condition));
$$->is_bool_expression = FALSE;
$$->left = $1;
$$->op = "OR";
$$->right = $3;
}
BOOL_OPERATION:
OPERATION_EQUAL
|
OPERATION_NE
|
OPERATION_GT
|
OPERATION_LT
|
OPERATION_GE
|
OPERATION_LE
%%
void *yy_scan_string(char *, void *);
void yy_delete_buffer(void *, void *);
queries
parse_query(char *const query_s)
{
queries q = {0, NULL};
void *scanner;
yylex_init(&scanner);
#ifdef DEBUG
yydebug = 1;
yyset_debug(1, scanner);
#endif
void *buffer = yy_scan_string(query_s, scanner);
yyparse (&q, scanner);
yy_delete_buffer(buffer, scanner);
yylex_destroy(scanner);
return q;
}
int yyerror(YYLTYPE *locp, queries *q, void *s, char *err) {
q->error = malloc(sizeof(error));
q->error->err = strdup(err);
q->error->first_line = locp->first_line;
q->error->first_column = locp->first_column;
q->error->last_line = locp->last_line;
q->error->last_column = locp->last_column;
}