From d8b9a921b8fb0a18887699fc398216daa9edb910 Mon Sep 17 00:00:00 2001 From: Seebs Date: Fri, 4 Mar 2022 13:50:22 -0600 Subject: [PATCH] handle BitN when updating an existing bitmap pointer cell We have code to correctly fill in cell.BitN when a leaf cell already exists but isn't of the correct sort, but not to handle the case where it already exists and *is* a BitmapPtr, but doesn't necessarily have the right BitN value. --- rbf/cursor.go | 4 ++-- rbf/cursor_test.go | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/rbf/cursor.go b/rbf/cursor.go index b0fb50df5..dea006e95 100644 --- a/rbf/cursor.go +++ b/rbf/cursor.go @@ -411,10 +411,10 @@ func (c *Cursor) putLeafCell(in leafCell) (err error) { } cell.Type = ContainerTypeBitmapPtr cell.Data = fromPgno(bitmapPgno) - // update the BitN too - cell.BitN = in.BitN cell.ElemN = in.ElemN } + // update the BitN regardless + cell.BitN = in.BitN } } diff --git a/rbf/cursor_test.go b/rbf/cursor_test.go index 7de5f7d77..7859a9efa 100644 --- a/rbf/cursor_test.go +++ b/rbf/cursor_test.go @@ -557,6 +557,44 @@ func TestCursor_RLETesting(t *testing.T) { }) } +func TestCursor_BitmapBitN(t *testing.T) { + db := MustOpenDB(t) + defer MustCloseDB(t, db) + tx := MustBegin(t, db, true) + defer tx.Rollback() + if err := tx.CreateBitmap("x"); err != nil { + t.Fatal(err) + } + bmData := make([]uint64, 1024) + for i := range bmData { + bmData[i] = 0x5555555555555555 + } + ct := roaring.NewContainerBitmap(-1, bmData) + err := tx.PutContainer("x", 0, ct) + if err != nil { + t.Fatalf("error writing container: %v", err) + } + // Putting a container to the same slot, which is also a bitmap + // (rather than a BitmapPtr), while the existing cell is a BitmapPtr, + // may not update BitN correctly. + ct, _ = ct.Add(1) + err = tx.PutContainer("x", 0, ct) + if err != nil { + t.Fatalf("rewriting container: %v", err) + } + v, err := tx.Container("x", 0) + if err != nil { + t.Fatalf("getting container: %v", err) + } + c1 := v.N() + v.Repair() + c2 := v.N() + if c1 != c2 { + t.Fatalf("expected count %d, got %d", c2, c1) + } + tx.Commit() +} + func TestCursor_RLEConversion(t *testing.T) { db := MustOpenDB(t) defer MustCloseDB(t, db)