diff options
| author | Paul Buetow <pbuetow@mimecast.com> | 2022-03-14 13:09:31 +0000 |
|---|---|---|
| committer | Paul Buetow <pbuetow@mimecast.com> | 2022-03-14 13:09:31 +0000 |
| commit | 1f10cafab36d6db860c2a684e0f6e27dce35034a (patch) | |
| tree | 879ba2bd9cfceffbb1568767f0c1b2eff9bd4945 | |
| parent | 45b2e60f7206f90af977e95e44e2529aa2ce8081 (diff) | |
Add "append" modifier for "outfile" keyword to the mapreduce language
| -rw-r--r-- | doc/querylanguage.md | 2 | ||||
| -rw-r--r-- | internal/mapr/groupsetresult.go | 8 | ||||
| -rw-r--r-- | internal/mapr/query.go | 27 | ||||
| -rw-r--r-- | internal/mapr/query_test.go | 46 |
4 files changed, 72 insertions, 11 deletions
diff --git a/doc/querylanguage.md b/doc/querylanguage.md index 41e95de..c3e567e 100644 --- a/doc/querylanguage.md +++ b/doc/querylanguage.md @@ -35,7 +35,7 @@ QUERY := select SELECT1[,SELECT2...] [set SET1,[,SET2...]] [interval NUMBER] [limit NUMBER] - [outfile STRING] + [outfile [append] STRING] [logformat LOGFORMAT] ``` diff --git a/internal/mapr/groupsetresult.go b/internal/mapr/groupsetresult.go index 6d0ac1f..915b342 100644 --- a/internal/mapr/groupsetresult.go +++ b/internal/mapr/groupsetresult.go @@ -159,7 +159,7 @@ func (g *GroupSet) resultWriteFormattedDataEntry(query *Query, sb *strings.Build } func (*GroupSet) writeQueryFile(query *Query) error { - queryFile := fmt.Sprintf("%s.query", query.Outfile) + queryFile := fmt.Sprintf("%s.query", query.Outfile.FilePath) tmpQueryFile := fmt.Sprintf("%s.tmp", queryFile) dlog.Common.Debug("Writing query file", queryFile) @@ -187,8 +187,8 @@ func (g *GroupSet) WriteResult(query *Query) error { return err } - dlog.Common.Info("Writing outfile", query.Outfile) - tmpOutfile := fmt.Sprintf("%s.tmp", query.Outfile) + dlog.Common.Info("Writing outfile", query.Outfile.FilePath) + tmpOutfile := fmt.Sprintf("%s.tmp", query.Outfile.FilePath) fd, err := os.Create(tmpOutfile) if err != nil { @@ -228,7 +228,7 @@ func (g *GroupSet) resultWriteUnformatted(query *Query, rows []result, tmpOutfil fd.WriteString("\n") } - if err := os.Rename(tmpOutfile, query.Outfile); err != nil { + if err := os.Rename(tmpOutfile, query.Outfile.FilePath); err != nil { os.Remove(tmpOutfile) return err } diff --git a/internal/mapr/query.go b/internal/mapr/query.go index d70675f..4eeb7b6 100644 --- a/internal/mapr/query.go +++ b/internal/mapr/query.go @@ -13,6 +13,15 @@ const ( unexpectedEnd string = "Unexpected end of query" ) +type Outfile struct { + FilePath string + AppendMode bool +} + +func (o Outfile) String() string { + return fmt.Sprintf("Outfile(FilePath:%v,AppendMode:%v)", o.FilePath, o.AppendMode) +} + // Query represents a parsed mapr query. type Query struct { Select []selectCondition @@ -25,7 +34,7 @@ type Query struct { GroupKey string Interval time.Duration Limit int - Outfile string + Outfile *Outfile RawQuery string tokens []token LogFormat string @@ -68,7 +77,7 @@ func NewQuery(queryStr string) (*Query, error) { // HasOutfile returns true if query result will be written to a CVS output file. func (q *Query) HasOutfile() bool { - return q.Outfile != "" + return q.Outfile != nil } // Has is a helper to determine whether a query contains a substring @@ -193,10 +202,18 @@ func (q *Query) parseTokens(tokens []token) ([]token, error) { q.Limit = i case "outfile": tokens, found = tokensConsume(tokens[1:]) - if len(found) == 0 { - return tokens, errors.New(invalidQuery + unexpectedEnd) + switch len(found) { + case 1: + q.Outfile = &Outfile{FilePath: found[0].str, AppendMode: false} + case 2: + if found[0].str == "append" { + q.Outfile = &Outfile{FilePath: found[1].str, AppendMode: true} + } else { + return tokens, errors.New(invalidQuery + invalidQuery) + } + default: + return tokens, errors.New(invalidQuery + invalidQuery) } - q.Outfile = found[0].str case "logformat": tokens, found = tokensConsume(tokens[1:]) if len(found) == 0 { diff --git a/internal/mapr/query_test.go b/internal/mapr/query_test.go index a0913fd..f03ccba 100644 --- a/internal/mapr/query_test.go +++ b/internal/mapr/query_test.go @@ -5,6 +5,48 @@ import ( "time" ) +func TestParseQueryOutfile(t *testing.T) { + queryStr := "select foo from bar outfile \"baz.csv\"" + + q, err := NewQuery(queryStr) + if err != nil { + t.Errorf("Query parse error: %s\n%v: %v", queryStr, q, err) + } + + if q.Outfile == nil { + t.Errorf("Expected non-nil outfile: %s\n%v", queryStr, q) + } + + if q.Outfile.FilePath != "baz.csv" { + t.Errorf("Expected \"baz.csv\" as outfile file path: %s\n%v", queryStr, q) + } + + if q.Outfile.AppendMode { + t.Errorf("Expected append mode of outfile to be false: %s\n%v", queryStr, q) + } +} + +func TestParseQueryOutfileAppend(t *testing.T) { + queryStr := "select foo from bar outfile append \"baz.csv\"" + + q, err := NewQuery(queryStr) + if err != nil { + t.Errorf("Query parse error: %s\n%v: %v", queryStr, q, err) + } + + if q.Outfile == nil { + t.Errorf("Expected non-nil outfile: %s\n%v", queryStr, q) + } + + if q.Outfile.FilePath != "baz.csv" { + t.Errorf("Expected \"baz.csv\" as outfile file path: %s\n%v", queryStr, q) + } + + if !q.Outfile.AppendMode { + t.Errorf("Expected append mode of outfile to be true: %s\n%v", queryStr, q) + } +} + func TestParseQuerySimple(t *testing.T) { errorQueries := []string{ "select", @@ -30,7 +72,9 @@ func TestParseQuerySimple(t *testing.T) { "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz " + "order by foo limit 23 outfile \"result.csv\"", "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz " + - "order by foo limit 23 outfile \"result.csv\" " + + "order by foo limit 23 outfile append \"result.csv\"", + "select foo from bar where baz < 100 bay eq 12 group by foo, bar, baz " + + "order by foo limit 23 outfile append \"result.csv\" " + "set $foo = maskdigits(bar), $baz = 12, $bay = $foo;", } |
