diff options
| author | Paul Bütow <pbuetow@mimecast.com> | 2020-01-20 18:41:05 +0000 |
|---|---|---|
| committer | Paul Bütow <pbuetow@mimecast.com> | 2020-01-21 14:35:23 +0000 |
| commit | c128865c4c7411c29a59fca9a3a2f95537686d7b (patch) | |
| tree | 193bccc70d942c8b70cc93fae2670263701e43aa /mapr | |
| parent | 3755a9911ecb05886577095f2b8cc8b9e4066a3a (diff) | |
Move commands to cmd/ and move internal dependencies to internal/
Diffstat (limited to 'mapr')
| -rw-r--r-- | mapr/aggregateset.go | 185 | ||||
| -rw-r--r-- | mapr/client/aggregate.go | 100 | ||||
| -rw-r--r-- | mapr/globalgroupset.go | 100 | ||||
| -rw-r--r-- | mapr/groupset.go | 178 | ||||
| -rw-r--r-- | mapr/logformat/default.go | 23 | ||||
| -rw-r--r-- | mapr/logformat/default_test.go | 35 | ||||
| -rw-r--r-- | mapr/logformat/parser.go | 75 | ||||
| -rw-r--r-- | mapr/query.go | 245 | ||||
| -rw-r--r-- | mapr/query_test.go | 149 | ||||
| -rw-r--r-- | mapr/selectcondition.go | 96 | ||||
| -rw-r--r-- | mapr/server/aggregate.go | 170 | ||||
| -rw-r--r-- | mapr/token.go | 108 | ||||
| -rw-r--r-- | mapr/wherecondition.go | 193 |
13 files changed, 0 insertions, 1657 deletions
diff --git a/mapr/aggregateset.go b/mapr/aggregateset.go deleted file mode 100644 index 2096c3c..0000000 --- a/mapr/aggregateset.go +++ /dev/null @@ -1,185 +0,0 @@ -package mapr - -import ( - "fmt" - "strconv" - "strings" -) - -// AggregateSet represents aggregated key/value pairs from the -// MAPREDUCE log lines. These could be either string values or float -// values. -type AggregateSet struct { - Samples int - FValues map[string]float64 - SValues map[string]string -} - -// NewAggregateSet creates a new empty aggregate set. -func NewAggregateSet() *AggregateSet { - return &AggregateSet{ - FValues: make(map[string]float64), - SValues: make(map[string]string), - } -} - -// String representation of aggregate set. -func (s *AggregateSet) String() string { - return fmt.Sprintf("AggregateSet(Samples:%d,FValues:%v,SValues:%v)", - s.Samples, s.FValues, s.SValues) -} - -// Merge one aggregate set into this one. -func (s *AggregateSet) Merge(query *Query, set *AggregateSet) error { - s.Samples += set.Samples - //logger.Trace("Merge", set) - - for _, sc := range query.Select { - storage := sc.FieldStorage - switch sc.Operation { - case Count: - fallthrough - case Sum: - fallthrough - case Avg: - value := set.FValues[storage] - s.addFloat(storage, value) - case Min: - value := set.FValues[storage] - s.addFloatMin(storage, value) - case Max: - value := set.FValues[storage] - s.addFloatMax(storage, value) - case Last: - value := set.SValues[storage] - s.setString(storage, value) - case Len: - s.setString(storage, set.SValues[storage]) - s.setFloat(storage, set.FValues[storage]) - default: - return fmt.Errorf("Unknown aggregation method '%v'", sc.Operation) - } - } - return nil -} - -// Serialize the aggregate set so it can be sent over the wire. -func (s *AggregateSet) Serialize(groupKey string, ch chan<- string, stop chan struct{}) { - //logger.Trace("Serialising mapr.AggregateSet", s) - var sb strings.Builder - - sb.WriteString(groupKey) - sb.WriteString("|") - sb.WriteString(fmt.Sprintf("%d|", s.Samples)) - - for k, v := range s.FValues { - sb.WriteString(k) - sb.WriteString("=") - sb.WriteString(fmt.Sprintf("%v|", v)) - } - - for k, v := range s.SValues { - sb.WriteString(k) - sb.WriteString("=") - sb.WriteString(v) - sb.WriteString("|") - } - - select { - case ch <- sb.String(): - case <-stop: - } -} - -// Add a float value. -func (s *AggregateSet) addFloat(key string, value float64) { - if _, ok := s.FValues[key]; !ok { - s.FValues[key] = value - return - } - s.FValues[key] += value -} - -// Add a float minimum value. -func (s *AggregateSet) addFloatMin(key string, value float64) { - f, ok := s.FValues[key] - if !ok { - s.FValues[key] = value - return - } - - if f > value { - s.FValues[key] = value - } -} - -// Add a float maximum value. -func (s *AggregateSet) addFloatMax(key string, value float64) { - f, ok := s.FValues[key] - if !ok { - s.FValues[key] = value - return - } - - if f < value { - s.FValues[key] = value - } -} - -// Set a string. -func (s *AggregateSet) setString(key, value string) { - s.SValues[key] = value -} - -// Set a float. -func (s *AggregateSet) setFloat(key string, value float64) { - s.FValues[key] = value -} - -// Aggregate data to the aggregate set. -func (s *AggregateSet) Aggregate(key string, agg AggregateOperation, value string, clientAggregation bool) (err error) { - var f float64 - - // First check if we can aggregate anything without converting value to float. - switch agg { - case Count: - if clientAggregation { - f, err = strconv.ParseFloat(value, 64) - if err != nil { - return - } - s.addFloat(key, f) - return - } - s.addFloat(key, 1) - return - case Last: - s.setString(key, value) - return - case Len: - s.setString(key, value) - s.setFloat(key, float64(len(value))) - return - default: - } - - // No, we have to convert to float. - f, err = strconv.ParseFloat(value, 64) - if err != nil { - return - } - - switch agg { - case Sum: - fallthrough - case Avg: - s.addFloat(key, f) - case Min: - s.addFloatMin(key, f) - case Max: - s.addFloatMax(key, f) - default: - err = fmt.Errorf("Unknown aggregation method '%v'", agg) - } - return -} diff --git a/mapr/client/aggregate.go b/mapr/client/aggregate.go deleted file mode 100644 index b9443bc..0000000 --- a/mapr/client/aggregate.go +++ /dev/null @@ -1,100 +0,0 @@ -package client - -import ( - "dtail/logger" - "dtail/mapr" - "strconv" - "strings" -) - -// Aggregate mapreduce data on the DTail client side. -type Aggregate struct { - // This is the mapr query specified on the command line. - query *mapr.Query - // This represents aggregated data of a single remote server. - group *mapr.GroupSet - // This represents the merged aggregated data of all servers. - globalGroup *mapr.GlobalGroupSet - stop chan struct{} - // The server we aggregate the data for (logging and debugging purposes only) - server string -} - -// NewAggregate create new client aggregator. -func NewAggregate(server string, query *mapr.Query, globalGroup *mapr.GlobalGroupSet) *Aggregate { - return &Aggregate{ - query: query, - group: mapr.NewGroupSet(), - globalGroup: globalGroup, - stop: make(chan struct{}), - server: server, - } -} - -// Aggregate data from mapr log line into local (and global) group sets. -func (a *Aggregate) Aggregate(parts []string) { - select { - case <-a.stop: - logger.Error("Client aggregator stopped for server, not processing new data", a.server) - return - default: - } - - groupKey := parts[0] - samples, err := strconv.Atoi(parts[1]) - if err != nil { - logger.FatalExit(parts, err) - } - fields := a.makeFields(parts[2:]) - set := a.group.GetSet(groupKey) - - var addedSamples bool - for _, sc := range a.query.Select { - if val, ok := fields[sc.FieldStorage]; ok { - if err := set.Aggregate(sc.FieldStorage, sc.Operation, val, true); err != nil { - logger.Error(err) - continue - } - addedSamples = true - } - } - if addedSamples { - set.Samples += samples - } - - // Merge data from group into global group. - isMerged, err := a.globalGroup.MergeNoblock(a.query, a.group) - if err != nil { - panic(err) - } - if isMerged { - // Re-init local group (make it empty again). - a.group.InitSet() - } -} - -// Create a map of key-value pairs from a part list such as ["foo=bar", "bar=baz"]. -func (a *Aggregate) makeFields(parts []string) map[string]string { - fields := make(map[string]string, len(parts)) - - for _, part := range parts { - kv := strings.Split(part, "=") - if len(kv) != 2 { - continue - } - fields[kv[0]] = kv[1] - } - - return fields -} - -// Stop the client side mapreduce aggregator. -func (a *Aggregate) Stop() { - logger.Debug("Stopping client mapreduce aggregator") - close(a.stop) - - err := a.globalGroup.Merge(a.query, a.group) - if err != nil { - panic(err) - } -} diff --git a/mapr/globalgroupset.go b/mapr/globalgroupset.go deleted file mode 100644 index cfab506..0000000 --- a/mapr/globalgroupset.go +++ /dev/null @@ -1,100 +0,0 @@ -package mapr - -import ( - "fmt" -) - -// GlobalGroupSet is used on the dtail client to merge multiple group sets -// (one group set per remote server) to one single global group set. -type GlobalGroupSet struct { - GroupSet - semaphore chan struct{} -} - -// NewGlobalGroupSet creates a new empty global group set. -func NewGlobalGroupSet() *GlobalGroupSet { - g := GlobalGroupSet{ - semaphore: make(chan struct{}, 1), - } - g.InitSet() - - return &g -} - -// String representation of the global group set. -func (g *GlobalGroupSet) String() string { - return fmt.Sprintf("GlobalGroupSet(%s)", g.GroupSet.String()) -} - -// Merge (blocking) a group set into the global group set. -func (g *GlobalGroupSet) Merge(query *Query, group *GroupSet) error { - g.semaphore <- struct{}{} - defer func() { <-g.semaphore }() - - return g.merge(query, group) -} - -// MergeNoblock merges (non-blocking) a group set into the global group set. -func (g *GlobalGroupSet) MergeNoblock(query *Query, group *GroupSet) (bool, error) { - select { - case g.semaphore <- struct{}{}: - err := g.merge(query, group) - <-g.semaphore - return true, err - default: - return false, nil - } -} - -// Merge a group set into the global group set. -func (g *GlobalGroupSet) merge(query *Query, group *GroupSet) error { - for groupKey, set := range group.sets { - s := g.GetSet(groupKey) - if err := s.Merge(query, set); err != nil { - return err - } - } - - return nil -} - -// IsEmpty determines whether the global group set has any data in it. -func (g *GlobalGroupSet) IsEmpty() bool { - return g.NumSets() == 0 -} - -// NumSets determines the number of sets. -func (g *GlobalGroupSet) NumSets() int { - g.semaphore <- struct{}{} - defer func() { <-g.semaphore }() - - return len(g.sets) -} - -// SwapOut teturn the underlying group set and create a new empty one, so -// that the global group set is empty again and can aggregate new data. -func (g *GlobalGroupSet) SwapOut() *GroupSet { - g.semaphore <- struct{}{} - defer func() { <-g.semaphore }() - - set := &GroupSet{sets: g.sets} - g.InitSet() - - return set -} - -// WriteResult writes the result of a mapreduce aggregation to an outfile. -func (g *GlobalGroupSet) WriteResult(query *Query) error { - g.semaphore <- struct{}{} - defer func() { <-g.semaphore }() - - return g.GroupSet.WriteResult(query) -} - -// Result returns the result of the mapreduce aggregation as a string. -func (g *GlobalGroupSet) Result(query *Query) (string, int, error) { - g.semaphore <- struct{}{} - defer func() { <-g.semaphore }() - - return g.GroupSet.Result(query) -} diff --git a/mapr/groupset.go b/mapr/groupset.go deleted file mode 100644 index d8f9379..0000000 --- a/mapr/groupset.go +++ /dev/null @@ -1,178 +0,0 @@ -package mapr - -import ( - "errors" - "fmt" - "io/ioutil" - "sort" - "strconv" - "strings" -) - -// GroupSet represents a map of aggregate sets. The group sets -// are requierd by the "group by" mapr clause, whereas the -// group set map keys are the values of the "group by" arguments. -// E.g. "group by $cid" would create one aggregate set and one map -// entry per customer id. -type GroupSet struct { - sets map[string]*AggregateSet -} - -// NewGroupSet returns a new empty group set. -func NewGroupSet() *GroupSet { - g := GroupSet{} - g.InitSet() - return &g -} - -// String representation of the group set. -func (g *GroupSet) String() string { - return fmt.Sprintf("GroupSet(%v)", g.sets) -} - -// InitSet makes the group set empty (initialize). -func (g *GroupSet) InitSet() { - g.sets = make(map[string]*AggregateSet) -} - -// GetSet gets a specific aggregate set from the group set. -func (g *GroupSet) GetSet(groupKey string) *AggregateSet { - set, ok := g.sets[groupKey] - if !ok { - set = NewAggregateSet() - g.sets[groupKey] = set - } - return set -} - -// Serialize the group set (e.g. to send it over the wire). -func (g *GroupSet) Serialize(ch chan<- string, stop chan struct{}) { - for groupKey, set := range g.sets { - set.Serialize(groupKey, ch, stop) - } -} - -// Result returns a nicely formated result of the query from the group set. -func (g *GroupSet) Result(query *Query) (string, int, error) { - return g.limitedResult(query, query.Limit, "\t", " ", false) -} - -// WriteResult writes the result to an outfile. -func (g *GroupSet) WriteResult(query *Query) error { - if query.Outfile == "" { - return errors.New("No outfile specified") - } - - // -1: Don't limit the result, include all data sets - result, _, err := g.limitedResult(query, -1, "", ",", true) - if err != nil { - return err - } - - return ioutil.WriteFile(query.Outfile, []byte(result), 0644) -} - -// Return a nicely formated result of the query from the group set. -func (g *GroupSet) limitedResult(query *Query, limit int, lineStarter, fieldSeparator string, addHeader bool) (string, int, error) { - type result struct { - groupKey string - resultStr string - orderBy float64 - } - - var resultSlice []result - - for groupKey, set := range g.sets { - var sb strings.Builder - r := result{groupKey: groupKey} - - lastIndex := len(query.Select) - 1 - for i, sc := range query.Select { - storage := sc.FieldStorage - orderByThis := storage == query.OrderBy - - switch sc.Operation { - case Count: - value := set.FValues[storage] - sb.WriteString(fmt.Sprintf("%d", int(value))) - if orderByThis { - r.orderBy = value - } - case Len: - fallthrough - case Sum: - fallthrough - case Min: - fallthrough - case Max: - value := set.FValues[storage] - sb.WriteString(fmt.Sprintf("%f", value)) - if orderByThis { - r.orderBy = value - } - case Last: - value := set.SValues[storage] - if orderByThis { - f, err := strconv.ParseFloat(value, 64) - if err == nil { - r.orderBy = f - } - } - sb.WriteString(value) - case Avg: - value := set.FValues[storage] / float64(set.Samples) - sb.WriteString(fmt.Sprintf("%f", value)) - if orderByThis { - r.orderBy = value - } - default: - return "", 0, fmt.Errorf("Unknown aggregation method '%v'", sc.Operation) - } - if i != lastIndex { - sb.WriteString(fieldSeparator) - } - } - - r.resultStr = sb.String() - resultSlice = append(resultSlice, r) - } - - if query.OrderBy != "" { - if query.ReverseOrder { - sort.SliceStable(resultSlice, func(i, j int) bool { - return resultSlice[i].orderBy < resultSlice[j].orderBy - }) - } else { - sort.SliceStable(resultSlice, func(i, j int) bool { - return resultSlice[i].orderBy > resultSlice[j].orderBy - }) - } - } - - var sb strings.Builder - - // Write header first - if addHeader { - lastIndex := len(query.Select) - 1 - sb.WriteString(lineStarter) - for i, sc := range query.Select { - sb.WriteString(sc.FieldStorage) - if i != lastIndex { - sb.WriteString(fieldSeparator) - } - } - sb.WriteString("\n") - } - - // And now write the data - for i, r := range resultSlice { - if i == limit { - break - } - sb.WriteString(lineStarter) - sb.WriteString(r.resultStr) - sb.WriteString("\n") - } - - return sb.String(), len(resultSlice), nil -} diff --git a/mapr/logformat/default.go b/mapr/logformat/default.go deleted file mode 100644 index f0df5bc..0000000 --- a/mapr/logformat/default.go +++ /dev/null @@ -1,23 +0,0 @@ -package logformat - -import ( - "errors" - "strings" -) - -// MakeFieldsDEFAULT is the default log file mapreduce parser. -func (p *Parser) MakeFieldsDEFAULT(maprLine string) (map[string]string, error) { - fields := make(map[string]string, 20) - splitted := strings.Split(maprLine, "|") - - fields["$hostname"] = p.hostname - - for _, kv := range splitted { - keyAndValue := strings.SplitN(kv, "=", 2) - if len(keyAndValue) != 2 { - return fields, errors.New("Error parsing mapr token: " + kv) - } - fields[strings.ToLower(keyAndValue[0])] = keyAndValue[1] - } - return fields, nil -} diff --git a/mapr/logformat/default_test.go b/mapr/logformat/default_test.go deleted file mode 100644 index a3c47fb..0000000 --- a/mapr/logformat/default_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package logformat - -import ( - "testing" -) - -func TestDefaultLogFormat(t *testing.T) { - parser, err := NewParser("default") - if err != nil { - t.Errorf("Unable to create parser: %s", err.Error()) - } - - fields, err := parser.MakeFields("foo=bar|baz=bay") - - if err != nil { - t.Errorf("Unable to parse: %s", err.Error()) - } - - if bar, ok := fields["foo"]; !ok { - t.Errorf("Expected field 'foo', but no such field there\n") - } else if bar != "bar" { - t.Errorf("Expected 'bar' stored in field 'foo', but got '%s'\n", bar) - } - - if bay, ok := fields["baz"]; !ok { - t.Errorf("Expected field 'baz', but no such field there\n") - } else if bay != "bay" { - t.Errorf("Expected 'bay' stored in field 'baz', but got '%s'\n", bay) - } - - fields, err = parser.MakeFields("foo=bar|bazbay") - if err == nil { - t.Errorf("Expected error but didn't: %s", err.Error()) - } -} diff --git a/mapr/logformat/parser.go b/mapr/logformat/parser.go deleted file mode 100644 index b7c8c5c..0000000 --- a/mapr/logformat/parser.go +++ /dev/null @@ -1,75 +0,0 @@ -package logformat - -import ( - "dtail/logger" - "errors" - "fmt" - "os" - "reflect" - "strings" -) - -// Parser is used to parse the mapreduce information from the server log files. -type Parser struct { - hostname string - logFormatName string - makeFieldsFunc reflect.Value - makeFieldsReceiver reflect.Value -} - -// NewParser returns a new log parser. -func NewParser(logFormatName string) (*Parser, error) { - hostname, err := os.Hostname() - - if err != nil { - return nil, err - } - - p := Parser{ - hostname: hostname, - } - - err = p.reflectLogFormat(logFormatName) - if err != nil { - return nil, err - } - - return &p, nil -} - -// The aim of this is that everyone can plug in their own mapr log format -// parsing method to DTail. Just add a method MakeFieldsMODULENAME to type -// Parser. Whereas MODULENAME must be a upeprcase string. -func (p *Parser) reflectLogFormat(logFormatName string) error { - methodName := fmt.Sprintf("MakeFields%s", strings.ToUpper(logFormatName)) - - rt := reflect.TypeOf(p) - method, ok := rt.MethodByName(methodName) - if !ok { - return errors.New("No such mapr log format module: " + methodName) - } - - p.makeFieldsFunc = method.Func - p.makeFieldsReceiver = reflect.ValueOf(p) - - return nil -} - -// MakeFields is for returning the fields from a given log line. -func (p *Parser) MakeFields(maprLine string) (fields map[string]string, err error) { - inputValues := []reflect.Value{p.makeFieldsReceiver, reflect.ValueOf(maprLine)} - returnValues := p.makeFieldsFunc.Call(inputValues) - - errInterface := returnValues[1].Interface() - - if errInterface == nil { - fields, err = returnValues[0].Interface().(map[string]string), nil - logger.Trace("parser.MakeFields", fields, err) - return - } - - fields, err = returnValues[0].Interface().(map[string]string), errInterface.(error) - logger.Trace("parser.MakeFields", fields, err) - - return -} diff --git a/mapr/query.go b/mapr/query.go deleted file mode 100644 index 8ed3c67..0000000 --- a/mapr/query.go +++ /dev/null @@ -1,245 +0,0 @@ -package mapr - -import ( - "dtail/logger" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -const ( - invalidQuery string = "Invalid query: " - unexpectedEnd string = "Unexpected end of query" -) - -// Query represents a parsed mapr query. -type Query struct { - Select []selectCondition - Table string - Where []whereCondition - GroupBy []string - OrderBy string - ReverseOrder bool - GroupKey string - Interval time.Duration - Limit int - Outfile string - RawQuery string - tokens []token -} - -func (q Query) String() string { - return fmt.Sprintf("Query(Select:%v,Table:%s,Where:%v,GroupBy:%v,GroupKey:%s,OrderBy:%v,ReverseOrder:%v,Interval:%v,Limit:%d,Outfile:%s,RawQuery:%s,tokens:%v)", - q.Select, - q.Table, - q.Where, - q.GroupBy, - q.GroupKey, - q.OrderBy, - q.ReverseOrder, - q.Interval, - q.Limit, - q.Outfile, - q.RawQuery, - q.tokens) -} - -// NewQuery returns a new mapreduce query. -func NewQuery(queryStr string) (*Query, error) { - if queryStr == "" { - return nil, nil - } - - tokens := tokenize(queryStr) - - q := Query{ - RawQuery: queryStr, - tokens: tokens, - Interval: time.Second * 5, - Limit: -1, - } - - err := q.parse(tokens) - - logger.Debug(q) - return &q, err -} - -func (q *Query) parse(tokens []token) error { - var found []token - var err error - - for tokens != nil && len(tokens) > 0 { - switch strings.ToLower(tokens[0].str) { - case "select": - tokens, found = tokensConsume(tokens[1:]) - q.Select, err = makeSelectConditions(found) - if err != nil { - return err - } - case "from": - tokens, found = tokensConsume(tokens[1:]) - if len(found) > 0 { - q.Table = strings.ToUpper(found[0].str) - } - case "where": - tokens, found = tokensConsume(tokens[1:]) - if q.Where, err = makeWhereConditions(found); err != nil { - return err - } - case "group": - tokens = tokensConsumeOptional(tokens[1:], "by") - if tokens == nil || len(tokens) < 1 { - return errors.New(invalidQuery + unexpectedEnd) - } - tokens, q.GroupBy = tokensConsumeStr(tokens) - q.GroupKey = strings.Join(q.GroupBy, ",") - case "rorder": - tokens = tokensConsumeOptional(tokens[1:], "by") - if tokens == nil || len(tokens) < 1 { - return errors.New(invalidQuery + unexpectedEnd) - } - tokens, found = tokensConsume(tokens) - if len(found) == 0 { - return errors.New(invalidQuery + unexpectedEnd) - } - q.OrderBy = found[0].str - q.ReverseOrder = true - case "order": - tokens = tokensConsumeOptional(tokens[1:], "by") - if tokens == nil || len(tokens) < 1 { - return errors.New(invalidQuery + unexpectedEnd) - } - tokens, found = tokensConsume(tokens) - if len(found) == 0 { - return errors.New(invalidQuery + unexpectedEnd) - } - q.OrderBy = found[0].str - case "interval": - tokens, found = tokensConsume(tokens[1:]) - if len(found) > 0 { - i, err := strconv.Atoi(found[0].str) - if err != nil { - return errors.New(invalidQuery + err.Error()) - } - q.Interval = time.Second * time.Duration(i) - } - case "limit": - tokens, found = tokensConsume(tokens[1:]) - if len(found) == 0 { - return errors.New(invalidQuery + unexpectedEnd) - } - i, err := strconv.Atoi(found[0].str) - if err != nil { - return errors.New(invalidQuery + err.Error()) - } - q.Limit = i - case "outfile": - tokens, found = tokensConsume(tokens[1:]) - if len(found) == 0 { - return errors.New(invalidQuery + unexpectedEnd) - } - q.Outfile = found[0].str - default: - return errors.New(invalidQuery + "Unexpected keyword " + tokens[0].str) - } - } - - if q.Table == "" { - return errors.New(invalidQuery + "Empty table specified in 'from' clause") - } - if len(q.Select) < 1 { - return errors.New(invalidQuery + "Expected at least one field in 'select' clause but got none") - } - if len(q.GroupBy) == 0 { - field := q.Select[0].Field - q.GroupBy = append(q.GroupBy, field) - } - - if q.OrderBy != "" { - var orderFieldIsValid bool - for _, sc := range q.Select { - if q.OrderBy == sc.FieldStorage { - orderFieldIsValid = true - break - } - } - if !orderFieldIsValid { - return errors.New(invalidQuery + fmt.Sprintf("Can not '(r)order by' '%s', must be present in 'select' clause", q.OrderBy)) - } - } - - return nil -} - -// WhereClause interprets the where clause of the mapreduce query. -func (q *Query) WhereClause(fields map[string]string) bool { - floatValue := func(str string, float float64, t whereType) (float64, bool) { - switch t { - case Float: - return float, true - case Field: - value, ok := fields[str] - if !ok { - return 0, false - } - f, err := strconv.ParseFloat(value, 64) - if err != nil { - return 0, false - } - return f, true - default: - logger.Error("Unexpected argument in 'where' clause", str, float, t) - return 0, false - } - } - - stringValue := func(str string, t whereType) (string, bool) { - switch t { - case Field: - value, ok := fields[str] - if !ok { - return str, false - } - return value, true - case String: - return str, true - default: - logger.Error("Unexpected argument in 'where' clause", str, t) - return str, false - } - } - - for _, wc := range q.Where { - var ok bool - - if wc.Operation > FloatOperation { - var lValue, rValue float64 - if lValue, ok = floatValue(wc.lString, wc.lFloat, wc.lType); !ok { - return false - } - if rValue, ok = floatValue(wc.rString, wc.rFloat, wc.rType); !ok { - return false - } - if ok = wc.floatClause(lValue, rValue); !ok { - return false - } - continue - } - - var lValue, rValue string - if lValue, ok = stringValue(wc.lString, wc.lType); !ok { - return false - } - if rValue, ok = stringValue(wc.rString, wc.rType); !ok { - return false - } - if ok = wc.stringClause(lValue, rValue); !ok { - return false - } - } - - return true -} diff --git a/mapr/query_test.go b/mapr/query_test.go deleted file mode 100644 index 6176461..0000000 --- a/mapr/query_test.go +++ /dev/null @@ -1,149 +0,0 @@ -package mapr - -import ( - "testing" - "time" -) - -func TestParseQuerySimple(t *testing.T) { - errorQueries := []string{ - "select", - "select foo", - "select foo from", - "select foo from bar where baz", - "select foo from bar where baz <", - "select foo from bar where baz < 100 bay eq 12 group", - "select foo from bar where baz < 100 bay eq 12 group by foo order by", - "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz order by foo limit", - } - okQueries := []string{"select foo from bar", - "select foo from bar where", - "select foo from bar where baz < 100 bay eq 12", - "select foo from bar where baz < 100, bay eq 12", - "select foo from bar where baz < 100 and bay eq 12", - "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz order by foo", - "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz order by foo limit 23", - "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz order by foo limit 23 outfile \"result.csv\"", - } - - for _, queryStr := range errorQueries { - q, err := NewQuery(queryStr) - if err == nil { - t.Errorf("Expected a parse error: %s\n%v", queryStr, q) - continue - } - } - - for _, queryStr := range okQueries { - _, err := NewQuery(queryStr) - if err != nil { - t.Errorf("%s: %s", err.Error(), queryStr) - continue - } - } -} - -func TestParseQueryDeep(t *testing.T) { - dialects := []string{ - "select s1, `from`, count(s3) from table where w1 == 2 and w2 eq \"free beer\" group by g1, g2 order by count(s3) interval 10 limit 23", - "SELECT s1, `from` COUNT(s3) FROM table WHERE w1 == 2 AND w2 eq \"free beer\" GROUP g1, g2 ORDER count(s3) INTERVAL 10 LIMIT 23", - "select s1, `from` count(s3) from table where w1 == 2 and w2 eq \"free beer\" group by g1, g2 order by count(s3) interval 10 limit 23", - "sElEct s1, `from` coUnt(s3) from taBle where w1 == 2 aNd w2 eq \"free beer\" Group By g1, g2 order bY count(s3) intervaL 10 LiMiT 23", - "SELECT s1 `from` COUNT(s3) FROM table WHERE w1 == 2 AND w2 eq \"free beer\" GROUP BY g1 g2 ORDER BY count(s3) INTERVAL 10 LIMIT 23", - "select s1 `from` count(s3) from table where w1 == 2 w2 eq \"free beer\" group g1 g2 order count(s3) interval 10 limit 23", - "limit 23 interval 10 order count(s3) group g1 g2 where w1 == 2 w2 eq \"free beer\" from table select s1 `from` count(s3)", - } - - for _, queryStr := range dialects { - q, err := NewQuery(queryStr) - if err != nil { - t.Errorf("%s: %s", err.Error(), queryStr) - } - - // 'select' clause - if len(q.Select) != 3 { - t.Errorf("Expected three elements in 'select' clause but got '%v': %s\n%v", q.Select, queryStr, q) - } - - if q.Select[0].Field != "s1" { - t.Errorf("Expected 's1' as first element in 'select' clause but got '%v': %s\n%v", q.Select[0].Field, queryStr, q) - } - if q.Select[0].Operation != Last { - t.Errorf("Expected 'last' as aggregation function of first element in 'select' clause but got '%v': %s\n%v", q.Select[0].Operation, queryStr, q) - } - - if q.Select[1].Field != "from" { - t.Errorf("Expected 'from' as second element in 'select' clause but got '%v': %s\n%v", q.Select[1].Field, queryStr, q) - } - if q.Select[1].Operation != Last { - t.Errorf("Expected 'last' as aggregation function of second element in 'select' clause but got '%v': %s\n%v", q.Select[1].Operation, queryStr, q) - } - - if q.Select[2].Field != "s3" { - t.Errorf("Expected 's3' as third element in 'select' clause but got '%v': %s\n%v", q.Select[2].Field, queryStr, q) - } - if q.Select[2].Operation != Count { - t.Errorf("Expected 'count' as aggregation function of third element in 'select' clause but got '%v': %s\n%v", q.Select[2].Operation, queryStr, q) - } - if q.Select[2].FieldStorage != "count(s3)" { - t.Errorf("Expected 'count(s3)' as third element's storage in 'select' clause but got '%v': %s\n%v", q.Select[2].FieldStorage, queryStr, q) - } - - // 'from' clause - if q.Table != "TABLE" { - t.Errorf("Expected 'TABLE' in 'from' clause but got '%v': %s\n%v", q.Table, queryStr, q) - } - - // 'where' clause - if len(q.Where) != 2 { - t.Errorf("Expected two elements in 'where' clause but got '%v': %s\n%v", q.Where, queryStr, q) - } - if q.Where[0].lString != "w1" { - t.Errorf("Expected w1 as first element in 'where' clause but got '%v': %s\n%v", q.Where[0].lString, queryStr, q) - } - if q.Where[0].Operation != FloatEq { - t.Errorf("Expected FloatEq operation in first 'where' condition but got '%v': %s\n%v", q.Where[0].Operation, queryStr, q) - } - if q.Where[0].rFloat != 2 { - t.Errorf("Expected '2' as float argument in first 'where' condition but got '%v': %s\n%v", q.Where[0].rFloat, queryStr, q) - } - if q.Where[1].lString != "w2" { - t.Errorf("Expected w2 as second element in 'where' clause but got '%v': %s\n%v", q.Where[1].lString, queryStr, q) - } - if q.Where[1].Operation != StringEq { - t.Errorf("Expected StringEq operation in second 'where' condition but got '%v': %s\n%v", q.Where[0].Operation, queryStr, q) - } - if q.Where[1].rStri |
