diff --git a/CHANGELOG.md b/CHANGELOG.md index b628c8e3c86..3837cffd6b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ The admin UI is removed and unusable in this release. The `[admin]` configuratio - [#8315](https://github.com/influxdata/influxdb/issues/8315): Remove default upper time bound on DELETE queries. - [#8066](https://github.com/influxdata/influxdb/issues/8066): Fix LIMIT and OFFSET for certain aggregate queries. - [#8045](https://github.com/influxdata/influxdb/issues/8045): Refactor the subquery code and fix outer condition queries. +- [#8343](https://github.com/influxdata/influxdb/issues/8343): Set the CSV output to an empty string for null values. ## v1.2.3 [unreleased] diff --git a/services/httpd/response_writer.go b/services/httpd/response_writer.go index 36db12a195f..fe523e06910 100644 --- a/services/httpd/response_writer.go +++ b/services/httpd/response_writer.go @@ -145,6 +145,11 @@ func (w *csvFormatter) WriteResponse(resp Response) (n int, err error) { } for _, values := range row.Values { for i, value := range values { + if value == nil { + w.columns[i+2] = "" + continue + } + switch v := value.(type) { case float64: w.columns[i+2] = strconv.FormatFloat(v, 'f', -1, 64) @@ -160,6 +165,8 @@ func (w *csvFormatter) WriteResponse(resp Response) (n int, err error) { } case time.Time: w.columns[i+2] = strconv.FormatInt(v.UnixNano(), 10) + case *float64, *int64, *string, *bool: + w.columns[i+2] = "" } } csv.Write(w.columns) diff --git a/services/httpd/response_writer_test.go b/services/httpd/response_writer_test.go new file mode 100644 index 00000000000..36841ed3828 --- /dev/null +++ b/services/httpd/response_writer_test.go @@ -0,0 +1,61 @@ +package httpd_test + +import ( + "net/http" + "net/http/httptest" + "net/url" + "testing" + "time" + + "github.com/influxdata/influxdb/influxql" + "github.com/influxdata/influxdb/models" + "github.com/influxdata/influxdb/services/httpd" +) + +func TestResponseWriter_CSV(t *testing.T) { + header := make(http.Header) + header.Set("Accept", "text/csv") + r := &http.Request{ + Header: header, + URL: &url.URL{}, + } + w := httptest.NewRecorder() + + writer := httpd.NewResponseWriter(w, r) + writer.WriteResponse(httpd.Response{ + Results: []*influxql.Result{ + { + StatementID: 0, + Series: []*models.Row{ + { + Name: "cpu", + Tags: map[string]string{ + "host": "server01", + "region": "uswest", + }, + Columns: []string{"time", "value"}, + Values: [][]interface{}{ + {time.Unix(0, 10), float64(2.5)}, + {time.Unix(0, 20), int64(5)}, + {time.Unix(0, 30), nil}, + {time.Unix(0, 40), "foobar"}, + {time.Unix(0, 50), true}, + {time.Unix(0, 60), false}, + }, + }, + }, + }, + }, + }) + + if got, want := w.Body.String(), `name,tags,time,value +cpu,"host=server01,region=uswest",10,2.5 +cpu,"host=server01,region=uswest",20,5 +cpu,"host=server01,region=uswest",30, +cpu,"host=server01,region=uswest",40,foobar +cpu,"host=server01,region=uswest",50,true +cpu,"host=server01,region=uswest",60,false +`; got != want { + t.Errorf("unexpected output:\n\ngot=%v\nwant=%s", got, want) + } +}