From 2321036228b5d24b480e1ad1c20aae65c28e521b Mon Sep 17 00:00:00 2001
From: Ben Johnson <benbjohnson@yahoo.com>
Date: Fri, 6 Jun 2014 17:14:17 -0600
Subject: [PATCH] Fix double free in merge-left rebalance.

This commit fixes a bug where deletions that caused merge-left rebalances were updating
the parent node which caused a node to "reappear" even after it had been deleted. This was
fixed in merge-right rebalances a while ago but merge-left is less frequent so it was
missed until now.

Many thanks to Jordan Sherer (@jsherer) for finding and reporting the bug.
---
 bucket_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++
 node.go        |  1 -
 2 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/bucket_test.go b/bucket_test.go
index bae3941..21ddfca 100644
--- a/bucket_test.go
+++ b/bucket_test.go
@@ -2,6 +2,7 @@ package bolt
 
 import (
 	"bytes"
+	"encoding/binary"
 	"errors"
 	"fmt"
 	"math/rand"
@@ -189,6 +190,50 @@ func TestBucket_Delete_Large(t *testing.T) {
 	})
 }
 
+// Ensure that deleting a large set of keys will work correctly.
+// Reported by Jordan Sherer: https://github.com/boltdb/bolt/issues/184
+func TestBucket_Delete_Large2(t *testing.T) {
+	if testing.Short() {
+		t.Skip("skipping test in short mode.")
+	}
+
+	withOpenDB(func(db *DB, path string) {
+		k := make([]byte, 16)
+		for i := uint64(0); i < 10000; i++ {
+			err := db.Update(func(tx *Tx) error {
+				b, err := tx.CreateBucketIfNotExists([]byte("0"))
+				if err != nil {
+					t.Fatalf("bucket error: %s", err)
+				}
+
+				for j := uint64(0); j < 1000; j++ {
+					binary.BigEndian.PutUint64(k[:8], i)
+					binary.BigEndian.PutUint64(k[8:], j)
+					if err := b.Put(k, nil); err != nil {
+						t.Fatalf("put error: %s", err)
+					}
+				}
+
+				return nil
+			})
+
+			if err != nil {
+				t.Fatalf("update error: %s", err)
+			}
+		}
+
+		// Delete all of them in one large transaction
+		db.Update(func(tx *Tx) error {
+			b := tx.Bucket([]byte("0"))
+			c := b.Cursor()
+			for k, _ := c.First(); k != nil; k, _ = c.Next() {
+				b.Delete(k)
+			}
+			return nil
+		})
+	})
+}
+
 // Ensure that accessing and updating nested buckets is ok across transactions.
 func TestBucket_Nested(t *testing.T) {
 	withOpenDB(func(db *DB, path string) {
diff --git a/node.go b/node.go
index f0978ca..1865ffe 100644
--- a/node.go
+++ b/node.go
@@ -486,7 +486,6 @@ func (n *node) rebalance() {
 		target.inodes = append(target.inodes, n.inodes...)
 		n.parent.del(n.key)
 		n.parent.removeChild(n)
-		n.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0)
 		delete(n.bucket.nodes, n.pgid)
 		n.free()
 	}