From dbc62089b85a08c2be110bda34560176b5bb192b Mon Sep 17 00:00:00 2001 From: John Shahid Date: Mon, 18 Nov 2013 12:50:43 -0500 Subject: [PATCH] wip: fix #36. support table aliases --- CHANGELOG.md | 1 + src/engine/engine.go | 4 +-- src/parser/frees.c | 18 ++++++++++- src/parser/parser.go | 38 +++++++++++++++++++++-- src/parser/parser_test.go | 22 +++++++------- src/parser/query.lex | 1 + src/parser/query.yacc | 64 ++++++++++++++++++++++++++++++--------- src/parser/query_api.go | 3 +- src/parser/query_types.h | 17 ++++++++--- 9 files changed, 132 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdd16b33cb0..3a11566600f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,7 @@ ## Features - [Issue #51](https://github.com/influxdb/influxdb/issues/51). Implement first and last aggregates +- [Issue #36](https://github.com/influxdb/influxdb/issues/36). Support table aliases ## Bugfixes diff --git a/src/engine/engine.go b/src/engine/engine.go index 70a46457c09..3cc23aed432 100644 --- a/src/engine/engine.go +++ b/src/engine/engine.go @@ -51,11 +51,11 @@ func (self *QueryEngine) distributeQuery(user common.User, database string, quer // see if this is a merge query fromClause := query.GetFromClause() if fromClause.Type == parser.FromClauseMerge { - yield = getMergeYield(fromClause.Names[0].Name, fromClause.Names[1].Name, yield) + yield = getMergeYield(fromClause.Names[0].Name.Name, fromClause.Names[1].Name.Name, yield) } if fromClause.Type == parser.FromClauseInnerJoin { - yield = getJoinYield(fromClause.Names[0].Name, fromClause.Names[1].Name, yield) + yield = getJoinYield(fromClause.Names[0].Name.Name, fromClause.Names[1].Name.Name, yield) } return self.coordinator.DistributeQuery(user, database, query, yield) diff --git a/src/parser/frees.c b/src/parser/frees.c index 02d832b3789..a31e8840170 100644 --- a/src/parser/frees.c +++ b/src/parser/frees.c @@ -10,10 +10,26 @@ free_array(array *array) free(array->elems); free(array); } +void free_table_name(table_name *name) +{ + free_value(name->name); + free(name->alias); + free(name); +} +void +free_table_name_array(table_name_array *array) +{ + int i; + for (i = 0; i < array->size; i++) + free_table_name(array->elems[i]); + free(array->elems); + free(array); +} + void free_from_clause(from_clause *f) { - free_value_array(f->names); + free_table_name_array(f->names); free(f); } diff --git a/src/parser/parser.go b/src/parser/parser.go index 754db31b5b4..0967951beef 100644 --- a/src/parser/parser.go +++ b/src/parser/parser.go @@ -57,9 +57,14 @@ const ( FromClauseInnerJoin FromClauseType = C.FROM_INNER_JOIN ) +type TableName struct { + Name *Value + Alias string +} + type FromClause struct { Type FromClauseType - Names []*Value + Names []*TableName } type Expression struct { @@ -213,8 +218,37 @@ func GetValue(value *C.value) (*Value, error) { return v, err } +func GetTableName(name *C.table_name) (*TableName, error) { + value, err := GetValue(name.name) + if err != nil { + return nil, err + } + + table := &TableName{Name: value} + if name.alias != nil { + table.Alias = C.GoString(name.alias) + } + + return table, nil +} + +func GetTableNameArray(array *C.table_name_array) ([]*TableName, error) { + var names []*C.table_name + setupSlice((*reflect.SliceHeader)((unsafe.Pointer(&names))), unsafe.Pointer(array.elems), array.size) + + tableNamesSlice := make([]*TableName, 0, array.size) + for _, name := range names { + tableName, err := GetTableName(name) + if err != nil { + return nil, err + } + tableNamesSlice = append(tableNamesSlice, tableName) + } + return tableNamesSlice, nil +} + func GetFromClause(fromClause *C.from_clause) (*FromClause, error) { - arr, err := GetValueArray(fromClause.names) + arr, err := GetTableNameArray(fromClause.names) if err != nil { return nil, err } diff --git a/src/parser/parser_test.go b/src/parser/parser_test.go index 3ce405adf3e..9f46263adab 100644 --- a/src/parser/parser_test.go +++ b/src/parser/parser_test.go @@ -60,7 +60,7 @@ func (self *QueryParserSuite) TestSimpleFromClause(c *C) { fromClause := q.GetFromClause() c.Assert(fromClause.Type, Equals, FromClauseArray) c.Assert(fromClause.Names, HasLen, 1) - c.Assert(fromClause.Names[0].Name, Equals, "t") + c.Assert(fromClause.Names[0].Name.Name, Equals, "t") } func (self *QueryParserSuite) TestParseFromWithMergedTable(c *C) { @@ -69,8 +69,8 @@ func (self *QueryParserSuite) TestParseFromWithMergedTable(c *C) { fromClause := q.GetFromClause() c.Assert(fromClause.Type, Equals, FromClauseMerge) c.Assert(fromClause.Names, HasLen, 2) - c.Assert(fromClause.Names[0].Name, Equals, "newsletter.signups") - c.Assert(fromClause.Names[1].Name, Equals, "user.signups") + c.Assert(fromClause.Names[0].Name.Name, Equals, "newsletter.signups") + c.Assert(fromClause.Names[1].Name.Name, Equals, "user.signups") } func (self *QueryParserSuite) TestMultipleAggregateFunctions(c *C) { @@ -88,8 +88,8 @@ func (self *QueryParserSuite) TestParseFromWithJoinedTable(c *C) { fromClause := q.GetFromClause() c.Assert(fromClause.Type, Equals, FromClauseInnerJoin) c.Assert(fromClause.Names, HasLen, 2) - c.Assert(fromClause.Names[0].Name, Equals, "newsletter.signups") - c.Assert(fromClause.Names[1].Name, Equals, "user.signups") + c.Assert(fromClause.Names[0].Name.Name, Equals, "newsletter.signups") + c.Assert(fromClause.Names[1].Name.Name, Equals, "user.signups") } func (self *QueryParserSuite) TestParseSelectWithInsensitiveRegexTables(c *C) { @@ -99,9 +99,9 @@ func (self *QueryParserSuite) TestParseSelectWithInsensitiveRegexTables(c *C) { fromClause := q.GetFromClause() c.Assert(fromClause.Type, Equals, FromClauseArray) c.Assert(fromClause.Names, HasLen, 1) - c.Assert(fromClause.Names[0].Name, Equals, "users.*") - c.Assert(fromClause.Names[0].Type, Equals, ValueRegex) - c.Assert(fromClause.Names[0].IsCaseInsensitive, Equals, true) + c.Assert(fromClause.Names[0].Name.Name, Equals, "users.*") + c.Assert(fromClause.Names[0].Name.Type, Equals, ValueRegex) + c.Assert(fromClause.Names[0].Name.IsCaseInsensitive, Equals, true) } func (self *QueryParserSuite) TestParseSelectWithRegexTables(c *C) { @@ -111,9 +111,9 @@ func (self *QueryParserSuite) TestParseSelectWithRegexTables(c *C) { fromClause := q.GetFromClause() c.Assert(fromClause.Type, Equals, FromClauseArray) c.Assert(fromClause.Names, HasLen, 1) - c.Assert(fromClause.Names[0].Name, Equals, "users.*") - c.Assert(fromClause.Names[0].Type, Equals, ValueRegex) - c.Assert(fromClause.Names[0].IsCaseInsensitive, Equals, false) + c.Assert(fromClause.Names[0].Name.Name, Equals, "users.*") + c.Assert(fromClause.Names[0].Name.Type, Equals, ValueRegex) + c.Assert(fromClause.Names[0].Name.IsCaseInsensitive, Equals, false) } func (self *QueryParserSuite) TestMergeFromClause(c *C) { diff --git a/src/parser/query.lex b/src/parser/query.lex index 9a40e652aee..0d09781da09 100644 --- a/src/parser/query.lex +++ b/src/parser/query.lex @@ -27,6 +27,7 @@ static int yycolumn = 1; "join" { return JOIN; } "from" { return FROM; } "where" { return WHERE; } +"as" { return AS; } "select" { return SELECT; } "limit" { return LIMIT; } "order" { return ORDER; } diff --git a/src/parser/query.yacc b/src/parser/query.yacc index 8824564e921..8866a203372 100644 --- a/src/parser/query.yacc +++ b/src/parser/query.yacc @@ -60,8 +60,9 @@ value *create_value(char *name, int type, char is_case_insensitive, value_array %lex-param {void *scanner} // define types of tokens (terminals) -%token SELECT FROM WHERE EQUAL GROUP BY LIMIT ORDER ASC DESC MERGE INNER JOIN -%token STRING_VALUE INT_VALUE FLOAT_VALUE TABLE_NAME SIMPLE_NAME REGEX_OP NEGATION_REGEX_OP REGEX_STRING INSENSITIVE_REGEX_STRING DURATION +%token SELECT FROM WHERE EQUAL GROUP BY LIMIT ORDER ASC DESC MERGE INNER JOIN AS +%token STRING_VALUE INT_VALUE FLOAT_VALUE TABLE_NAME SIMPLE_NAME REGEX_OP +%token NEGATION_REGEX_OP REGEX_STRING INSENSITIVE_REGEX_STRING DURATION // define the precedence of these operators %left OR @@ -74,7 +75,7 @@ value *create_value(char *name, int type, char is_case_insensitive, value_array %type FROM_CLAUSE %type WHERE_CLAUSE %type COLUMN_NAMES -%type BOOL_OPERATION +%type BOOL_OPERATION ALIAS_CLAUSE %type CONDITION %type BOOL_EXPRESSION %type VALUES @@ -213,36 +214,69 @@ GROUP_BY_CLAUSE: COLUMN_NAMES: VALUES +ALIAS_CLAUSE: + AS SIMPLE_TABLE_VALUE + { + $$ = $2->name; + free($2); + } + | + { + $$ = NULL; + } + FROM_CLAUSE: FROM TABLE_VALUE { $$ = malloc(sizeof(from_clause)); - $$->names = malloc(sizeof(value_array)); - $$->names->elems = malloc(sizeof(value*)); + $$->names = malloc(sizeof(table_name_array)); + $$->names->elems = malloc(sizeof(table_name*)); $$->names->size = 1; - $$->names->elems[0] = $2; + $$->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 + FROM SIMPLE_TABLE_VALUE ALIAS_CLAUSE { $$ = malloc(sizeof(from_clause)); - $$->names = malloc(sizeof(value_array)); - $$->names->elems = malloc(2 * sizeof(value*)); + $$->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 = $3; + $$->from_clause_type = FROM_ARRAY; + } + | + FROM SIMPLE_TABLE_VALUE ALIAS_CLAUSE MERGE SIMPLE_TABLE_VALUE ALIAS_CLAUSE + { + $$ = malloc(sizeof(from_clause)); + $$->names = malloc(sizeof(table_name_array)); + $$->names->elems = malloc(2 * sizeof(table_name*)); $$->names->size = 2; - $$->names->elems[0] = $2; - $$->names->elems[1] = $4; + $$->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 = $5; + $$->names->elems[1]->alias = $6; $$->from_clause_type = FROM_MERGE; } | - FROM SIMPLE_TABLE_VALUE INNER JOIN SIMPLE_TABLE_VALUE + FROM SIMPLE_TABLE_VALUE ALIAS_CLAUSE INNER JOIN SIMPLE_TABLE_VALUE ALIAS_CLAUSE { $$ = malloc(sizeof(from_clause)); - $$->names = malloc(sizeof(value_array)); + $$->names = malloc(sizeof(table_name_array)); $$->names->elems = malloc(2 * sizeof(value*)); $$->names->size = 2; - $$->names->elems[0] = $2; - $$->names->elems[1] = $5; + $$->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_INNER_JOIN; } diff --git a/src/parser/query_api.go b/src/parser/query_api.go index 05507395c51..96a30c177bb 100644 --- a/src/parser/query_api.go +++ b/src/parser/query_api.go @@ -50,7 +50,8 @@ func (self *Query) GetReferencedColumns() map[*Value][]string { notPrefixedColumns = uniq(notPrefixedColumns) returnedMapping := make(map[*Value][]string) - for _, value := range self.GetFromClause().Names { + for _, tableName := range self.GetFromClause().Names { + value := tableName.Name if _, ok := value.GetCompiledRegex(); ok { // this is a regex table, cannot be referenced, only unreferenced // columns will be attached to regex table names diff --git a/src/parser/query_types.h b/src/parser/query_types.h index eb21563c0fe..61487c98a73 100644 --- a/src/parser/query_types.h +++ b/src/parser/query_types.h @@ -57,16 +57,25 @@ typedef struct { char *err; } error; +typedef struct { + value *name; + char *alias; +} table_name; + +typedef struct { + size_t size; + table_name **elems; +} table_name_array; + typedef struct { enum { FROM_ARRAY, FROM_MERGE, FROM_INNER_JOIN } from_clause_type; - // in case of merge or join, it's guaranteed - // that the names array will have two table - // names only and they aren't regex. - value_array *names; + // in case of merge or join, it's guaranteed that the names array + // will have two table names only and they aren't regex. + table_name_array *names; } from_clause; typedef struct {