diff --git a/fragment_internal_test.go b/fragment_internal_test.go index f101443ca..dd57635d2 100644 --- a/fragment_internal_test.go +++ b/fragment_internal_test.go @@ -3576,3 +3576,47 @@ func TestFragmentConcurrentReadWrite(t *testing.T) { t.Logf("%d", acc) } + +func TestFragment_Bug_Q2DoubleDelete(t *testing.T) { + f := mustOpenFragment("i", "f", viewStandard, 0, "") + b := []byte{60, 48, 0, 0, 1, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 24, 0, 0, 0, 1, 0} + defer f.Clean(t) + err := f.importRoaringT(b, false) + if err != nil { + t.Fatalf("importing roaring: %v", err) + } + //check the bit + res := f.row(1).Columns() + if len(res) < 1 || f.row(1).Columns()[0] != 1 { + t.Fatalf("expecting 1 got: %v", res) + } + //clear the bit + changed, _ := f.clearBit(1, 1) + if !changed { + t.Fatalf("expected change got %v", changed) + } + //check missing + res = f.row(1).Columns() + if len(res) != 0 { + t.Fatalf("expected nothing got %v", res) + } + // import again + err = f.importRoaringT(b, false) + if err != nil { + t.Fatalf("importing roaring: %v", err) + } + //check + res = f.row(1).Columns() + if len(res) < 1 || f.row(1).Columns()[0] != 1 { + t.Fatalf("again expecting 1 got: %v", res) + } + changed, _ = f.clearBit(1, 1) + if !changed { + t.Fatalf("again expected change got %v", changed) + } + //check missing + res = f.row(1).Columns() + if len(res) != 0 { + t.Fatalf("expected nothing got %v", res) + } +} diff --git a/roaring/roaring.go b/roaring/roaring.go index bb0479f8f..19a671dbf 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -422,7 +422,11 @@ func (b *Bitmap) remove(v uint64) bool { c := b.Containers.Get(highbits(v)) newC, changed := c.remove(lowbits(v)) if newC != c { - b.Containers.Put(highbits(v), newC) + if newC != nil { + b.Containers.Put(highbits(v), newC) + } else { + b.Containers.Remove(highbits(v)) + } } return changed } diff --git a/server/grpc.go b/server/grpc.go index 60e7dcc29..78f9b87f6 100644 --- a/server/grpc.go +++ b/server/grpc.go @@ -80,11 +80,16 @@ func (h grpcHandler) QueryPQL(req *pb.QueryPQLRequest, stream pb.Pilosa_QueryPQL // uint64, bool, etc.) based on the Pilosa field type. func fieldDataType(f *pilosa.Field) string { switch f.Type() { - case "set", "mutex": + case "set": if f.Keys() { return "[]string" } return "[]uint64" + case "mutex": + if f.Keys() { + return "string" + } + return "uint64" case "int": if f.Keys() { return "string"