From 2603391c21cbe5d5a690778886e69f6e0b74cf38 Mon Sep 17 00:00:00 2001 From: John Shahid Date: Mon, 27 Jan 2014 19:04:31 -0500 Subject: [PATCH 1/4] reconstruct the query string from the ast --- src/coordinator/raft_server.go | 2 +- src/parser/from_clause.go | 65 +++++++++ src/parser/group_by.go | 46 ++++++ src/parser/parser.go | 251 ++++++++++++++++----------------- src/parser/parser_test.go | 39 +++-- src/parser/query_api.go | 64 +++------ src/parser/query_api_test.go | 4 +- src/parser/query_spec.go | 7 +- src/parser/value.go | 64 +++++++++ src/parser/values.go | 13 ++ src/parser/where_condition.go | 34 +++++ 11 files changed, 398 insertions(+), 191 deletions(-) create mode 100644 src/parser/from_clause.go create mode 100644 src/parser/group_by.go create mode 100644 src/parser/value.go create mode 100644 src/parser/values.go create mode 100644 src/parser/where_condition.go diff --git a/src/coordinator/raft_server.go b/src/coordinator/raft_server.go index 7e7294f5c1d..57c143988c4 100644 --- a/src/coordinator/raft_server.go +++ b/src/coordinator/raft_server.go @@ -477,7 +477,7 @@ func (s *RaftServer) runContinuousQuery(db string, query *parser.SelectQuery, st clusterAdmin := s.clusterConfig.GetClusterAdmin(adminName) intoClause := query.GetIntoClause() targetName := intoClause.Target.Name - queryString := query.GetQueryStringForContinuousQuery(start, end) + queryString := query.GetQueryStringWithTimesAndNoIntoClause(start, end) f := func(series *protocol.Series) error { return s.coordinator.InterpolateValuesAndCommit(query.GetQueryString(), db, series, targetName, true) diff --git a/src/parser/from_clause.go b/src/parser/from_clause.go new file mode 100644 index 00000000000..58aef71de22 --- /dev/null +++ b/src/parser/from_clause.go @@ -0,0 +1,65 @@ +package parser + +// #include "query_types.h" +// #include +import "C" +import ( + "bytes" + "strings" +) +import "fmt" + +type FromClauseType int + +const ( + FromClauseArray FromClauseType = C.FROM_ARRAY + FromClauseMerge FromClauseType = C.FROM_MERGE + FromClauseInnerJoin FromClauseType = C.FROM_INNER_JOIN +) + +func (self *TableName) GetAlias() string { + if self.Alias != "" { + return self.Alias + } + return self.Name.Name +} + +func (self *TableName) GetAliasString() string { + if self.Alias != "" { + return fmt.Sprintf(" as %s", self.Alias) + } + return "" +} + +type TableName struct { + Name *Value + Alias string +} + +type FromClause struct { + Type FromClauseType + Names []*TableName +} + +func (self *FromClause) GetString() string { + buffer := bytes.NewBufferString("") + switch self.Type { + case FromClauseMerge: + fmt.Fprintf(buffer, "%s%s merge %s %s", self.Names[0].Name.GetString(), self.Names[1].GetAliasString(), + self.Names[1].Name.GetString(), self.Names[1].GetAliasString()) + case FromClauseInnerJoin: + fmt.Fprintf(buffer, "%s%s inner join %s%s", self.Names[0].Name.GetString(), self.Names[0].GetAliasString(), + self.Names[1].Name.GetString(), self.Names[1].GetAliasString()) + default: + names := make([]string, 0, len(self.Names)) + for _, t := range self.Names { + alias := "" + if t.Alias != "" { + alias = fmt.Sprintf(" as %s", t.Alias) + } + names = append(names, fmt.Sprintf("%s%s", t.Name.GetString(), alias)) + } + buffer.WriteString(strings.Join(names, ",")) + } + return buffer.String() +} diff --git a/src/parser/group_by.go b/src/parser/group_by.go new file mode 100644 index 00000000000..3154a4a280f --- /dev/null +++ b/src/parser/group_by.go @@ -0,0 +1,46 @@ +package parser + +import ( + "bytes" + "common" + "fmt" + "time" +) + +type GroupByClause struct { + FillWithZero bool + FillValue *Value + Elems []*Value +} + +func (self GroupByClause) GetGroupByTime() (*time.Duration, error) { + for _, groupBy := range self.Elems { + if groupBy.IsFunctionCall() { + // TODO: check the number of arguments and return an error + if len(groupBy.Elems) != 1 { + return nil, common.NewQueryError(common.WrongNumberOfArguments, "time function only accepts one argument") + } + // TODO: check the function name + // TODO: error checking + arg := groupBy.Elems[0].Name + durationInt, err := common.ParseTimeDuration(arg) + if err != nil { + return nil, common.NewQueryError(common.InvalidArgument, fmt.Sprintf("invalid argument %s to the time function", arg)) + } + duration := time.Duration(durationInt) + return &duration, nil + } + } + return nil, nil +} + +func (self *GroupByClause) GetString() string { + buffer := bytes.NewBufferString("") + + buffer.WriteString(Values(self.Elems).GetString()) + + if self.FillWithZero { + fmt.Fprintf(buffer, " fill(%s)", self.FillValue.GetString()) + } + return buffer.String() +} diff --git a/src/parser/parser.go b/src/parser/parser.go index 38dd4754373..6e5297190db 100644 --- a/src/parser/parser.go +++ b/src/parser/parser.go @@ -5,7 +5,7 @@ package parser import "C" import ( - "common" + "bytes" "fmt" "math" "reflect" @@ -21,127 +21,19 @@ type From struct { type Operation int -type ValueType int - -const ( - ValueRegex ValueType = C.VALUE_REGEX - ValueInt = C.VALUE_INT - ValueBool = C.VALUE_BOOLEAN - ValueFloat = C.VALUE_FLOAT - ValueString = C.VALUE_STRING - ValueIntoName = C.VALUE_INTO_NAME - ValueTableName = C.VALUE_TABLE_NAME - ValueSimpleName = C.VALUE_SIMPLE_NAME - ValueDuration = C.VALUE_DURATION - ValueWildcard = C.VALUE_WILDCARD - ValueFunctionCall = C.VALUE_FUNCTION_CALL - ValueExpression = C.VALUE_EXPRESSION -) - -type Value struct { - Name string - Alias string - Type ValueType - Elems []*Value - compiledRegex *regexp.Regexp -} - -func (self *Value) IsFunctionCall() bool { - return self.Type == ValueFunctionCall -} - -func (self *Value) GetCompiledRegex() (*regexp.Regexp, bool) { - return self.compiledRegex, self.Type == ValueRegex -} - -type FromClauseType int - -const ( - FromClauseArray FromClauseType = C.FROM_ARRAY - FromClauseMerge FromClauseType = C.FROM_MERGE - FromClauseInnerJoin FromClauseType = C.FROM_INNER_JOIN -) - -type TableName struct { - Name *Value - Alias string -} - -func (self *TableName) GetAlias() string { - if self.Alias != "" { - return self.Alias - } - return self.Name.Name -} - -type FromClause struct { - Type FromClauseType - Names []*TableName -} - type IntoClause struct { Target *Value } -type GroupByClause struct { - FillWithZero bool - FillValue *Value - Elems []*Value -} - -func (self GroupByClause) GetGroupByTime() (*time.Duration, error) { - for _, groupBy := range self.Elems { - if groupBy.IsFunctionCall() { - // TODO: check the number of arguments and return an error - if len(groupBy.Elems) != 1 { - return nil, common.NewQueryError(common.WrongNumberOfArguments, "time function only accepts one argument") - } - // TODO: check the function name - // TODO: error checking - arg := groupBy.Elems[0].Name - durationInt, err := common.ParseTimeDuration(arg) - if err != nil { - return nil, common.NewQueryError(common.InvalidArgument, fmt.Sprintf("invalid argument %s to the time function", arg)) - } - duration := time.Duration(durationInt) - return &duration, nil - } - } - return nil, nil -} - -type WhereCondition struct { - isBooleanExpression bool - Left interface{} - Operation string - Right *WhereCondition -} - -func (self *WhereCondition) GetBoolExpression() (*Value, bool) { - if self.isBooleanExpression { - return self.Left.(*Value), true - } - return nil, false -} - -func (self *WhereCondition) GetLeftWhereCondition() (*WhereCondition, bool) { - if !self.isBooleanExpression { - return self.Left.(*WhereCondition), true - } - return nil, false -} - type BasicQuery struct { - queryString string - startTime time.Time - endTime time.Time + startTime time.Time + endTime time.Time } type SelectDeleteCommonQuery struct { BasicQuery FromClause *FromClause Condition *WhereCondition - endTimeSet bool } type SelectQuery struct { @@ -190,13 +82,28 @@ type Query struct { DropQuery *DropQuery } +func (self *IntoClause) GetString() string { + return self.Target.GetString() +} + func (self *Query) GetQueryString() string { + return self.commonGetQueryString(false) +} + +func (self *Query) GetQueryStringWithTimeCondition() string { + return self.commonGetQueryString(true) +} + +func (self *Query) commonGetQueryString(withTime bool) string { if self.SelectQuery != nil { + if withTime { + return self.SelectQuery.GetQueryStringWithTimeCondition() + } return self.SelectQuery.GetQueryString() } else if self.ListQuery != nil { return "list series" } else if self.DeleteQuery != nil { - return self.DeleteQuery.GetQueryString() + return self.DeleteQuery.GetQueryString(withTime) } return self.QueryString } @@ -217,8 +124,15 @@ func (self *Query) IsListContinuousQueriesQuery() bool { return self.ListQuery != nil && self.ListQuery.Type == ContinuousQueries } -func (self *BasicQuery) GetQueryString() string { - return self.queryString +func (self *DeleteQuery) GetQueryString(withTime bool) string { + buffer := bytes.NewBufferString("delete ") + fmt.Fprintf(buffer, "from %s", self.FromClause.GetString()) + if withTime { + fmt.Fprintf(buffer, " where %s", self.GetWhereConditionWithTime(self.startTime, self.endTime).GetString()) + } else if condition := self.GetWhereCondition(); condition != nil { + fmt.Fprintf(buffer, " where %s", condition.GetString()) + } + return buffer.String() } func (self *SelectQuery) GetColumnNames() []*Value { @@ -229,6 +143,53 @@ func (self *SelectQuery) IsExplainQuery() bool { return self.Explain } +func (self *SelectQuery) GetQueryString() string { + return self.commonGetQueryStringWithTimes(false, true, self.startTime, self.endTime) +} + +func (self *SelectQuery) GetQueryStringWithTimeCondition() string { + return self.commonGetQueryStringWithTimes(true, true, self.startTime, self.endTime) +} + +func (self *SelectQuery) GetQueryStringWithTimes(startTime, endTime time.Time) string { + return self.commonGetQueryStringWithTimes(true, true, startTime, endTime) +} + +func (self *SelectQuery) GetQueryStringWithTimesAndNoIntoClause(startTime, endTime time.Time) string { + return self.commonGetQueryStringWithTimes(true, false, startTime, endTime) +} + +func (self *SelectQuery) commonGetQueryStringWithTimes(withTime, withIntoClause bool, startTime, endTime time.Time) string { + buffer := bytes.NewBufferString("") + fmt.Fprintf(buffer, "select ") + + buffer.WriteString(Values(self.ColumnNames).GetString()) + + fmt.Fprintf(buffer, " from %s", self.FromClause.GetString()) + if withTime { + fmt.Fprintf(buffer, " where %s", self.GetWhereConditionWithTime(startTime, endTime).GetString()) + } else if condition := self.GetWhereCondition(); condition != nil { + fmt.Fprintf(buffer, " where %s", condition.GetString()) + } + if self.GetGroupByClause() != nil && len(self.GetGroupByClause().Elems) > 0 { + fmt.Fprintf(buffer, " group by %s", self.GetGroupByClause().GetString()) + } + + if self.Limit > 0 { + fmt.Fprintf(buffer, " limit %d", self.Limit) + } + + if self.Ascending { + fmt.Fprintf(buffer, " order asc") + } + + if clause := self.IntoClause; withIntoClause && clause != nil { + fmt.Fprintf(buffer, " into %s", clause.GetString()) + } + + return buffer.String() +} + func (self *SelectQuery) IsSinglePointQuery() bool { w := self.GetWhereCondition() if w == nil { @@ -482,6 +443,47 @@ func (self *SelectDeleteCommonQuery) GetWhereCondition() *WhereCondition { return self.Condition } +func (self *SelectDeleteCommonQuery) GetWhereConditionWithTime(startTime, endTime time.Time) *WhereCondition { + timeCondition := &WhereCondition{ + isBooleanExpression: false, + Operation: "AND", + Left: &WhereCondition{ + isBooleanExpression: true, + Left: &Value{ + Name: "<", + Type: ValueExpression, + Elems: []*Value{ + &Value{Name: "time", Type: ValueSimpleName}, + &Value{Name: strconv.FormatInt(endTime.UnixNano(), 10), Type: ValueInt}, + }, + }, + }, + Right: &WhereCondition{ + isBooleanExpression: true, + Left: &Value{ + Name: ">", + Type: ValueExpression, + Elems: []*Value{ + &Value{Name: "time", Type: ValueSimpleName}, + &Value{Name: strconv.FormatInt(startTime.UnixNano(), 10), Type: ValueInt}, + }, + }, + }, + } + + if self.Condition == nil { + return timeCondition + } + + return &WhereCondition{ + isBooleanExpression: false, + Left: self.Condition, + Right: timeCondition, + Operation: "AND", + } + +} + func (self *SelectQuery) GetGroupByClause() *GroupByClause { return self.groupByClause } @@ -515,7 +517,6 @@ func ParseQuery(query string) ([]*Query, error) { if q.error != nil { str := C.GoString(q.error.err) return nil, &QueryError{ - queryString: query, firstLine: int(q.error.first_line), firstColumn: int(q.error.first_column) - 1, lastLine: int(q.error.last_line), @@ -533,14 +534,14 @@ func ParseQuery(query string) ([]*Query, error) { } if q.select_query != nil { - selectQuery, err := parseSelectQuery(query, q.select_query) + selectQuery, err := parseSelectQuery(q.select_query) if err != nil { return nil, err } return []*Query{&Query{QueryString: query, SelectQuery: selectQuery}}, nil } else if q.delete_query != nil { - deleteQuery, err := parseDeleteQuery(query, q.delete_query) + deleteQuery, err := parseDeleteQuery(q.delete_query) if err != nil { return nil, err } @@ -568,13 +569,12 @@ func parseDropSeriesQuery(queryStirng string, dropSeriesQuery *C.drop_series_que }, nil } -func parseSelectDeleteCommonQuery(queryString string, fromClause *C.from_clause, whereCondition *C.condition) (SelectDeleteCommonQuery, error) { +func parseSelectDeleteCommonQuery(fromClause *C.from_clause, whereCondition *C.condition) (SelectDeleteCommonQuery, error) { goQuery := SelectDeleteCommonQuery{ BasicQuery: BasicQuery{ - queryString: queryString, - startTime: time.Unix(math.MinInt64/1000000, 0).UTC(), - endTime: time.Now().UTC(), + startTime: time.Unix(math.MinInt64/1000000000, 0).UTC(), + endTime: time.Now().UTC(), }, } @@ -601,7 +601,6 @@ func parseSelectDeleteCommonQuery(queryString string, fromClause *C.from_clause, } if endTime != nil { - goQuery.endTimeSet = true goQuery.endTime = *endTime } @@ -617,14 +616,14 @@ func parseSelectDeleteCommonQuery(queryString string, fromClause *C.from_clause, return goQuery, nil } -func parseSelectQuery(queryString string, q *C.select_query) (*SelectQuery, error) { +func parseSelectQuery(q *C.select_query) (*SelectQuery, error) { limit := q.limit if limit == -1 { // no limit by default limit = 0 } - basicQuery, err := parseSelectDeleteCommonQuery(queryString, q.from_clause, q.where_condition) + basicQuery, err := parseSelectDeleteCommonQuery(q.from_clause, q.where_condition) if err != nil { return nil, err } @@ -661,8 +660,8 @@ func parseSelectQuery(queryString string, q *C.select_query) (*SelectQuery, erro return goQuery, nil } -func parseDeleteQuery(queryString string, query *C.delete_query) (*DeleteQuery, error) { - basicQuery, err := parseSelectDeleteCommonQuery(queryString, query.from_clause, query.where_condition) +func parseDeleteQuery(query *C.delete_query) (*DeleteQuery, error) { + basicQuery, err := parseSelectDeleteCommonQuery(query.from_clause, query.where_condition) if err != nil { return nil, err } diff --git a/src/parser/parser_test.go b/src/parser/parser_test.go index 4ca7b912c11..090840cb77b 100644 --- a/src/parser/parser_test.go +++ b/src/parser/parser_test.go @@ -57,8 +57,6 @@ func (self *QueryParserSuite) TestParseBasicSelectQuery(c *C) { q, err := ParseSelectQuery(query) c.Assert(err, IsNil) - c.Assert(q.GetQueryString(), Equals, query) - c.Assert(q.GetColumnNames(), DeepEquals, ToValueArray("value")) w := q.GetWhereCondition() @@ -74,6 +72,34 @@ func (self *QueryParserSuite) TestParseBasicSelectQuery(c *C) { } } +func (self *QueryParserSuite) TestGetQueryString(c *C) { + for _, query := range []string{ + "select value from t", + "select value from t where c = '5'", + "select value from t where c = '5' limit 1", + "select value from t where c = '5' limit 1 order asc", + "select a.value, b.value from foo as a inner join bar as b where c = '5' limit 1 order asc", + "select count(value) from t group by time(1h)", + "select count(value) from t group by time(1h) into value.hourly", + "select count(value), host from t group by time(1h), host into value.hourly.[:host]", + "delete from foo", + } { + fmt.Printf("testing %s\n", query) + expectedQuery, err := ParseQuery(query) + c.Assert(err, IsNil) + c.Assert(expectedQuery, HasLen, 1) + queryString := expectedQuery[0].GetQueryStringWithTimeCondition() + fmt.Printf("query string: %s\n", queryString) + actualQuery, err := ParseQuery(queryString) + c.Assert(err, IsNil) + c.Assert(actualQuery, HasLen, 1) + expectedQuery[0].QueryString = "" + actualQuery[0].QueryString = "" + + c.Assert(actualQuery[0], DeepEquals, expectedQuery[0]) + } +} + func (self *QueryParserSuite) TestParseDeleteQueryWithEndTime(c *C) { query := "delete from foo where time < 1389040522000000u" queries, err := ParseQuery(query) @@ -164,11 +190,7 @@ func (self *QueryParserSuite) TestGetQueryStringForContinuousQuery(c *C) { start := base.UTC() end := base.Add(time.Minute).UTC() - startMicroseconds := common.TimeToMicroseconds(start.UTC()) - 1 - endMicroseconds := common.TimeToMicroseconds(end.UTC()) - inputQuery := "select count(c1) from s1 group by time(1m) into d1;" - outputQuery := fmt.Sprintf("select count(c1) from s1 group by time(1m) where time > %du and time < %du", startMicroseconds, endMicroseconds) queries, err := ParseQuery(inputQuery) c.Assert(err, IsNil) @@ -178,10 +200,9 @@ func (self *QueryParserSuite) TestGetQueryStringForContinuousQuery(c *C) { c.Assert(query.SelectQuery, NotNil) selectQuery := query.SelectQuery - c.Assert(selectQuery.GetQueryStringForContinuousQuery(start, end), Equals, outputQuery) // try to parse the query with the time condition - queries, err = ParseQuery(selectQuery.GetQueryStringForContinuousQuery(start, end)) + queries, err = ParseQuery(selectQuery.GetQueryStringWithTimesAndNoIntoClause(start, end)) c.Assert(err, IsNil) query = queries[0] @@ -205,8 +226,6 @@ func (self *QueryParserSuite) TestParseDeleteQuery(c *C) { q := _q.DeleteQuery - c.Assert(q.GetQueryString(), Equals, query) - startTime, _ := time.Parse("2006-01-02", "2012-08-13") endTime, _ := time.Parse("2006-01-02", "2013-08-13") c.Assert(q.GetStartTime(), Equals, startTime) diff --git a/src/parser/query_api.go b/src/parser/query_api.go index 36c1dbee286..75ab6690e9e 100644 --- a/src/parser/query_api.go +++ b/src/parser/query_api.go @@ -236,11 +236,28 @@ func parseTimeString(t string) (*time.Time, error) { return &_t, err } +func parseTimeWithoutSuffix(value string) (int64, error) { + var err error + var f float64 + var i int64 + if strings.Contains(value, ".") { + f, err = strconv.ParseFloat(value, 64) + i = int64(f) + } else { + fmt.Printf("here %v\n", value) + i, err = strconv.ParseInt(value, 10, 64) + } + if err != nil { + return 0, err + } + return i, nil +} + // parse time expressions, e.g. now() - 1d func parseTime(value *Value) (int64, error) { if value.Type != ValueExpression { if value.IsFunctionCall() && strings.ToLower(value.Name) == "now" { - return time.Now().UnixNano(), nil + return time.Now().UTC().UnixNano(), nil } if value.IsFunctionCall() { @@ -322,51 +339,6 @@ func isNumericValue(value *Value) bool { } } -func (self *SelectDeleteCommonQuery) GetQueryStringWithTimeCondition() string { - queryString := self.GetQueryString() - - if self.endTimeSet { - return queryString - } - - t := common.TimeToMicroseconds(self.GetEndTime()) - timeStr := strconv.FormatInt(t, 10) - - condition := self.GetWhereCondition() - if condition == nil { - return queryString + " where time < " + timeStr + "u" - } - - return queryString + " and time < " + timeStr + "u" -} - -func (self *SelectDeleteCommonQuery) GetQueryStringForContinuousQuery(start, end time.Time) string { - queryString := self.GetQueryString() - queryString = strings.TrimSuffix(queryString, ";") - - intoRegex, _ := regexp.Compile("(?i)\\s+into\\s+") - components := intoRegex.Split(queryString, 2) - - queryString = components[0] - - startTime := common.TimeToMicroseconds(start) - startTimeStr := strconv.FormatInt(startTime-1, 10) - endTime := common.TimeToMicroseconds(end) - endTimeStr := strconv.FormatInt(endTime, 10) - - if self.GetWhereCondition() == nil { - queryString = queryString + " where " - } else { - queryString = queryString + " and " - } - - if start.IsZero() { - return queryString + "time < " + endTimeStr + "u" - } else { - return queryString + "time > " + startTimeStr + "u and time < " + endTimeStr + "u" - } -} - // parse the start time or end time from the where conditions and return the new condition // without the time clauses, or nil if there are no where conditions left func getTime(condition *WhereCondition, isParsingStartTime bool) (*WhereCondition, *time.Time, error) { diff --git a/src/parser/query_api_test.go b/src/parser/query_api_test.go index 7993b5f94e8..8ce87a3adde 100644 --- a/src/parser/query_api_test.go +++ b/src/parser/query_api_test.go @@ -187,8 +187,8 @@ func (self *QueryApiSuite) TestDefaultLimit(c *C) { func (self *QueryApiSuite) TestDefaultStartTime(c *C) { for queryStr, t := range map[string]time.Time{ - "select * from t where time < now() - 1d;": time.Unix(math.MinInt64/1000/1000, 0).UTC(), - "select * from t;": time.Unix(math.MinInt64/1000/1000, 0).UTC(), + "select * from t where time < now() - 1d;": time.Unix(math.MinInt64/1000000000, 0).UTC(), + "select * from t;": time.Unix(math.MinInt64/1000000000, 0).UTC(), } { query, err := ParseSelectQuery(queryStr) c.Assert(err, IsNil) diff --git a/src/parser/query_spec.go b/src/parser/query_spec.go index 7fedd1984f9..4ed1144061a 100644 --- a/src/parser/query_spec.go +++ b/src/parser/query_spec.go @@ -163,12 +163,7 @@ func (self *QuerySpec) GetQueryString() string { } func (self *QuerySpec) GetQueryStringWithTimeCondition() string { - if self.query.SelectQuery != nil { - return self.query.SelectQuery.GetQueryStringWithTimeCondition() - } else if self.query.DeleteQuery != nil { - return self.query.DeleteQuery.GetQueryStringWithTimeCondition() - } - return self.query.GetQueryString() + return self.query.GetQueryStringWithTimeCondition() } func (self *QuerySpec) IsDropSeriesQuery() bool { diff --git a/src/parser/value.go b/src/parser/value.go new file mode 100644 index 00000000000..07e3a98f950 --- /dev/null +++ b/src/parser/value.go @@ -0,0 +1,64 @@ +package parser + +// #include "query_types.h" +// #include +import "C" + +import ( + "bytes" + "fmt" + "regexp" +) + +type ValueType int + +const ( + ValueRegex ValueType = C.VALUE_REGEX + ValueInt = C.VALUE_INT + ValueBool = C.VALUE_BOOLEAN + ValueFloat = C.VALUE_FLOAT + ValueString = C.VALUE_STRING + ValueIntoName = C.VALUE_INTO_NAME + ValueTableName = C.VALUE_TABLE_NAME + ValueSimpleName = C.VALUE_SIMPLE_NAME + ValueDuration = C.VALUE_DURATION + ValueWildcard = C.VALUE_WILDCARD + ValueFunctionCall = C.VALUE_FUNCTION_CALL + ValueExpression = C.VALUE_EXPRESSION +) + +type Value struct { + Name string + Alias string + Type ValueType + Elems []*Value + compiledRegex *regexp.Regexp +} + +func (self *Value) IsFunctionCall() bool { + return self.Type == ValueFunctionCall +} + +func (self *Value) GetCompiledRegex() (*regexp.Regexp, bool) { + return self.compiledRegex, self.Type == ValueRegex +} + +func (self *Value) GetString() string { + buffer := bytes.NewBufferString("") + switch self.Type { + case ValueExpression: + fmt.Fprintf(buffer, "%s %s %s", self.Elems[0].GetString(), self.Name, self.Elems[1].GetString()) + case ValueFunctionCall: + fmt.Fprintf(buffer, "%s(%s)", self.Name, Values(self.Elems).GetString()) + case ValueString: + fmt.Fprintf(buffer, "'%s'", self.Name) + default: + buffer.WriteString(self.Name) + } + + if self.Alias != "" { + fmt.Fprintf(buffer, " as %s", self.Alias) + } + + return buffer.String() +} diff --git a/src/parser/values.go b/src/parser/values.go new file mode 100644 index 00000000000..f84b71d3390 --- /dev/null +++ b/src/parser/values.go @@ -0,0 +1,13 @@ +package parser + +import "strings" + +type Values []*Value + +func (self Values) GetString() string { + names := make([]string, 0, len(self)) + for _, name := range self { + names = append(names, name.GetString()) + } + return strings.Join(names, ",") +} diff --git a/src/parser/where_condition.go b/src/parser/where_condition.go new file mode 100644 index 00000000000..6ecb41725a6 --- /dev/null +++ b/src/parser/where_condition.go @@ -0,0 +1,34 @@ +package parser + +import ( + "fmt" +) + +type WhereCondition struct { + isBooleanExpression bool + Left interface{} + Operation string + Right *WhereCondition +} + +func (self *WhereCondition) GetBoolExpression() (*Value, bool) { + if self.isBooleanExpression { + return self.Left.(*Value), true + } + return nil, false +} + +func (self *WhereCondition) GetLeftWhereCondition() (*WhereCondition, bool) { + if !self.isBooleanExpression { + return self.Left.(*WhereCondition), true + } + return nil, false +} + +func (self *WhereCondition) GetString() string { + if expr, ok := self.GetBoolExpression(); ok { + return expr.GetString() + } + + return fmt.Sprintf("(%s) %s (%s)", self.Left.(*WhereCondition).GetString(), self.Operation, self.Right.GetString()) +} From ff247d99b4d62a308d1c253b2590d4d67d5c35ac Mon Sep 17 00:00:00 2001 From: John Shahid Date: Wed, 16 Apr 2014 14:46:21 -0400 Subject: [PATCH 2/4] remove an unnecessary test --- src/parser/parser_test.go | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/src/parser/parser_test.go b/src/parser/parser_test.go index 090840cb77b..50bed538212 100644 --- a/src/parser/parser_test.go +++ b/src/parser/parser_test.go @@ -1,7 +1,6 @@ package parser import ( - "common" "fmt" "testing" "time" @@ -150,41 +149,6 @@ func (self *QueryParserSuite) TestParseDropSeries(c *C) { c.Assert(q.GetTableName(), Equals, "foobar") } -func (self *QueryParserSuite) TestGetQueryStringWithTimeCondition(c *C) { - now := time.Now().Round(time.Minute).UTC() - micros := common.TimeToMicroseconds(now) - - for _, q := range []string{ - "delete from foo", - fmt.Sprintf("delete from foo where time < %du", micros), - } { - fmt.Printf("testing %s\n", q) - - queries, err := ParseQuery(q) - c.Assert(err, IsNil) - - c.Assert(queries, HasLen, 1) - - _q := queries[0] - - c.Assert(_q.DeleteQuery, NotNil) - - q := _q.DeleteQuery - - // try to parse the query with the time condition - queries, err = ParseQuery(q.GetQueryStringWithTimeCondition()) - fmt.Printf("query: %s\n", q.GetQueryStringWithTimeCondition()) - c.Assert(err, IsNil) - - _q = queries[0] - c.Assert(_q.DeleteQuery, NotNil) - - q = _q.DeleteQuery - - c.Assert(q.GetEndTime().Round(time.Minute), Equals, now) - } -} - func (self *QueryParserSuite) TestGetQueryStringForContinuousQuery(c *C) { base := time.Now().Truncate(time.Minute) start := base.UTC() From 915084ac1234a8dc5539b0d8ae71f71a17f511a4 Mon Sep 17 00:00:00 2001 From: John Shahid Date: Tue, 15 Apr 2014 20:07:13 -0400 Subject: [PATCH 3/4] take a shortcut if we get an integer value --- src/common/helpers.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/helpers.go b/src/common/helpers.go index ba0af8e6e01..e11c469ecbd 100644 --- a/src/common/helpers.go +++ b/src/common/helpers.go @@ -6,6 +6,7 @@ import ( "os" "protocol" "strconv" + "strings" "time" "unicode" ) @@ -13,6 +14,11 @@ import ( // Returns the parsed duration in nanoseconds, support 'u', 's', 'm', // 'h', 'd' and 'w' suffixes. func ParseTimeDuration(value string) (int64, error) { + // shortcut for nanoseconds + if idx := strings.IndexFunc(value, func(r rune) bool { return !unicode.IsNumber(r) }); idx == -1 { + return strconv.ParseInt(value, 10, 64) + } + parsedFloat, err := strconv.ParseFloat(value[:len(value)-1], 64) if err != nil { return 0, err From d61eb7fdd09b245a99f8e0fabb014e6567709f9f Mon Sep 17 00:00:00 2001 From: John Shahid Date: Wed, 16 Apr 2014 13:12:52 -0400 Subject: [PATCH 4/4] remove the stupid pprof that was in the WAL test suite --- src/wal/wal_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/wal/wal_test.go b/src/wal/wal_test.go index 57cb3824974..52b119bc1e2 100644 --- a/src/wal/wal_test.go +++ b/src/wal/wal_test.go @@ -5,8 +5,6 @@ import ( "configuration" "fmt" "math" - "net/http" - _ "net/http/pprof" "os" "path" "protocol" @@ -30,10 +28,6 @@ func (_ *WalSuite) SetUpSuite(c *C) { for _, filter := range logger.Global { filter.Level = logger.INFO } - - go func() { - panic(http.ListenAndServe("localhost:6060", nil)) - }() } func generateSeries(numberOfPoints int) *protocol.Series {