summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <pbuetow@mimecast.com>2022-03-14 13:09:31 +0000
committerPaul Buetow <pbuetow@mimecast.com>2022-03-14 13:09:31 +0000
commit1f10cafab36d6db860c2a684e0f6e27dce35034a (patch)
tree879ba2bd9cfceffbb1568767f0c1b2eff9bd4945
parent45b2e60f7206f90af977e95e44e2529aa2ce8081 (diff)
Add "append" modifier for "outfile" keyword to the mapreduce language
-rw-r--r--doc/querylanguage.md2
-rw-r--r--internal/mapr/groupsetresult.go8
-rw-r--r--internal/mapr/query.go27
-rw-r--r--internal/mapr/query_test.go46
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;",
}