diff --git a/CHANGELOG.md b/CHANGELOG.md index 42330df384..aecda3e4b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### Bugfixes - [#2255](https://github.com/influxdb/influxdb/pull/2255): Fix panic when changing default retention policy. - [#2257](https://github.com/influxdb/influxdb/pull/2257): Add "snapshotting" pseudo state & log entry cache. +- [#2261](https://github.com/influxdb/influxdb/pull/2261): Support int64 value types. +- [#2191](https://github.com/influxdb/influxdb/pull/2191): Case-insensitive check for "fill" ## v0.9.0-rc23 [2015-04-11] diff --git a/cmd/influx/main.go b/cmd/influx/main.go index be75b898c8..654a5888e1 100644 --- a/cmd/influx/main.go +++ b/cmd/influx/main.go @@ -84,13 +84,19 @@ func main() { c.connect("") if c.ShouldDump { - c.dump() - return + if err := c.dump(); err != nil { + os.Exit(1) + } else { + os.Exit(0) + } } if c.Execute != "" { - c.executeQuery(c.Execute) - return + if err := c.executeQuery(c.Execute); err != nil { + os.Exit(1) + } else { + os.Exit(0) + } } fmt.Println("InfluxDB shell " + version) @@ -264,33 +270,38 @@ func (c *CommandLine) SetFormat(cmd string) { } } -func (c *CommandLine) dump() { +func (c *CommandLine) dump() error { response, err := c.Client.Dump(c.Database) defer response.Close() if err != nil { fmt.Printf("Dump failed. %s\n", err) + return err } else { _, err := io.Copy(os.Stdout, response) if err != nil { fmt.Printf("Dump failed. %s\n", err) + return err } } + return nil } -func (c *CommandLine) executeQuery(query string) { +func (c *CommandLine) executeQuery(query string) error { response, err := c.Client.Query(client.Query{Command: query, Database: c.Database}) if err != nil { fmt.Printf("ERR: %s\n", err) - return + return err } c.FormatResponse(response, os.Stdout) - if response.Error() != nil { + if err := response.Error(); err != nil { fmt.Printf("ERR: %s\n", response.Error()) if c.Database == "" { fmt.Println("Warning: It is possible this error is due to not setting a database.") fmt.Println(`Please set a database with the command "use ".`) } + return err } + return nil } func (c *CommandLine) FormatResponse(response *client.Response, w io.Writer) { diff --git a/database.go b/database.go index 09864abf46..084f0ce51f 100644 --- a/database.go +++ b/database.go @@ -780,17 +780,14 @@ func (f *FieldCodec) EncodeFields(values map[string]interface{}) ([]byte, error) var buf []byte switch field.Type { - case influxql.Number: - var value float64 - // Convert integers to floats. - if intval, ok := v.(int); ok { - value = float64(intval) - } else { - value = v.(float64) - } - + case influxql.Float: + value := v.(float64) buf = make([]byte, 9) binary.BigEndian.PutUint64(buf[1:9], math.Float64bits(value)) + case influxql.Integer: + value := v.(int64) + buf = make([]byte, 9) + binary.BigEndian.PutUint64(buf[1:9], uint64(value)) case influxql.Boolean: value := v.(bool) @@ -850,10 +847,13 @@ func (f *FieldCodec) DecodeByID(targetID uint8, b []byte) (interface{}, error) { var value interface{} switch field.Type { - case influxql.Number: + case influxql.Float: // Move bytes forward. value = math.Float64frombits(binary.BigEndian.Uint64(b[1:9])) b = b[9:] + case influxql.Integer: + value = int64(binary.BigEndian.Uint64(b[1:9])) + b = b[9:] case influxql.Boolean: if b[1] == 1 { value = true @@ -904,10 +904,14 @@ func (f *FieldCodec) DecodeFields(b []byte) (map[uint8]interface{}, error) { var value interface{} switch field.Type { - case influxql.Number: + case influxql.Float: value = math.Float64frombits(binary.BigEndian.Uint64(b[1:9])) // Move bytes forward. b = b[9:] + case influxql.Integer: + value = int64(binary.BigEndian.Uint64(b[1:9])) + // Move bytes forward. + b = b[9:] case influxql.Boolean: if b[1] == 1 { value = true diff --git a/httpd/handler_test.go b/httpd/handler_test.go index 670f52963d..9f630cb1a3 100644 --- a/httpd/handler_test.go +++ b/httpd/handler_test.go @@ -1564,7 +1564,7 @@ func TestHandler_serveWriteSeriesFieldTypeConflict(t *testing.T) { if len(r.Results) != 0 { t.Fatalf("unexpected results count") } - if r.Err.Error() != "field \"value\" is type string, mapped as type number" { + if r.Err.Error() != "field \"value\" is type string, mapped as type float" { t.Fatalf("unexpected error returned, actual: %s", r.Err.Error()) } } diff --git a/influxql/ast.go b/influxql/ast.go index 3db30ac87f..a78006771b 100644 --- a/influxql/ast.go +++ b/influxql/ast.go @@ -17,8 +17,10 @@ type DataType string const ( // Unknown primitive data type. Unknown = DataType("") - // Number means the data type is an int or float. - Number = DataType("number") + // Float means the data type is a float + Float = DataType("float") + // Integer means the data type is a integer + Integer = DataType("integer") // Boolean means the data type is a boolean. Boolean = DataType("boolean") // String means the data type is a string of text. @@ -33,9 +35,9 @@ const ( func InspectDataType(v interface{}) DataType { switch v.(type) { case float64: - return Number - case int: - return Number + return Float + case int64, int32, int: + return Integer case bool: return Boolean case string: diff --git a/influxql/ast_test.go b/influxql/ast_test.go index 237b21b74d..aabf7cfca2 100644 --- a/influxql/ast_test.go +++ b/influxql/ast_test.go @@ -16,7 +16,7 @@ func TestInspectDataType(t *testing.T) { v interface{} typ influxql.DataType }{ - {float64(100), influxql.Number}, + {float64(100), influxql.Float}, } { if typ := influxql.InspectDataType(tt.v); tt.typ != typ { t.Errorf("%d. %v (%s): unexpected type: %s", i, tt.v, tt.typ, typ) diff --git a/influxql/parser.go b/influxql/parser.go index 3acf1d48e3..324d32163d 100644 --- a/influxql/parser.go +++ b/influxql/parser.go @@ -1558,7 +1558,7 @@ func (p *Parser) parseFill() (FillOption, interface{}, error) { p.unscan() return NullFill, nil, nil } else { - if lit.Name != "fill" { + if strings.ToLower(lit.Name) != "fill" { p.unscan() return NullFill, nil, nil } diff --git a/influxql/parser_test.go b/influxql/parser_test.go index 0ac0899299..b2ed10369a 100644 --- a/influxql/parser_test.go +++ b/influxql/parser_test.go @@ -317,6 +317,20 @@ func TestParser_ParseStatement(t *testing.T) { }, }, + // SELECT statement with FILL(none) -- check case insensitivity + { + s: `SELECT mean(value) FROM cpu GROUP BY time(5m) FILL(none)`, + stmt: &influxql.SelectStatement{ + Fields: []*influxql.Field{{ + Expr: &influxql.Call{ + Name: "mean", + Args: []influxql.Expr{&influxql.VarRef{Val: "value"}}}}}, + Sources: []influxql.Source{&influxql.Measurement{Name: "cpu"}}, + Dimensions: []*influxql.Dimension{{Expr: &influxql.Call{Name: "time", Args: []influxql.Expr{&influxql.DurationLiteral{Val: 5 * time.Minute}}}}}, + Fill: influxql.NoFill, + }, + }, + // SELECT statement with previous fill { s: `SELECT mean(value) FROM cpu GROUP BY time(5m) fill(previous)`, diff --git a/internal_test.go b/internal_test.go index cb74408755..9fe27a5a4c 100644 --- a/internal_test.go +++ b/internal_test.go @@ -16,7 +16,7 @@ import ( func TestMeasurement_uniqueTagValues(t *testing.T) { // Create a measurement to run against. m := NewMeasurement("cpu") - m.createFieldIfNotExists("value", influxql.Number) + m.createFieldIfNotExists("value", influxql.Float) for i, tt := range []struct { expr string @@ -37,7 +37,7 @@ func TestMeasurement_uniqueTagValues(t *testing.T) { // Ensure a measurement can expand an expression for all possible tag values used. func TestMeasurement_expandExpr(t *testing.T) { m := NewMeasurement("cpu") - m.createFieldIfNotExists("value", influxql.Number) + m.createFieldIfNotExists("value", influxql.Float) type tagSetExprString struct { tagExpr []tagExpr @@ -129,13 +129,13 @@ func TestCreateMeasurementsCommand(t *testing.T) { } // Add a field. - err = c.addFieldIfNotExists("bar", "value", influxql.Number) + err = c.addFieldIfNotExists("bar", "value", influxql.Integer) if err != nil { t.Fatal("error adding field \"value\"") } // Add same field again. - err = c.addFieldIfNotExists("bar", "value", influxql.Number) + err = c.addFieldIfNotExists("bar", "value", influxql.Integer) if err != nil { t.Fatal("error re-adding field \"value\"") } @@ -167,7 +167,7 @@ func TestCreateMeasurementsCommand_Errors(t *testing.T) { // Measurements should be created automatically. c.addSeriesIfNotExists("bar", nil) - err = c.addFieldIfNotExists("bar", "value", influxql.Number) + err = c.addFieldIfNotExists("bar", "value", influxql.Float) if err != nil { t.Fatalf("unexpected error got %s", err.Error()) } @@ -176,7 +176,7 @@ func TestCreateMeasurementsCommand_Errors(t *testing.T) { c.addMeasurementIfNotExists("bar") // Test type conflicts - err = c.addFieldIfNotExists("bar", "value", influxql.Number) + err = c.addFieldIfNotExists("bar", "value", influxql.Float) if err != nil { t.Fatal("error adding field \"value\"") } diff --git a/raft/log_test.go b/raft/log_test.go index 786cfaea8f..35058382c3 100644 --- a/raft/log_test.go +++ b/raft/log_test.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "path/filepath" + "runtime" "strings" "sync" "testing" @@ -44,6 +45,10 @@ func TestLog_Open_ErrMkdir(t *testing.T) { // Ensure that opening a log with an inaccessible ID path returns an error. func TestLog_Open_ErrInaccessibleID(t *testing.T) { + if "windows" == runtime.GOOS { + t.Skip("skip it on the windows") + } + path := tempfile() MustWriteFile(filepath.Join(path, "id"), []byte(`1`)) MustChmod(filepath.Join(path, "id"), 0) @@ -73,6 +78,10 @@ func TestLog_Open_ErrInvalidID(t *testing.T) { // Ensure that opening a log with an inaccesible term path returns an error. func TestLog_Open_ErrInaccessibleTerm(t *testing.T) { + if "windows" == runtime.GOOS { + t.Skip("skip it on the windows") + } + path := tempfile() MustWriteFile(filepath.Join(path, "id"), []byte(`1`)) MustWriteFile(filepath.Join(path, "term"), []byte(`1`)) @@ -104,6 +113,10 @@ func TestLog_Open_ErrInvalidTerm(t *testing.T) { // Ensure that opening an inaccessible config path returns an error. func TestLog_Open_ErrInaccessibleConfig(t *testing.T) { + if "windows" == runtime.GOOS { + t.Skip("skip it on the windows") + } + path := tempfile() MustWriteFile(filepath.Join(path, "id"), []byte(`1`)) MustWriteFile(filepath.Join(path, "term"), []byte(`1`)) diff --git a/server_test.go b/server_test.go index 3631f6f652..302a3284f9 100644 --- a/server_test.go +++ b/server_test.go @@ -976,6 +976,40 @@ func TestServer_StartRetentionPolicyEnforcement_ErrZeroInterval(t *testing.T) { } } +// Ensure the server can support writes of all data types. +func TestServer_WriteAllDataTypes(t *testing.T) { + c := test.NewDefaultMessagingClient() + defer c.Close() + s := OpenServer(c) + defer s.Close() + s.CreateDatabase("foo") + s.CreateRetentionPolicy("foo", &influxdb.RetentionPolicy{Name: "raw", Duration: 1 * time.Hour}) + s.SetDefaultRetentionPolicy("foo", "raw") + + // Write series with one point to the database. + s.MustWriteSeries("foo", "raw", []influxdb.Point{{Name: "series1", Timestamp: mustParseTime("2000-01-01T00:00:00Z"), Fields: map[string]interface{}{"value": float64(20)}}}) + s.MustWriteSeries("foo", "raw", []influxdb.Point{{Name: "series2", Timestamp: mustParseTime("2000-01-01T00:00:00Z"), Fields: map[string]interface{}{"value": int64(30)}}}) + s.MustWriteSeries("foo", "raw", []influxdb.Point{{Name: "series3", Timestamp: mustParseTime("2000-01-01T00:00:00Z"), Fields: map[string]interface{}{"value": "baz"}}}) + s.MustWriteSeries("foo", "raw", []influxdb.Point{{Name: "series4", Timestamp: mustParseTime("2000-01-01T00:00:00Z"), Fields: map[string]interface{}{"value": true}}}) + time.Sleep(time.Millisecond * 100) + + f := func(t *testing.T, database, query, expected string) { + results := s.executeQuery(MustParseQuery(query), database, nil) + if res := results.Results[0]; res.Err != nil { + t.Errorf("unexpected error: %s", res.Err) + } else if len(res.Series) != 1 { + t.Errorf("unexpected row count: %d", len(res.Series)) + } else if s := mustMarshalJSON(res); s != expected { + t.Errorf("unexpected row(0): \nexp: %s\ngot: %s", expected, s) + } + } + + f(t, "foo", "SELECT * from series1", `{"series":[{"name":"series1","columns":["time","value"],"values":[["2000-01-01T00:00:00Z",20]]}]}`) + f(t, "foo", "SELECT * from series2", `{"series":[{"name":"series2","columns":["time","value"],"values":[["2000-01-01T00:00:00Z",30]]}]}`) + f(t, "foo", "SELECT * from series3", `{"series":[{"name":"series3","columns":["time","value"],"values":[["2000-01-01T00:00:00Z","baz"]]}]}`) + f(t, "foo", "SELECT * from series4", `{"series":[{"name":"series4","columns":["time","value"],"values":[["2000-01-01T00:00:00Z",true]]}]}`) +} + func TestServer_EnforceRetentionPolices(t *testing.T) { c := test.NewDefaultMessagingClient() s := OpenServer(c)