diff --git a/api.go b/api.go index 7adff1d1b..22e6ba2f6 100644 --- a/api.go +++ b/api.go @@ -658,7 +658,6 @@ func (api *API) ImportValue(ctx context.Context, req internal.ImportValueRequest if err != nil { return errors.Wrap(err, "getting field") } - // Import into fragment. err = field.ImportValue(req.ColumnIDs, req.Values) if err != nil { diff --git a/ctl/import_test.go b/ctl/import_test.go index ba8a9dc6e..e6e7494b9 100644 --- a/ctl/import_test.go +++ b/ctl/import_test.go @@ -197,3 +197,55 @@ func GetIO(buf bytes.Buffer) (io.Reader, io.Writer, io.Writer) { stderr := bufio.NewWriter(&buf) return stdin, stdout, stderr } + +func TestImportCommand_BugOverwriteValue(t *testing.T) { + + buf := bytes.Buffer{} + stdin, stdout, stderr := GetIO(buf) + cm := NewImportCommand(stdin, stdout, stderr) + file, err := ioutil.TempFile("", "import-value.csv") + file.Write([]byte("0,17\n")) + ctx := context.Background() + if err != nil { + t.Fatal(err) + } + + hldr := test.MustOpenHolder() + defer hldr.Close() + s := test.NewServer() + defer s.Close() + + s.Handler.API.Cluster = test.NewCluster(1) + s.Handler.API.Cluster.Nodes[0].URI = s.HostURI() + s.Handler.API.Holder = hldr.Holder + cm.Host = s.Host() + + http.DefaultClient.Do(MustNewHTTPRequest("POST", s.URL+"/index/i", strings.NewReader(""))) + http.DefaultClient.Do(MustNewHTTPRequest("POST", s.URL+"/index/i/field/f", strings.NewReader(`{"options":{"type": "int", "min": 0, "max":2147483648 }}`))) + + cm.Index = "i" + cm.Field = "f" + cm.Paths = []string{file.Name()} + err = cm.Run(ctx) + if err != nil { + t.Fatalf("Import Run with values doesn't work: %s", err) + } + + file.Close() + file, err = ioutil.TempFile("", "import-value2.csv") + file.Write([]byte("0,16\n")) + cm.Paths = []string{file.Name()} + err = cm.Run(ctx) + if err != nil { + t.Fatalf("Import Run with values doesn't work: %s", err) + } + + file.Close() + file, err = ioutil.TempFile("", "import-value3.csv") + file.Write([]byte("0,19\n")) + cm.Paths = []string{file.Name()} + err = cm.Run(ctx) + if err != nil { + t.Fatalf("Import Run with values doesn't work: %s", err) + } +} diff --git a/enterprise/b/containers_btree.go b/enterprise/b/containers_btree.go index eb779fe06..de001fcb7 100644 --- a/enterprise/b/containers_btree.go +++ b/enterprise/b/containers_btree.go @@ -160,6 +160,10 @@ func (btc *BTreeContainers) Size() int { return btc.tree.Len() } +func (btc *BTreeContainers) Reset() { + btc.tree = TreeNew(cmp) +} + func (btc *BTreeContainers) Iterator(key uint64) (citer roaring.ContainerIterator, found bool) { e, ok := btc.tree.Seek(key) if ok { diff --git a/fragment_internal_test.go b/fragment_internal_test.go index 6c733ba4c..6da3ded7e 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -213,6 +213,39 @@ func TestFragment_SetValue(t *testing.T) { t.Fatal(err) } }) + t.Run("Crash", func(t *testing.T) { + f := mustOpenFragment("i", "f", ViewStandard, 0, "") + defer f.Close() + + // Set value. + if changed, err := f.setValue(0, 32, 17); err != nil { + t.Fatal(err) + } else if !changed { + t.Fatal("expected change") + } + + if changed, err := f.setValue(0, 32, 16); err != nil { + t.Fatal(err) + } else if !changed { + t.Fatal("expected change") + } + + if changed, err := f.setValue(0, 32, 19); err != nil { + t.Fatal(err) + } else if !changed { + t.Fatal("expected change") + } + + // Read value. + if value, exists, err := f.value(0, 32); err != nil { + t.Fatal(err) + } else if value != 19 { + t.Fatalf("unexpected value: %d", value) + } else if !exists { + t.Fatal("expected to exist") + } + }) + } // Ensure a fragment can sum values. diff --git a/roaring/containers.go b/roaring/containers.go index 133a30cf3..19871050b 100644 --- a/roaring/containers.go +++ b/roaring/containers.go @@ -132,6 +132,13 @@ func (sc *SliceContainers) Count() uint64 { return n } +func (sc *SliceContainers) Reset() { + sc.keys = sc.keys[:0] + sc.containers = sc.containers[:0] + sc.lastContainer = nil + sc.lastKey = 0 +} + func (sc *SliceContainers) seek(key uint64) (int, bool) { i := search64(sc.keys, key) found := true diff --git a/roaring/roaring.go b/roaring/roaring.go index f4d9218e2..07f1600a7 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -94,6 +94,8 @@ type Containers interface { // container is found at key. Iterator(key uint64) (citer ContainerIterator, found bool) Count() uint64 + //Reset will clear the containers collection to allow for recycling during snapshot + Reset() } type ContainerIterator interface { @@ -631,7 +633,7 @@ func (b *Bitmap) UnmarshalBinary(data []byte) error { keyN := binary.LittleEndian.Uint32(data[4:8]) headerSize := headerBaseSize - + b.Containers.Reset() // Descriptive header section: Read container keys and cardinalities. for i, buf := 0, data[headerSize:]; i < int(keyN); i, buf = i+1, buf[12:] { b.Containers.PutContainerValues( @@ -688,6 +690,7 @@ func (b *Bitmap) UnmarshalBinary(data []byte) error { // FIXME(benbjohnson): return error with position so file can be trimmed. return err } + opr.apply(b) // Increase the op count.