From 5bb5161c8d1690bc3ca5a6bd458cbc0b94b72147 Mon Sep 17 00:00:00 2001 From: John Shahid <jvshahid@gmail.com> Date: Mon, 3 Mar 2014 13:05:02 -0500 Subject: [PATCH] add support for function calls aliasing in the parser --- src/parser/frees.c | 1 + src/parser/parser.go | 4 +++ src/parser/parser_test.go | 58 ++++++++++++++++++++------------- src/parser/query.yacc | 28 ++++++++++++++++ src/parser/query_types.h | 1 + src/parser/test_memory_leaks.sh | 4 +++ 6 files changed, 73 insertions(+), 23 deletions(-) diff --git a/src/parser/frees.c b/src/parser/frees.c index 62b02e3962..582b5e8e1a 100644 --- a/src/parser/frees.c +++ b/src/parser/frees.c @@ -60,6 +60,7 @@ void free_value(value *value) { free(value->name); + if (value->alias) free(value->alias); if (value->args) free_value_array(value->args); free(value); } diff --git a/src/parser/parser.go b/src/parser/parser.go index edc9eac153..a694178813 100644 --- a/src/parser/parser.go +++ b/src/parser/parser.go @@ -39,6 +39,7 @@ const ( type Value struct { Name string + Alias string Type ValueType Elems []*Value compiledRegex *regexp.Regexp @@ -385,6 +386,9 @@ func GetValue(value *C.value) (*Value, error) { v.compiledRegex, err = regexp.Compile(v.Name) } } + if value.alias != nil { + v.Alias = C.GoString(value.alias) + } return v, err } diff --git a/src/parser/parser_test.go b/src/parser/parser_test.go index 342ed06191..e9112d7dfb 100644 --- a/src/parser/parser_test.go +++ b/src/parser/parser_test.go @@ -19,7 +19,7 @@ var _ = Suite(&QueryParserSuite{}) func ToValueArray(strings ...string) (values []*Value) { for _, str := range strings { - values = append(values, &Value{str, ValueSimpleName, nil, nil}) + values = append(values, &Value{str, "", ValueSimpleName, nil, nil}) } return } @@ -463,18 +463,18 @@ func (self *QueryParserSuite) TestParseSelectWithAnd(c *C) { rightBoolExpression, ok := w.Right.GetBoolExpression() c.Assert(ok, Equals, true) - c.Assert(leftBoolExpression.Elems[0], DeepEquals, &Value{"value", ValueSimpleName, nil, nil}) + c.Assert(leftBoolExpression.Elems[0], DeepEquals, &Value{"value", "", ValueSimpleName, nil, nil}) value := leftBoolExpression.Elems[1].Elems[0] - c.Assert(value, DeepEquals, &Value{"exp", ValueFunctionCall, nil, nil}) + c.Assert(value, DeepEquals, &Value{"exp", "", ValueFunctionCall, nil, nil}) value = leftBoolExpression.Elems[1].Elems[1] - c.Assert(value, DeepEquals, &Value{"2", ValueInt, nil, nil}) + c.Assert(value, DeepEquals, &Value{"2", "", ValueInt, nil, nil}) c.Assert(leftBoolExpression.Name, Equals, ">") - c.Assert(rightBoolExpression.Elems[0], DeepEquals, &Value{"value", ValueSimpleName, nil, nil}) + c.Assert(rightBoolExpression.Elems[0], DeepEquals, &Value{"value", "", ValueSimpleName, nil, nil}) value = rightBoolExpression.Elems[1].Elems[0] - c.Assert(value, DeepEquals, &Value{"exp", ValueFunctionCall, nil, nil}) + c.Assert(value, DeepEquals, &Value{"exp", "", ValueFunctionCall, nil, nil}) value = rightBoolExpression.Elems[1].Elems[1] - c.Assert(value, DeepEquals, &Value{"3", ValueInt, nil, nil}) + c.Assert(value, DeepEquals, &Value{"3", "", ValueInt, nil, nil}) c.Assert(rightBoolExpression.Name, Equals, "<") } @@ -568,14 +568,14 @@ func (self *QueryParserSuite) TestParseWhereClausePrecedence(c *C) { leftExpression, ok := condition.GetBoolExpression() c.Assert(ok, Equals, true) c.Assert(leftExpression.Name, Equals, ">") - c.Assert(leftExpression.Elems[0], DeepEquals, &Value{"value", ValueSimpleName, nil, nil}) - c.Assert(leftExpression.Elems[1], DeepEquals, &Value{"90", ValueInt, nil, nil}) + c.Assert(leftExpression.Elems[0], DeepEquals, &Value{"value", "", ValueSimpleName, nil, nil}) + c.Assert(leftExpression.Elems[1], DeepEquals, &Value{"90", "", ValueInt, nil, nil}) rightExpression, ok := leftCondition.Right.GetBoolExpression() c.Assert(ok, Equals, true) c.Assert(rightExpression.Name, Equals, ">") - c.Assert(rightExpression.Elems[0], DeepEquals, &Value{"other_value", ValueSimpleName, nil, nil}) - c.Assert(rightExpression.Elems[1], DeepEquals, &Value{"10.0", ValueFloat, nil, nil}) + c.Assert(rightExpression.Elems[0], DeepEquals, &Value{"other_value", "", ValueSimpleName, nil, nil}) + c.Assert(rightExpression.Elems[1], DeepEquals, &Value{"10.0", "", ValueFloat, nil, nil}) } func (self *QueryParserSuite) TestParseWhereClauseParentheses(c *C) { @@ -631,11 +631,23 @@ func (self *QueryParserSuite) TestParseFromWithNestedFunctions2(c *C) { c.Assert(q.GetGroupByClause().Elems, HasLen, 1) c.Assert(q.GetGroupByClause().Elems[0], DeepEquals, &Value{ Name: "time", + Alias: "", Type: ValueFunctionCall, - Elems: []*Value{&Value{"15m", ValueDuration, nil, nil}}, + Elems: []*Value{&Value{"15m", "", ValueDuration, nil, nil}}, }) } +func (self *QueryParserSuite) TestParseWithColumnAlias(c *C) { + q, err := ParseSelectQuery("select count(email) as email_count from user.events") + c.Assert(err, IsNil) + c.Assert(q.GetColumnNames(), HasLen, 1) + column := q.GetColumnNames()[0] + c.Assert(column.IsFunctionCall(), Equals, true) + c.Assert(column.Name, Equals, "count") + c.Assert(column.Elems, HasLen, 1) + c.Assert(column.Alias, Equals, "email_count") +} + func (self *QueryParserSuite) TestParseSelectWithInvalidRegex(c *C) { _, err := ParseSelectQuery("select email from users.events where email =~ /[/i and time>now()-2d;") c.Assert(err, ErrorMatches, ".*missing closing.*") @@ -648,7 +660,7 @@ func (self *QueryParserSuite) TestParseSelectWithRegexCondition(c *C) { // note: conditions that involve time are removed after the query is parsed regexExpression, _ := w.GetBoolExpression() - c.Assert(regexExpression.Elems[0], DeepEquals, &Value{"email", ValueSimpleName, nil, nil}) + c.Assert(regexExpression.Elems[0], DeepEquals, &Value{"email", "", ValueSimpleName, nil, nil}) c.Assert(regexExpression.Name, Equals, "=~") expr := regexExpression.Elems[1] c.Assert(expr.Type, Equals, ValueRegex) @@ -662,7 +674,7 @@ func (self *QueryParserSuite) TestParseSelectWithComplexArithmeticOperations(c * boolExpression, ok := q.GetWhereCondition().GetBoolExpression() c.Assert(ok, Equals, true) - c.Assert(boolExpression.Elems[0], DeepEquals, &Value{".30", ValueFloat, nil, nil}) + c.Assert(boolExpression.Elems[0], DeepEquals, &Value{".30", "", ValueFloat, nil, nil}) // value * 1 / 3 rightExpression := boolExpression.Elems[1] @@ -727,14 +739,14 @@ func (self *QueryParserSuite) TestParseSinglePointQuery(c *C) { rightBoolExpression, ok := w.Right.GetBoolExpression() c.Assert(ok, Equals, true) - c.Assert(leftBoolExpression.Elems[0], DeepEquals, &Value{"time", ValueSimpleName, nil, nil}) + c.Assert(leftBoolExpression.Elems[0], DeepEquals, &Value{"time", "", ValueSimpleName, nil, nil}) value := leftBoolExpression.Elems[1] - c.Assert(value, DeepEquals, &Value{"999", ValueInt, nil, nil}) + c.Assert(value, DeepEquals, &Value{"999", "", ValueInt, nil, nil}) c.Assert(leftBoolExpression.Name, Equals, "=") - c.Assert(rightBoolExpression.Elems[0], DeepEquals, &Value{"sequence_number", ValueSimpleName, nil, nil}) + c.Assert(rightBoolExpression.Elems[0], DeepEquals, &Value{"sequence_number", "", ValueSimpleName, nil, nil}) value = rightBoolExpression.Elems[1] - c.Assert(value, DeepEquals, &Value{"1", ValueInt, nil, nil}) + c.Assert(value, DeepEquals, &Value{"1", "", ValueInt, nil, nil}) c.Assert(rightBoolExpression.Name, Equals, "=") } @@ -754,7 +766,7 @@ func (self *QueryParserSuite) TestParseContinuousQueryCreation(c *C) { c.Assert(q.IsContinuousQuery(), Equals, true) c.Assert(q.IsValidContinuousQuery(), Equals, true) clause := q.GetIntoClause() - c.Assert(clause.Target, DeepEquals, &Value{"bar", ValueSimpleName, nil, nil}) + c.Assert(clause.Target, DeepEquals, &Value{"bar", "", ValueSimpleName, nil, nil}) } func (self *QueryParserSuite) TestParseInterpolatedContinuousQueryCreation(c *C) { @@ -763,28 +775,28 @@ func (self *QueryParserSuite) TestParseInterpolatedContinuousQueryCreation(c *C) c.Assert(err, IsNil) c.Assert(q.IsContinuousQuery(), Equals, true) clause := q.GetIntoClause() - c.Assert(clause.Target, DeepEquals, &Value{"bar.[c4]", ValueIntoName, nil, nil}) + c.Assert(clause.Target, DeepEquals, &Value{"bar.[c4]", "", ValueIntoName, nil, nil}) query = "select * from foo into [c5].bar.[c4];" q, err = ParseSelectQuery(query) c.Assert(err, IsNil) c.Assert(q.IsContinuousQuery(), Equals, true) clause = q.GetIntoClause() - c.Assert(clause.Target, DeepEquals, &Value{"[c5].bar.[c4]", ValueIntoName, nil, nil}) + c.Assert(clause.Target, DeepEquals, &Value{"[c5].bar.[c4]", "", ValueIntoName, nil, nil}) query = "select average(c4), count(c5) from s3 group by time(1h) into [average].[count];" q, err = ParseSelectQuery(query) c.Assert(err, IsNil) c.Assert(q.IsContinuousQuery(), Equals, true) clause = q.GetIntoClause() - c.Assert(clause.Target, DeepEquals, &Value{"[average].[count]", ValueIntoName, nil, nil}) + c.Assert(clause.Target, DeepEquals, &Value{"[average].[count]", "", ValueIntoName, nil, nil}) query = "select * from foo into :series_name.foo;" q, err = ParseSelectQuery(query) c.Assert(err, IsNil) c.Assert(q.IsContinuousQuery(), Equals, true) clause = q.GetIntoClause() - c.Assert(clause.Target, DeepEquals, &Value{":series_name.foo", ValueIntoName, nil, nil}) + c.Assert(clause.Target, DeepEquals, &Value{":series_name.foo", "", ValueIntoName, nil, nil}) query = "select * from foo into ]bar" q, err = ParseSelectQuery(query) diff --git a/src/parser/query.yacc b/src/parser/query.yacc index 24e3f51a4b..f894fbc83c 100644 --- a/src/parser/query.yacc +++ b/src/parser/query.yacc @@ -13,12 +13,14 @@ value *create_value(char *name, int type, char is_case_insensitive, value_array 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)); @@ -418,20 +420,46 @@ VALUE: } | DURATION_VALUE + { + $$ = $1; + $$->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; } | + 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); } diff --git a/src/parser/query_types.h b/src/parser/query_types.h index b11898b1c3..c9d5c4775a 100644 --- a/src/parser/query_types.h +++ b/src/parser/query_types.h @@ -32,6 +32,7 @@ typedef struct value_t { VALUE_FUNCTION_CALL, VALUE_EXPRESSION } value_type; + char *alias; char is_case_insensitive; value_array *args; } value; diff --git a/src/parser/test_memory_leaks.sh b/src/parser/test_memory_leaks.sh index 356f0d5e2e..b25c7aff64 100755 --- a/src/parser/test_memory_leaks.sh +++ b/src/parser/test_memory_leaks.sh @@ -12,6 +12,10 @@ int main(int argc, char **argv) { q = parse_query("select count(*) from users.events group_by user_email,time(1h) where time >> now()-1d;"); close_query(&q); + // test freeing alias + q = parse_query("select count(bar) as the_count from users.events group_by user_email,time(1h);"); + close_query(&q); + // test freeing where conditions q = parse_query("select value from t where c == 5 and b == 6;"); close_query(&q);