diff --git a/client/influxdb.go b/client/influxdb.go index 38758c35c8..2a0484ab25 100644 --- a/client/influxdb.go +++ b/client/influxdb.go @@ -44,7 +44,7 @@ func NewClient(c Config) (*Client, error) { return &client, nil } -func (c *Client) Query(q Query) (influxdb.Results, error) { +func (c *Client) Query(q Query) (*influxdb.Results, error) { u := c.url u.Path = "query" @@ -64,10 +64,10 @@ func (c *Client) Query(q Query) (influxdb.Results, error) { if err != nil { return nil, err } - return results, nil + return &results, nil } -func (c *Client) Write(writes ...Write) (influxdb.Results, error) { +func (c *Client) Write(writes ...Write) (*influxdb.Results, error) { c.url.Path = "write" type data struct { Points []influxdb.Point `json:"points"` @@ -94,7 +94,7 @@ func (c *Client) Write(writes ...Write) (influxdb.Results, error) { if err != nil { return nil, err } - return results, nil + return &results, nil } func (c *Client) Ping() (time.Duration, string, error) { diff --git a/cmd/influxdb/main.go b/cmd/influxdb/main.go index bd74572195..cb145d39dc 100644 --- a/cmd/influxdb/main.go +++ b/cmd/influxdb/main.go @@ -111,7 +111,7 @@ func (c *cli) executeQuery(query string) { if err != nil { fmt.Printf("ERR: %s\n", err) } - for _, r := range results { + for _, r := range results.Results { var i interface{} if r.Err != nil { i = r.Err diff --git a/httpd/handler.go b/httpd/handler.go index b27988c44e..86f00cf173 100644 --- a/httpd/handler.go +++ b/httpd/handler.go @@ -331,12 +331,9 @@ func httpResults(w http.ResponseWriter, results influxdb.Results, pretty bool) { // httpError writes an error to the client in a standard format. func httpError(w http.ResponseWriter, error string, code int) { - var results influxdb.Results - results = append(results, &influxdb.Result{Err: errors.New(error)}) - w.Header().Add("content-type", "application/json") w.WriteHeader(code) - _ = json.NewEncoder(w).Encode(results) + _ = json.NewEncoder(w).Encode(influxdb.Results{Err: errors.New(error)}) } // Filters and filter helpers diff --git a/httpd/handler_test.go b/httpd/handler_test.go index c9ce070f8c..3bdb30348c 100644 --- a/httpd/handler_test.go +++ b/httpd/handler_test.go @@ -30,7 +30,7 @@ func TestHandler_Databases(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "SHOW DATABASES"}, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"rows":[{"columns":["Name"],"values":[["bar"],["foo"]]}]}]` { + } else if body != `{"results":[{"rows":[{"columns":["Name"],"values":[["bar"],["foo"]]}]}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -45,25 +45,27 @@ func TestHandler_DatabasesPrettyPrinted(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "SHOW DATABASES", "pretty": "true"}, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[ - { - "rows": [ - { - "columns": [ - "Name" - ], - "values": [ - [ - "bar" + } else if body != `{ + "results": [ + { + "rows": [ + { + "columns": [ + "Name" ], - [ - "foo" + "values": [ + [ + "bar" + ], + [ + "foo" + ] ] - ] - } - ] - } -]` { + } + ] + } + ] +}` { t.Fatalf("unexpected body: %s", body) } } @@ -76,7 +78,7 @@ func TestHandler_CreateDatabase(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "CREATE DATABASE foo"}, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -101,7 +103,7 @@ func TestHandler_CreateDatabase_Conflict(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "CREATE DATABASE foo"}, nil, "") if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"database exists"}]` { + } else if body != `{"results":[{"error":"database exists"}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -115,7 +117,7 @@ func TestHandler_DeleteDatabase(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "DROP DATABASE foo"}, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -128,7 +130,7 @@ func TestHandler_DeleteDatabase_NotFound(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, map[string]string{"q": "DROP DATABASE bar"}, nil, "") if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"database not found"}]` { + } else if body != `{"results":[{"error":"database not found"}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -144,7 +146,7 @@ func TestHandler_RetentionPolicies(t *testing.T) { if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"rows":[{"columns":["Name"],"values":[["bar"]]}]}]` { + } else if body != `{"results":[{"rows":[{"columns":["Name"],"values":[["bar"]]}]}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -158,7 +160,7 @@ func TestHandler_RetentionPolicies_DatabaseNotFound(t *testing.T) { if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"database not found"}]` { + } else if body != `{"results":[{"error":"database not found"}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -174,7 +176,7 @@ func TestHandler_CreateRetentionPolicy(t *testing.T) { if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -302,7 +304,7 @@ func TestHandler_DeleteRetentionPolicy(t *testing.T) { if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -317,7 +319,7 @@ func TestHandler_DeleteRetentionPolicy_DatabaseNotFound(t *testing.T) { if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"database not found"}]` { + } else if body != `{"results":[{"error":"database not found"}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -333,7 +335,7 @@ func TestHandler_DeleteRetentionPolicy_NotFound(t *testing.T) { if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"retention policy not found"}]` { + } else if body != `{"results":[{"error":"retention policy not found"}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -360,7 +362,7 @@ func TestHandler_Users_NoUsers(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"rows":[{"columns":["User"]}]}]` { + } else if body != `{"results":[{"rows":[{"columns":["User"]}]}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -375,7 +377,7 @@ func TestHandler_Users_OneUser(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"rows":[{"columns":["User"],"values":[["jdoe"]]}]}]` { + } else if body != `{"results":[{"rows":[{"columns":["User"],"values":[["jdoe"]]}]}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -392,7 +394,7 @@ func TestHandler_Users_MultipleUsers(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"rows":[{"columns":["User"],"values":[["csmith"],["jdoe"],["mclark"]]}]}]` { + } else if body != `{"results":[{"rows":[{"columns":["User"],"values":[["csmith"],["jdoe"],["mclark"]]}]}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -406,7 +408,7 @@ func TestHandler_CreateUser(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -420,7 +422,7 @@ func TestHandler_CreateUser_BadRequest(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusBadRequest { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"error parsing query: found 0, expected identifier at line 1, char 13"}]` { + } else if body != `{"error":"error parsing query: found 0, expected identifier at line 1, char 13"}` { t.Fatalf("unexpected body: %s", body) } } @@ -434,7 +436,7 @@ func TestHandler_CreateUser_BadRequest_NoName(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusBadRequest { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"error parsing query: found WITH, expected identifier at line 1, char 13"}]` { + } else if body != `{"error":"error parsing query: found WITH, expected identifier at line 1, char 13"}` { t.Fatalf("unexpected body: %s", body) } } @@ -448,7 +450,7 @@ func TestHandler_CreateUser_BadRequest_NoPassword(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusBadRequest { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"error parsing query: found EOF, expected WITH at line 1, char 18"}]` { + } else if body != `{"error":"error parsing query: found EOF, expected WITH at line 1, char 18"}` { t.Fatalf("unexpected body: %s", body) } } @@ -499,7 +501,7 @@ func TestHandler_DeleteUser(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusOK { t.Fatalf("unexpected status: %d", status) - } else if body != `[{}]` { + } else if body != `{"results":[{}]}` { t.Fatalf("unexpected body: %s", body) } } @@ -513,7 +515,7 @@ func TestHandler_DeleteUser_UserNotFound(t *testing.T) { status, body := MustHTTP("GET", s.URL+`/query`, query, nil, "") if status != http.StatusInternalServerError { t.Fatalf("unexpected status: %d", status) - } else if body != `[{"error":"user not found"}]` { + } else if body != `{"results":[{"error":"user not found"}]}` { t.Fatalf("unexpected body: %s", body) } } diff --git a/server.go b/server.go index f9fd2b50bf..9613502211 100644 --- a/server.go +++ b/server.go @@ -1517,13 +1517,11 @@ func (s *Server) ReadSeries(database, retentionPolicy, name string, tags map[str func (s *Server) ExecuteQuery(q *influxql.Query, database string, user *User) Results { // Authorize user to execute the query. if err := Authorize(user, q, database); err != nil { - return Results{ - {Err: err}, - } + return Results{Err: err} } // Build empty resultsets. - results := make(Results, len(q.Statements)) + results := Results{Results: make([]*Result, len(q.Statements))} // Execute each statement. for i, stmt := range q.Statements { @@ -1580,16 +1578,16 @@ func (s *Server) ExecuteQuery(q *influxql.Query, database string, user *User) Re } // If an error occurs then stop processing remaining statements. - results[i] = res + results.Results[i] = res if res.Err != nil { break } } // Fill any empty results after error. - for i, res := range results { + for i, res := range results.Results { if res == nil { - results[i] = &Result{Err: ErrNotExecuted} + results.Results[i] = &Result{Err: ErrNotExecuted} } } @@ -1936,12 +1934,31 @@ func (r *Result) MarshalJSON() ([]byte, error) { } // Results represents a list of statement results. -type Results []*Result +type Results struct { + Results []*Result + Err error +} + +func (r Results) MarshalJSON() ([]byte, error) { + // Define a struct that outputs "error" as a string. + var o struct { + Results []*Result `json:"results,omitempty"` + Err string `json:"error,omitempty"` + } + + // Copy fields to output struct. + o.Results = r.Results + if r.Err != nil { + o.Err = r.Err.Error() + } + + return json.Marshal(&o) +} // Error returns the first error from any statement. // Returns nil if no errors occurred on any statements. func (a Results) Error() error { - for _, r := range a { + for _, r := range a.Results { if r.Err != nil { return r.Err } diff --git a/server_test.go b/server_test.go index 2322bf11d4..73673bcdf7 100644 --- a/server_test.go +++ b/server_test.go @@ -712,10 +712,10 @@ func TestServer_ExecuteQuery(t *testing.T) { // Select data from the server. results := s.ExecuteQuery(MustParseQuery("SELECT sum(value) FROM cpu"), "foo", nil) - if len(results) != 1 { - t.Fatalf("unexpected result count: %d", len(results)) + if len(results.Results) != 1 { + t.Fatalf("unexpected result count: %d", len(results.Results)) } - if res := results[0]; res.Err != nil { + if res := results.Results[0]; res.Err != nil { t.Fatalf("unexpected error: %s", res.Err) } else if len(res.Rows) != 1 { t.Fatalf("unexpected row count: %d", len(res.Rows))