1package bbolt_test 2 3import ( 4 "bytes" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "log" 9 "math/rand" 10 "os" 11 "strconv" 12 "strings" 13 "testing" 14 "testing/quick" 15 16 bolt "go.etcd.io/bbolt" 17) 18 19// Ensure that a bucket that gets a non-existent key returns nil. 20func TestBucket_Get_NonExistent(t *testing.T) { 21 db := MustOpenDB() 22 defer db.MustClose() 23 24 if err := db.Update(func(tx *bolt.Tx) error { 25 b, err := tx.CreateBucket([]byte("widgets")) 26 if err != nil { 27 t.Fatal(err) 28 } 29 if v := b.Get([]byte("foo")); v != nil { 30 t.Fatal("expected nil value") 31 } 32 return nil 33 }); err != nil { 34 t.Fatal(err) 35 } 36} 37 38// Ensure that a bucket can read a value that is not flushed yet. 39func TestBucket_Get_FromNode(t *testing.T) { 40 db := MustOpenDB() 41 defer db.MustClose() 42 43 if err := db.Update(func(tx *bolt.Tx) error { 44 b, err := tx.CreateBucket([]byte("widgets")) 45 if err != nil { 46 t.Fatal(err) 47 } 48 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 49 t.Fatal(err) 50 } 51 if v := b.Get([]byte("foo")); !bytes.Equal(v, []byte("bar")) { 52 t.Fatalf("unexpected value: %v", v) 53 } 54 return nil 55 }); err != nil { 56 t.Fatal(err) 57 } 58} 59 60// Ensure that a bucket retrieved via Get() returns a nil. 61func TestBucket_Get_IncompatibleValue(t *testing.T) { 62 db := MustOpenDB() 63 defer db.MustClose() 64 if err := db.Update(func(tx *bolt.Tx) error { 65 _, err := tx.CreateBucket([]byte("widgets")) 66 if err != nil { 67 t.Fatal(err) 68 } 69 70 if _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")); err != nil { 71 t.Fatal(err) 72 } 73 74 if tx.Bucket([]byte("widgets")).Get([]byte("foo")) != nil { 75 t.Fatal("expected nil value") 76 } 77 return nil 78 }); err != nil { 79 t.Fatal(err) 80 } 81} 82 83// Ensure that a slice returned from a bucket has a capacity equal to its length. 84// This also allows slices to be appended to since it will require a realloc by Go. 85// 86// https://github.com/boltdb/bolt/issues/544 87func TestBucket_Get_Capacity(t *testing.T) { 88 db := MustOpenDB() 89 defer db.MustClose() 90 91 // Write key to a bucket. 92 if err := db.Update(func(tx *bolt.Tx) error { 93 b, err := tx.CreateBucket([]byte("bucket")) 94 if err != nil { 95 return err 96 } 97 return b.Put([]byte("key"), []byte("val")) 98 }); err != nil { 99 t.Fatal(err) 100 } 101 102 // Retrieve value and attempt to append to it. 103 if err := db.Update(func(tx *bolt.Tx) error { 104 k, v := tx.Bucket([]byte("bucket")).Cursor().First() 105 106 // Verify capacity. 107 if len(k) != cap(k) { 108 t.Fatalf("unexpected key slice capacity: %d", cap(k)) 109 } else if len(v) != cap(v) { 110 t.Fatalf("unexpected value slice capacity: %d", cap(v)) 111 } 112 113 // Ensure slice can be appended to without a segfault. 114 k = append(k, []byte("123")...) 115 v = append(v, []byte("123")...) 116 _, _ = k, v // to pass ineffassign 117 118 return nil 119 }); err != nil { 120 t.Fatal(err) 121 } 122} 123 124// Ensure that a bucket can write a key/value. 125func TestBucket_Put(t *testing.T) { 126 db := MustOpenDB() 127 defer db.MustClose() 128 if err := db.Update(func(tx *bolt.Tx) error { 129 b, err := tx.CreateBucket([]byte("widgets")) 130 if err != nil { 131 t.Fatal(err) 132 } 133 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 134 t.Fatal(err) 135 } 136 137 v := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 138 if !bytes.Equal([]byte("bar"), v) { 139 t.Fatalf("unexpected value: %v", v) 140 } 141 return nil 142 }); err != nil { 143 t.Fatal(err) 144 } 145} 146 147// Ensure that a bucket can rewrite a key in the same transaction. 148func TestBucket_Put_Repeat(t *testing.T) { 149 db := MustOpenDB() 150 defer db.MustClose() 151 if err := db.Update(func(tx *bolt.Tx) error { 152 b, err := tx.CreateBucket([]byte("widgets")) 153 if err != nil { 154 t.Fatal(err) 155 } 156 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 157 t.Fatal(err) 158 } 159 if err := b.Put([]byte("foo"), []byte("baz")); err != nil { 160 t.Fatal(err) 161 } 162 163 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 164 if !bytes.Equal([]byte("baz"), value) { 165 t.Fatalf("unexpected value: %v", value) 166 } 167 return nil 168 }); err != nil { 169 t.Fatal(err) 170 } 171} 172 173// Ensure that a bucket can write a bunch of large values. 174func TestBucket_Put_Large(t *testing.T) { 175 db := MustOpenDB() 176 defer db.MustClose() 177 178 count, factor := 100, 200 179 if err := db.Update(func(tx *bolt.Tx) error { 180 b, err := tx.CreateBucket([]byte("widgets")) 181 if err != nil { 182 t.Fatal(err) 183 } 184 for i := 1; i < count; i++ { 185 if err := b.Put([]byte(strings.Repeat("0", i*factor)), []byte(strings.Repeat("X", (count-i)*factor))); err != nil { 186 t.Fatal(err) 187 } 188 } 189 return nil 190 }); err != nil { 191 t.Fatal(err) 192 } 193 194 if err := db.View(func(tx *bolt.Tx) error { 195 b := tx.Bucket([]byte("widgets")) 196 for i := 1; i < count; i++ { 197 value := b.Get([]byte(strings.Repeat("0", i*factor))) 198 if !bytes.Equal(value, []byte(strings.Repeat("X", (count-i)*factor))) { 199 t.Fatalf("unexpected value: %v", value) 200 } 201 } 202 return nil 203 }); err != nil { 204 t.Fatal(err) 205 } 206} 207 208// Ensure that a database can perform multiple large appends safely. 209func TestDB_Put_VeryLarge(t *testing.T) { 210 if testing.Short() { 211 t.Skip("skipping test in short mode.") 212 } 213 214 n, batchN := 400000, 200000 215 ksize, vsize := 8, 500 216 217 db := MustOpenDB() 218 defer db.MustClose() 219 220 for i := 0; i < n; i += batchN { 221 if err := db.Update(func(tx *bolt.Tx) error { 222 b, err := tx.CreateBucketIfNotExists([]byte("widgets")) 223 if err != nil { 224 t.Fatal(err) 225 } 226 for j := 0; j < batchN; j++ { 227 k, v := make([]byte, ksize), make([]byte, vsize) 228 binary.BigEndian.PutUint32(k, uint32(i+j)) 229 if err := b.Put(k, v); err != nil { 230 t.Fatal(err) 231 } 232 } 233 return nil 234 }); err != nil { 235 t.Fatal(err) 236 } 237 } 238} 239 240// Ensure that a setting a value on a key with a bucket value returns an error. 241func TestBucket_Put_IncompatibleValue(t *testing.T) { 242 db := MustOpenDB() 243 defer db.MustClose() 244 245 if err := db.Update(func(tx *bolt.Tx) error { 246 b0, err := tx.CreateBucket([]byte("widgets")) 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 if _, err := tx.Bucket([]byte("widgets")).CreateBucket([]byte("foo")); err != nil { 252 t.Fatal(err) 253 } 254 if err := b0.Put([]byte("foo"), []byte("bar")); err != bolt.ErrIncompatibleValue { 255 t.Fatalf("unexpected error: %s", err) 256 } 257 return nil 258 }); err != nil { 259 t.Fatal(err) 260 } 261} 262 263// Ensure that a setting a value while the transaction is closed returns an error. 264func TestBucket_Put_Closed(t *testing.T) { 265 db := MustOpenDB() 266 defer db.MustClose() 267 tx, err := db.Begin(true) 268 if err != nil { 269 t.Fatal(err) 270 } 271 272 b, err := tx.CreateBucket([]byte("widgets")) 273 if err != nil { 274 t.Fatal(err) 275 } 276 277 if err := tx.Rollback(); err != nil { 278 t.Fatal(err) 279 } 280 281 if err := b.Put([]byte("foo"), []byte("bar")); err != bolt.ErrTxClosed { 282 t.Fatalf("unexpected error: %s", err) 283 } 284} 285 286// Ensure that setting a value on a read-only bucket returns an error. 287func TestBucket_Put_ReadOnly(t *testing.T) { 288 db := MustOpenDB() 289 defer db.MustClose() 290 291 if err := db.Update(func(tx *bolt.Tx) error { 292 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 293 t.Fatal(err) 294 } 295 return nil 296 }); err != nil { 297 t.Fatal(err) 298 } 299 300 if err := db.View(func(tx *bolt.Tx) error { 301 b := tx.Bucket([]byte("widgets")) 302 if err := b.Put([]byte("foo"), []byte("bar")); err != bolt.ErrTxNotWritable { 303 t.Fatalf("unexpected error: %s", err) 304 } 305 return nil 306 }); err != nil { 307 t.Fatal(err) 308 } 309} 310 311// Ensure that a bucket can delete an existing key. 312func TestBucket_Delete(t *testing.T) { 313 db := MustOpenDB() 314 defer db.MustClose() 315 316 if err := db.Update(func(tx *bolt.Tx) error { 317 b, err := tx.CreateBucket([]byte("widgets")) 318 if err != nil { 319 t.Fatal(err) 320 } 321 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 322 t.Fatal(err) 323 } 324 if err := b.Delete([]byte("foo")); err != nil { 325 t.Fatal(err) 326 } 327 if v := b.Get([]byte("foo")); v != nil { 328 t.Fatalf("unexpected value: %v", v) 329 } 330 return nil 331 }); err != nil { 332 t.Fatal(err) 333 } 334} 335 336// Ensure that deleting a large set of keys will work correctly. 337func TestBucket_Delete_Large(t *testing.T) { 338 db := MustOpenDB() 339 defer db.MustClose() 340 341 if err := db.Update(func(tx *bolt.Tx) error { 342 b, err := tx.CreateBucket([]byte("widgets")) 343 if err != nil { 344 t.Fatal(err) 345 } 346 347 for i := 0; i < 100; i++ { 348 if err := b.Put([]byte(strconv.Itoa(i)), []byte(strings.Repeat("*", 1024))); err != nil { 349 t.Fatal(err) 350 } 351 } 352 353 return nil 354 }); err != nil { 355 t.Fatal(err) 356 } 357 358 if err := db.Update(func(tx *bolt.Tx) error { 359 b := tx.Bucket([]byte("widgets")) 360 for i := 0; i < 100; i++ { 361 if err := b.Delete([]byte(strconv.Itoa(i))); err != nil { 362 t.Fatal(err) 363 } 364 } 365 return nil 366 }); err != nil { 367 t.Fatal(err) 368 } 369 370 if err := db.View(func(tx *bolt.Tx) error { 371 b := tx.Bucket([]byte("widgets")) 372 for i := 0; i < 100; i++ { 373 if v := b.Get([]byte(strconv.Itoa(i))); v != nil { 374 t.Fatalf("unexpected value: %v, i=%d", v, i) 375 } 376 } 377 return nil 378 }); err != nil { 379 t.Fatal(err) 380 } 381} 382 383// Deleting a very large list of keys will cause the freelist to use overflow. 384func TestBucket_Delete_FreelistOverflow(t *testing.T) { 385 if testing.Short() { 386 t.Skip("skipping test in short mode.") 387 } 388 389 db := MustOpenDB() 390 defer db.MustClose() 391 392 k := make([]byte, 16) 393 for i := uint64(0); i < 10000; i++ { 394 if err := db.Update(func(tx *bolt.Tx) error { 395 b, err := tx.CreateBucketIfNotExists([]byte("0")) 396 if err != nil { 397 t.Fatalf("bucket error: %s", err) 398 } 399 400 for j := uint64(0); j < 1000; j++ { 401 binary.BigEndian.PutUint64(k[:8], i) 402 binary.BigEndian.PutUint64(k[8:], j) 403 if err := b.Put(k, nil); err != nil { 404 t.Fatalf("put error: %s", err) 405 } 406 } 407 408 return nil 409 }); err != nil { 410 t.Fatal(err) 411 } 412 } 413 414 // Delete all of them in one large transaction 415 if err := db.Update(func(tx *bolt.Tx) error { 416 b := tx.Bucket([]byte("0")) 417 c := b.Cursor() 418 for k, _ := c.First(); k != nil; k, _ = c.Next() { 419 if err := c.Delete(); err != nil { 420 t.Fatal(err) 421 } 422 } 423 return nil 424 }); err != nil { 425 t.Fatal(err) 426 } 427 428 // Check more than an overflow's worth of pages are freed. 429 stats := db.Stats() 430 freePages := stats.FreePageN + stats.PendingPageN 431 if freePages <= 0xFFFF { 432 t.Fatalf("expected more than 0xFFFF free pages, got %v", freePages) 433 } 434 435 // Free page count should be preserved on reopen. 436 if err := db.DB.Close(); err != nil { 437 t.Fatal(err) 438 } 439 db.MustReopen() 440 if reopenFreePages := db.Stats().FreePageN; freePages != reopenFreePages { 441 t.Fatalf("expected %d free pages, got %+v", freePages, db.Stats()) 442 } 443} 444 445// Ensure that deleting of non-existing key is a no-op. 446func TestBucket_Delete_NonExisting(t *testing.T) { 447 db := MustOpenDB() 448 defer db.MustClose() 449 450 if err := db.Update(func(tx *bolt.Tx) error { 451 b, err := tx.CreateBucket([]byte("widgets")) 452 if err != nil { 453 t.Fatal(err) 454 } 455 456 if _, err = b.CreateBucket([]byte("nested")); err != nil { 457 t.Fatal(err) 458 } 459 return nil 460 }); err != nil { 461 t.Fatal(err) 462 } 463 464 if err := db.Update(func(tx *bolt.Tx) error { 465 b := tx.Bucket([]byte("widgets")) 466 if err := b.Delete([]byte("foo")); err != nil { 467 t.Fatal(err) 468 } 469 if b.Bucket([]byte("nested")) == nil { 470 t.Fatal("nested bucket has been deleted") 471 } 472 return nil 473 }); err != nil { 474 t.Fatal(err) 475 } 476} 477 478// Ensure that accessing and updating nested buckets is ok across transactions. 479func TestBucket_Nested(t *testing.T) { 480 db := MustOpenDB() 481 defer db.MustClose() 482 483 if err := db.Update(func(tx *bolt.Tx) error { 484 // Create a widgets bucket. 485 b, err := tx.CreateBucket([]byte("widgets")) 486 if err != nil { 487 t.Fatal(err) 488 } 489 490 // Create a widgets/foo bucket. 491 _, err = b.CreateBucket([]byte("foo")) 492 if err != nil { 493 t.Fatal(err) 494 } 495 496 // Create a widgets/bar key. 497 if err := b.Put([]byte("bar"), []byte("0000")); err != nil { 498 t.Fatal(err) 499 } 500 501 return nil 502 }); err != nil { 503 t.Fatal(err) 504 } 505 db.MustCheck() 506 507 // Update widgets/bar. 508 if err := db.Update(func(tx *bolt.Tx) error { 509 b := tx.Bucket([]byte("widgets")) 510 if err := b.Put([]byte("bar"), []byte("xxxx")); err != nil { 511 t.Fatal(err) 512 } 513 return nil 514 }); err != nil { 515 t.Fatal(err) 516 } 517 db.MustCheck() 518 519 // Cause a split. 520 if err := db.Update(func(tx *bolt.Tx) error { 521 var b = tx.Bucket([]byte("widgets")) 522 for i := 0; i < 10000; i++ { 523 if err := b.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 524 t.Fatal(err) 525 } 526 } 527 return nil 528 }); err != nil { 529 t.Fatal(err) 530 } 531 db.MustCheck() 532 533 // Insert into widgets/foo/baz. 534 if err := db.Update(func(tx *bolt.Tx) error { 535 var b = tx.Bucket([]byte("widgets")) 536 if err := b.Bucket([]byte("foo")).Put([]byte("baz"), []byte("yyyy")); err != nil { 537 t.Fatal(err) 538 } 539 return nil 540 }); err != nil { 541 t.Fatal(err) 542 } 543 db.MustCheck() 544 545 // Verify. 546 if err := db.View(func(tx *bolt.Tx) error { 547 var b = tx.Bucket([]byte("widgets")) 548 if v := b.Bucket([]byte("foo")).Get([]byte("baz")); !bytes.Equal(v, []byte("yyyy")) { 549 t.Fatalf("unexpected value: %v", v) 550 } 551 if v := b.Get([]byte("bar")); !bytes.Equal(v, []byte("xxxx")) { 552 t.Fatalf("unexpected value: %v", v) 553 } 554 for i := 0; i < 10000; i++ { 555 if v := b.Get([]byte(strconv.Itoa(i))); !bytes.Equal(v, []byte(strconv.Itoa(i))) { 556 t.Fatalf("unexpected value: %v", v) 557 } 558 } 559 return nil 560 }); err != nil { 561 t.Fatal(err) 562 } 563} 564 565// Ensure that deleting a bucket using Delete() returns an error. 566func TestBucket_Delete_Bucket(t *testing.T) { 567 db := MustOpenDB() 568 defer db.MustClose() 569 if err := db.Update(func(tx *bolt.Tx) error { 570 b, err := tx.CreateBucket([]byte("widgets")) 571 if err != nil { 572 t.Fatal(err) 573 } 574 if _, err := b.CreateBucket([]byte("foo")); err != nil { 575 t.Fatal(err) 576 } 577 if err := b.Delete([]byte("foo")); err != bolt.ErrIncompatibleValue { 578 t.Fatalf("unexpected error: %s", err) 579 } 580 return nil 581 }); err != nil { 582 t.Fatal(err) 583 } 584} 585 586// Ensure that deleting a key on a read-only bucket returns an error. 587func TestBucket_Delete_ReadOnly(t *testing.T) { 588 db := MustOpenDB() 589 defer db.MustClose() 590 591 if err := db.Update(func(tx *bolt.Tx) error { 592 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 593 t.Fatal(err) 594 } 595 return nil 596 }); err != nil { 597 t.Fatal(err) 598 } 599 600 if err := db.View(func(tx *bolt.Tx) error { 601 if err := tx.Bucket([]byte("widgets")).Delete([]byte("foo")); err != bolt.ErrTxNotWritable { 602 t.Fatalf("unexpected error: %s", err) 603 } 604 return nil 605 }); err != nil { 606 t.Fatal(err) 607 } 608} 609 610// Ensure that a deleting value while the transaction is closed returns an error. 611func TestBucket_Delete_Closed(t *testing.T) { 612 db := MustOpenDB() 613 defer db.MustClose() 614 615 tx, err := db.Begin(true) 616 if err != nil { 617 t.Fatal(err) 618 } 619 620 b, err := tx.CreateBucket([]byte("widgets")) 621 if err != nil { 622 t.Fatal(err) 623 } 624 625 if err := tx.Rollback(); err != nil { 626 t.Fatal(err) 627 } 628 if err := b.Delete([]byte("foo")); err != bolt.ErrTxClosed { 629 t.Fatalf("unexpected error: %s", err) 630 } 631} 632 633// Ensure that deleting a bucket causes nested buckets to be deleted. 634func TestBucket_DeleteBucket_Nested(t *testing.T) { 635 db := MustOpenDB() 636 defer db.MustClose() 637 638 if err := db.Update(func(tx *bolt.Tx) error { 639 widgets, err := tx.CreateBucket([]byte("widgets")) 640 if err != nil { 641 t.Fatal(err) 642 } 643 644 foo, err := widgets.CreateBucket([]byte("foo")) 645 if err != nil { 646 t.Fatal(err) 647 } 648 649 bar, err := foo.CreateBucket([]byte("bar")) 650 if err != nil { 651 t.Fatal(err) 652 } 653 if err := bar.Put([]byte("baz"), []byte("bat")); err != nil { 654 t.Fatal(err) 655 } 656 if err := tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")); err != nil { 657 t.Fatal(err) 658 } 659 return nil 660 }); err != nil { 661 t.Fatal(err) 662 } 663} 664 665// Ensure that deleting a bucket causes nested buckets to be deleted after they have been committed. 666func TestBucket_DeleteBucket_Nested2(t *testing.T) { 667 db := MustOpenDB() 668 defer db.MustClose() 669 670 if err := db.Update(func(tx *bolt.Tx) error { 671 widgets, err := tx.CreateBucket([]byte("widgets")) 672 if err != nil { 673 t.Fatal(err) 674 } 675 676 foo, err := widgets.CreateBucket([]byte("foo")) 677 if err != nil { 678 t.Fatal(err) 679 } 680 681 bar, err := foo.CreateBucket([]byte("bar")) 682 if err != nil { 683 t.Fatal(err) 684 } 685 686 if err := bar.Put([]byte("baz"), []byte("bat")); err != nil { 687 t.Fatal(err) 688 } 689 return nil 690 }); err != nil { 691 t.Fatal(err) 692 } 693 694 if err := db.Update(func(tx *bolt.Tx) error { 695 widgets := tx.Bucket([]byte("widgets")) 696 if widgets == nil { 697 t.Fatal("expected widgets bucket") 698 } 699 700 foo := widgets.Bucket([]byte("foo")) 701 if foo == nil { 702 t.Fatal("expected foo bucket") 703 } 704 705 bar := foo.Bucket([]byte("bar")) 706 if bar == nil { 707 t.Fatal("expected bar bucket") 708 } 709 710 if v := bar.Get([]byte("baz")); !bytes.Equal(v, []byte("bat")) { 711 t.Fatalf("unexpected value: %v", v) 712 } 713 if err := tx.DeleteBucket([]byte("widgets")); err != nil { 714 t.Fatal(err) 715 } 716 return nil 717 }); err != nil { 718 t.Fatal(err) 719 } 720 721 if err := db.View(func(tx *bolt.Tx) error { 722 if tx.Bucket([]byte("widgets")) != nil { 723 t.Fatal("expected bucket to be deleted") 724 } 725 return nil 726 }); err != nil { 727 t.Fatal(err) 728 } 729} 730 731// Ensure that deleting a child bucket with multiple pages causes all pages to get collected. 732// NOTE: Consistency check in bolt_test.DB.Close() will panic if pages not freed properly. 733func TestBucket_DeleteBucket_Large(t *testing.T) { 734 db := MustOpenDB() 735 defer db.MustClose() 736 737 if err := db.Update(func(tx *bolt.Tx) error { 738 widgets, err := tx.CreateBucket([]byte("widgets")) 739 if err != nil { 740 t.Fatal(err) 741 } 742 743 foo, err := widgets.CreateBucket([]byte("foo")) 744 if err != nil { 745 t.Fatal(err) 746 } 747 748 for i := 0; i < 1000; i++ { 749 if err := foo.Put([]byte(fmt.Sprintf("%d", i)), []byte(fmt.Sprintf("%0100d", i))); err != nil { 750 t.Fatal(err) 751 } 752 } 753 return nil 754 }); err != nil { 755 t.Fatal(err) 756 } 757 758 if err := db.Update(func(tx *bolt.Tx) error { 759 if err := tx.DeleteBucket([]byte("widgets")); err != nil { 760 t.Fatal(err) 761 } 762 return nil 763 }); err != nil { 764 t.Fatal(err) 765 } 766} 767 768// Ensure that a simple value retrieved via Bucket() returns a nil. 769func TestBucket_Bucket_IncompatibleValue(t *testing.T) { 770 db := MustOpenDB() 771 defer db.MustClose() 772 773 if err := db.Update(func(tx *bolt.Tx) error { 774 widgets, err := tx.CreateBucket([]byte("widgets")) 775 if err != nil { 776 t.Fatal(err) 777 } 778 779 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 780 t.Fatal(err) 781 } 782 if b := tx.Bucket([]byte("widgets")).Bucket([]byte("foo")); b != nil { 783 t.Fatal("expected nil bucket") 784 } 785 return nil 786 }); err != nil { 787 t.Fatal(err) 788 } 789} 790 791// Ensure that creating a bucket on an existing non-bucket key returns an error. 792func TestBucket_CreateBucket_IncompatibleValue(t *testing.T) { 793 db := MustOpenDB() 794 defer db.MustClose() 795 if err := db.Update(func(tx *bolt.Tx) error { 796 widgets, err := tx.CreateBucket([]byte("widgets")) 797 if err != nil { 798 t.Fatal(err) 799 } 800 801 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 802 t.Fatal(err) 803 } 804 if _, err := widgets.CreateBucket([]byte("foo")); err != bolt.ErrIncompatibleValue { 805 t.Fatalf("unexpected error: %s", err) 806 } 807 return nil 808 }); err != nil { 809 t.Fatal(err) 810 } 811} 812 813// Ensure that deleting a bucket on an existing non-bucket key returns an error. 814func TestBucket_DeleteBucket_IncompatibleValue(t *testing.T) { 815 db := MustOpenDB() 816 defer db.MustClose() 817 818 if err := db.Update(func(tx *bolt.Tx) error { 819 widgets, err := tx.CreateBucket([]byte("widgets")) 820 if err != nil { 821 t.Fatal(err) 822 } 823 if err := widgets.Put([]byte("foo"), []byte("bar")); err != nil { 824 t.Fatal(err) 825 } 826 if err := tx.Bucket([]byte("widgets")).DeleteBucket([]byte("foo")); err != bolt.ErrIncompatibleValue { 827 t.Fatalf("unexpected error: %s", err) 828 } 829 return nil 830 }); err != nil { 831 t.Fatal(err) 832 } 833} 834 835// Ensure bucket can set and update its sequence number. 836func TestBucket_Sequence(t *testing.T) { 837 db := MustOpenDB() 838 defer db.MustClose() 839 840 if err := db.Update(func(tx *bolt.Tx) error { 841 bkt, err := tx.CreateBucket([]byte("0")) 842 if err != nil { 843 t.Fatal(err) 844 } 845 846 // Retrieve sequence. 847 if v := bkt.Sequence(); v != 0 { 848 t.Fatalf("unexpected sequence: %d", v) 849 } 850 851 // Update sequence. 852 if err := bkt.SetSequence(1000); err != nil { 853 t.Fatal(err) 854 } 855 856 // Read sequence again. 857 if v := bkt.Sequence(); v != 1000 { 858 t.Fatalf("unexpected sequence: %d", v) 859 } 860 861 return nil 862 }); err != nil { 863 t.Fatal(err) 864 } 865 866 // Verify sequence in separate transaction. 867 if err := db.View(func(tx *bolt.Tx) error { 868 if v := tx.Bucket([]byte("0")).Sequence(); v != 1000 { 869 t.Fatalf("unexpected sequence: %d", v) 870 } 871 return nil 872 }); err != nil { 873 t.Fatal(err) 874 } 875} 876 877// Ensure that a bucket can return an autoincrementing sequence. 878func TestBucket_NextSequence(t *testing.T) { 879 db := MustOpenDB() 880 defer db.MustClose() 881 882 if err := db.Update(func(tx *bolt.Tx) error { 883 widgets, err := tx.CreateBucket([]byte("widgets")) 884 if err != nil { 885 t.Fatal(err) 886 } 887 woojits, err := tx.CreateBucket([]byte("woojits")) 888 if err != nil { 889 t.Fatal(err) 890 } 891 892 // Make sure sequence increments. 893 if seq, err := widgets.NextSequence(); err != nil { 894 t.Fatal(err) 895 } else if seq != 1 { 896 t.Fatalf("unexpecte sequence: %d", seq) 897 } 898 899 if seq, err := widgets.NextSequence(); err != nil { 900 t.Fatal(err) 901 } else if seq != 2 { 902 t.Fatalf("unexpected sequence: %d", seq) 903 } 904 905 // Buckets should be separate. 906 if seq, err := woojits.NextSequence(); err != nil { 907 t.Fatal(err) 908 } else if seq != 1 { 909 t.Fatalf("unexpected sequence: %d", 1) 910 } 911 912 return nil 913 }); err != nil { 914 t.Fatal(err) 915 } 916} 917 918// Ensure that a bucket will persist an autoincrementing sequence even if its 919// the only thing updated on the bucket. 920// https://github.com/boltdb/bolt/issues/296 921func TestBucket_NextSequence_Persist(t *testing.T) { 922 db := MustOpenDB() 923 defer db.MustClose() 924 925 if err := db.Update(func(tx *bolt.Tx) error { 926 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 927 t.Fatal(err) 928 } 929 return nil 930 }); err != nil { 931 t.Fatal(err) 932 } 933 934 if err := db.Update(func(tx *bolt.Tx) error { 935 if _, err := tx.Bucket([]byte("widgets")).NextSequence(); err != nil { 936 t.Fatal(err) 937 } 938 return nil 939 }); err != nil { 940 t.Fatal(err) 941 } 942 943 if err := db.Update(func(tx *bolt.Tx) error { 944 seq, err := tx.Bucket([]byte("widgets")).NextSequence() 945 if err != nil { 946 t.Fatalf("unexpected error: %s", err) 947 } else if seq != 2 { 948 t.Fatalf("unexpected sequence: %d", seq) 949 } 950 return nil 951 }); err != nil { 952 t.Fatal(err) 953 } 954} 955 956// Ensure that retrieving the next sequence on a read-only bucket returns an error. 957func TestBucket_NextSequence_ReadOnly(t *testing.T) { 958 db := MustOpenDB() 959 defer db.MustClose() 960 961 if err := db.Update(func(tx *bolt.Tx) error { 962 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 963 t.Fatal(err) 964 } 965 return nil 966 }); err != nil { 967 t.Fatal(err) 968 } 969 970 if err := db.View(func(tx *bolt.Tx) error { 971 _, err := tx.Bucket([]byte("widgets")).NextSequence() 972 if err != bolt.ErrTxNotWritable { 973 t.Fatalf("unexpected error: %s", err) 974 } 975 return nil 976 }); err != nil { 977 t.Fatal(err) 978 } 979} 980 981// Ensure that retrieving the next sequence for a bucket on a closed database return an error. 982func TestBucket_NextSequence_Closed(t *testing.T) { 983 db := MustOpenDB() 984 defer db.MustClose() 985 tx, err := db.Begin(true) 986 if err != nil { 987 t.Fatal(err) 988 } 989 b, err := tx.CreateBucket([]byte("widgets")) 990 if err != nil { 991 t.Fatal(err) 992 } 993 if err := tx.Rollback(); err != nil { 994 t.Fatal(err) 995 } 996 if _, err := b.NextSequence(); err != bolt.ErrTxClosed { 997 t.Fatal(err) 998 } 999} 1000 1001// Ensure a user can loop over all key/value pairs in a bucket. 1002func TestBucket_ForEach(t *testing.T) { 1003 db := MustOpenDB() 1004 defer db.MustClose() 1005 1006 if err := db.Update(func(tx *bolt.Tx) error { 1007 b, err := tx.CreateBucket([]byte("widgets")) 1008 if err != nil { 1009 t.Fatal(err) 1010 } 1011 if err := b.Put([]byte("foo"), []byte("0000")); err != nil { 1012 t.Fatal(err) 1013 } 1014 if err := b.Put([]byte("baz"), []byte("0001")); err != nil { 1015 t.Fatal(err) 1016 } 1017 if err := b.Put([]byte("bar"), []byte("0002")); err != nil { 1018 t.Fatal(err) 1019 } 1020 1021 var index int 1022 if err := b.ForEach(func(k, v []byte) error { 1023 switch index { 1024 case 0: 1025 if !bytes.Equal(k, []byte("bar")) { 1026 t.Fatalf("unexpected key: %v", k) 1027 } else if !bytes.Equal(v, []byte("0002")) { 1028 t.Fatalf("unexpected value: %v", v) 1029 } 1030 case 1: 1031 if !bytes.Equal(k, []byte("baz")) { 1032 t.Fatalf("unexpected key: %v", k) 1033 } else if !bytes.Equal(v, []byte("0001")) { 1034 t.Fatalf("unexpected value: %v", v) 1035 } 1036 case 2: 1037 if !bytes.Equal(k, []byte("foo")) { 1038 t.Fatalf("unexpected key: %v", k) 1039 } else if !bytes.Equal(v, []byte("0000")) { 1040 t.Fatalf("unexpected value: %v", v) 1041 } 1042 } 1043 index++ 1044 return nil 1045 }); err != nil { 1046 t.Fatal(err) 1047 } 1048 1049 if index != 3 { 1050 t.Fatalf("unexpected index: %d", index) 1051 } 1052 1053 return nil 1054 }); err != nil { 1055 t.Fatal(err) 1056 } 1057} 1058 1059// Ensure a database can stop iteration early. 1060func TestBucket_ForEach_ShortCircuit(t *testing.T) { 1061 db := MustOpenDB() 1062 defer db.MustClose() 1063 if err := db.Update(func(tx *bolt.Tx) error { 1064 b, err := tx.CreateBucket([]byte("widgets")) 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 if err := b.Put([]byte("bar"), []byte("0000")); err != nil { 1069 t.Fatal(err) 1070 } 1071 if err := b.Put([]byte("baz"), []byte("0000")); err != nil { 1072 t.Fatal(err) 1073 } 1074 if err := b.Put([]byte("foo"), []byte("0000")); err != nil { 1075 t.Fatal(err) 1076 } 1077 1078 var index int 1079 if err := tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error { 1080 index++ 1081 if bytes.Equal(k, []byte("baz")) { 1082 return errors.New("marker") 1083 } 1084 return nil 1085 }); err == nil || err.Error() != "marker" { 1086 t.Fatalf("unexpected error: %s", err) 1087 } 1088 if index != 2 { 1089 t.Fatalf("unexpected index: %d", index) 1090 } 1091 1092 return nil 1093 }); err != nil { 1094 t.Fatal(err) 1095 } 1096} 1097 1098// Ensure that looping over a bucket on a closed database returns an error. 1099func TestBucket_ForEach_Closed(t *testing.T) { 1100 db := MustOpenDB() 1101 defer db.MustClose() 1102 1103 tx, err := db.Begin(true) 1104 if err != nil { 1105 t.Fatal(err) 1106 } 1107 1108 b, err := tx.CreateBucket([]byte("widgets")) 1109 if err != nil { 1110 t.Fatal(err) 1111 } 1112 1113 if err := tx.Rollback(); err != nil { 1114 t.Fatal(err) 1115 } 1116 1117 if err := b.ForEach(func(k, v []byte) error { return nil }); err != bolt.ErrTxClosed { 1118 t.Fatalf("unexpected error: %s", err) 1119 } 1120} 1121 1122// Ensure that an error is returned when inserting with an empty key. 1123func TestBucket_Put_EmptyKey(t *testing.T) { 1124 db := MustOpenDB() 1125 defer db.MustClose() 1126 1127 if err := db.Update(func(tx *bolt.Tx) error { 1128 b, err := tx.CreateBucket([]byte("widgets")) 1129 if err != nil { 1130 t.Fatal(err) 1131 } 1132 if err := b.Put([]byte(""), []byte("bar")); err != bolt.ErrKeyRequired { 1133 t.Fatalf("unexpected error: %s", err) 1134 } 1135 if err := b.Put(nil, []byte("bar")); err != bolt.ErrKeyRequired { 1136 t.Fatalf("unexpected error: %s", err) 1137 } 1138 return nil 1139 }); err != nil { 1140 t.Fatal(err) 1141 } 1142} 1143 1144// Ensure that an error is returned when inserting with a key that's too large. 1145func TestBucket_Put_KeyTooLarge(t *testing.T) { 1146 db := MustOpenDB() 1147 defer db.MustClose() 1148 if err := db.Update(func(tx *bolt.Tx) error { 1149 b, err := tx.CreateBucket([]byte("widgets")) 1150 if err != nil { 1151 t.Fatal(err) 1152 } 1153 if err := b.Put(make([]byte, 32769), []byte("bar")); err != bolt.ErrKeyTooLarge { 1154 t.Fatalf("unexpected error: %s", err) 1155 } 1156 return nil 1157 }); err != nil { 1158 t.Fatal(err) 1159 } 1160} 1161 1162// Ensure that an error is returned when inserting a value that's too large. 1163func TestBucket_Put_ValueTooLarge(t *testing.T) { 1164 // Skip this test on DroneCI because the machine is resource constrained. 1165 if os.Getenv("DRONE") == "true" { 1166 t.Skip("not enough RAM for test") 1167 } 1168 1169 db := MustOpenDB() 1170 defer db.MustClose() 1171 1172 if err := db.Update(func(tx *bolt.Tx) error { 1173 b, err := tx.CreateBucket([]byte("widgets")) 1174 if err != nil { 1175 t.Fatal(err) 1176 } 1177 if err := b.Put([]byte("foo"), make([]byte, bolt.MaxValueSize+1)); err != bolt.ErrValueTooLarge { 1178 t.Fatalf("unexpected error: %s", err) 1179 } 1180 return nil 1181 }); err != nil { 1182 t.Fatal(err) 1183 } 1184} 1185 1186// Ensure a bucket can calculate stats. 1187func TestBucket_Stats(t *testing.T) { 1188 db := MustOpenDB() 1189 defer db.MustClose() 1190 1191 // Add bucket with fewer keys but one big value. 1192 bigKey := []byte("really-big-value") 1193 for i := 0; i < 500; i++ { 1194 if err := db.Update(func(tx *bolt.Tx) error { 1195 b, err := tx.CreateBucketIfNotExists([]byte("woojits")) 1196 if err != nil { 1197 t.Fatal(err) 1198 } 1199 1200 if err := b.Put([]byte(fmt.Sprintf("%03d", i)), []byte(strconv.Itoa(i))); err != nil { 1201 t.Fatal(err) 1202 } 1203 return nil 1204 }); err != nil { 1205 t.Fatal(err) 1206 } 1207 } 1208 if err := db.Update(func(tx *bolt.Tx) error { 1209 if err := tx.Bucket([]byte("woojits")).Put(bigKey, []byte(strings.Repeat("*", 10000))); err != nil { 1210 t.Fatal(err) 1211 } 1212 return nil 1213 }); err != nil { 1214 t.Fatal(err) 1215 } 1216 1217 db.MustCheck() 1218 1219 if err := db.View(func(tx *bolt.Tx) error { 1220 stats := tx.Bucket([]byte("woojits")).Stats() 1221 if stats.BranchPageN != 1 { 1222 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1223 } else if stats.BranchOverflowN != 0 { 1224 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1225 } else if stats.LeafPageN != 7 { 1226 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1227 } else if stats.LeafOverflowN != 2 { 1228 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1229 } else if stats.KeyN != 501 { 1230 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1231 } else if stats.Depth != 2 { 1232 t.Fatalf("unexpected Depth: %d", stats.Depth) 1233 } 1234 1235 branchInuse := 16 // branch page header 1236 branchInuse += 7 * 16 // branch elements 1237 branchInuse += 7 * 3 // branch keys (6 3-byte keys) 1238 if stats.BranchInuse != branchInuse { 1239 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1240 } 1241 1242 leafInuse := 7 * 16 // leaf page header 1243 leafInuse += 501 * 16 // leaf elements 1244 leafInuse += 500*3 + len(bigKey) // leaf keys 1245 leafInuse += 1*10 + 2*90 + 3*400 + 10000 // leaf values 1246 if stats.LeafInuse != leafInuse { 1247 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1248 } 1249 1250 // Only check allocations for 4KB pages. 1251 if db.Info().PageSize == 4096 { 1252 if stats.BranchAlloc != 4096 { 1253 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1254 } else if stats.LeafAlloc != 36864 { 1255 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1256 } 1257 } 1258 1259 if stats.BucketN != 1 { 1260 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1261 } else if stats.InlineBucketN != 0 { 1262 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1263 } else if stats.InlineBucketInuse != 0 { 1264 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1265 } 1266 1267 return nil 1268 }); err != nil { 1269 t.Fatal(err) 1270 } 1271} 1272 1273// Ensure a bucket with random insertion utilizes fill percentage correctly. 1274func TestBucket_Stats_RandomFill(t *testing.T) { 1275 if testing.Short() { 1276 t.Skip("skipping test in short mode.") 1277 } else if os.Getpagesize() != 4096 { 1278 t.Skip("invalid page size for test") 1279 } 1280 1281 db := MustOpenDB() 1282 defer db.MustClose() 1283 1284 // Add a set of values in random order. It will be the same random 1285 // order so we can maintain consistency between test runs. 1286 var count int 1287 rand := rand.New(rand.NewSource(42)) 1288 for _, i := range rand.Perm(1000) { 1289 if err := db.Update(func(tx *bolt.Tx) error { 1290 b, err := tx.CreateBucketIfNotExists([]byte("woojits")) 1291 if err != nil { 1292 t.Fatal(err) 1293 } 1294 b.FillPercent = 0.9 1295 for _, j := range rand.Perm(100) { 1296 index := (j * 10000) + i 1297 if err := b.Put([]byte(fmt.Sprintf("%d000000000000000", index)), []byte("0000000000")); err != nil { 1298 t.Fatal(err) 1299 } 1300 count++ 1301 } 1302 return nil 1303 }); err != nil { 1304 t.Fatal(err) 1305 } 1306 } 1307 1308 db.MustCheck() 1309 1310 if err := db.View(func(tx *bolt.Tx) error { 1311 stats := tx.Bucket([]byte("woojits")).Stats() 1312 if stats.KeyN != 100000 { 1313 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1314 } 1315 1316 if stats.BranchPageN != 98 { 1317 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1318 } else if stats.BranchOverflowN != 0 { 1319 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1320 } else if stats.BranchInuse != 130984 { 1321 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1322 } else if stats.BranchAlloc != 401408 { 1323 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1324 } 1325 1326 if stats.LeafPageN != 3412 { 1327 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1328 } else if stats.LeafOverflowN != 0 { 1329 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1330 } else if stats.LeafInuse != 4742482 { 1331 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1332 } else if stats.LeafAlloc != 13975552 { 1333 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1334 } 1335 return nil 1336 }); err != nil { 1337 t.Fatal(err) 1338 } 1339} 1340 1341// Ensure a bucket can calculate stats. 1342func TestBucket_Stats_Small(t *testing.T) { 1343 db := MustOpenDB() 1344 defer db.MustClose() 1345 1346 if err := db.Update(func(tx *bolt.Tx) error { 1347 // Add a bucket that fits on a single root leaf. 1348 b, err := tx.CreateBucket([]byte("whozawhats")) 1349 if err != nil { 1350 t.Fatal(err) 1351 } 1352 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1353 t.Fatal(err) 1354 } 1355 1356 return nil 1357 }); err != nil { 1358 t.Fatal(err) 1359 } 1360 1361 db.MustCheck() 1362 1363 if err := db.View(func(tx *bolt.Tx) error { 1364 b := tx.Bucket([]byte("whozawhats")) 1365 stats := b.Stats() 1366 if stats.BranchPageN != 0 { 1367 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1368 } else if stats.BranchOverflowN != 0 { 1369 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1370 } else if stats.LeafPageN != 0 { 1371 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1372 } else if stats.LeafOverflowN != 0 { 1373 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1374 } else if stats.KeyN != 1 { 1375 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1376 } else if stats.Depth != 1 { 1377 t.Fatalf("unexpected Depth: %d", stats.Depth) 1378 } else if stats.BranchInuse != 0 { 1379 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1380 } else if stats.LeafInuse != 0 { 1381 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1382 } 1383 1384 if db.Info().PageSize == 4096 { 1385 if stats.BranchAlloc != 0 { 1386 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1387 } else if stats.LeafAlloc != 0 { 1388 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1389 } 1390 } 1391 1392 if stats.BucketN != 1 { 1393 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1394 } else if stats.InlineBucketN != 1 { 1395 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1396 } else if stats.InlineBucketInuse != 16+16+6 { 1397 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1398 } 1399 1400 return nil 1401 }); err != nil { 1402 t.Fatal(err) 1403 } 1404} 1405 1406func TestBucket_Stats_EmptyBucket(t *testing.T) { 1407 db := MustOpenDB() 1408 defer db.MustClose() 1409 1410 if err := db.Update(func(tx *bolt.Tx) error { 1411 // Add a bucket that fits on a single root leaf. 1412 if _, err := tx.CreateBucket([]byte("whozawhats")); err != nil { 1413 t.Fatal(err) 1414 } 1415 return nil 1416 }); err != nil { 1417 t.Fatal(err) 1418 } 1419 1420 db.MustCheck() 1421 1422 if err := db.View(func(tx *bolt.Tx) error { 1423 b := tx.Bucket([]byte("whozawhats")) 1424 stats := b.Stats() 1425 if stats.BranchPageN != 0 { 1426 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1427 } else if stats.BranchOverflowN != 0 { 1428 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1429 } else if stats.LeafPageN != 0 { 1430 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1431 } else if stats.LeafOverflowN != 0 { 1432 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1433 } else if stats.KeyN != 0 { 1434 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1435 } else if stats.Depth != 1 { 1436 t.Fatalf("unexpected Depth: %d", stats.Depth) 1437 } else if stats.BranchInuse != 0 { 1438 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1439 } else if stats.LeafInuse != 0 { 1440 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1441 } 1442 1443 if db.Info().PageSize == 4096 { 1444 if stats.BranchAlloc != 0 { 1445 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1446 } else if stats.LeafAlloc != 0 { 1447 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1448 } 1449 } 1450 1451 if stats.BucketN != 1 { 1452 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1453 } else if stats.InlineBucketN != 1 { 1454 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1455 } else if stats.InlineBucketInuse != 16 { 1456 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1457 } 1458 1459 return nil 1460 }); err != nil { 1461 t.Fatal(err) 1462 } 1463} 1464 1465// Ensure a bucket can calculate stats. 1466func TestBucket_Stats_Nested(t *testing.T) { 1467 db := MustOpenDB() 1468 defer db.MustClose() 1469 1470 if err := db.Update(func(tx *bolt.Tx) error { 1471 b, err := tx.CreateBucket([]byte("foo")) 1472 if err != nil { 1473 t.Fatal(err) 1474 } 1475 for i := 0; i < 100; i++ { 1476 if err := b.Put([]byte(fmt.Sprintf("%02d", i)), []byte(fmt.Sprintf("%02d", i))); err != nil { 1477 t.Fatal(err) 1478 } 1479 } 1480 1481 bar, err := b.CreateBucket([]byte("bar")) 1482 if err != nil { 1483 t.Fatal(err) 1484 } 1485 for i := 0; i < 10; i++ { 1486 if err := bar.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 1487 t.Fatal(err) 1488 } 1489 } 1490 1491 baz, err := bar.CreateBucket([]byte("baz")) 1492 if err != nil { 1493 t.Fatal(err) 1494 } 1495 for i := 0; i < 10; i++ { 1496 if err := baz.Put([]byte(strconv.Itoa(i)), []byte(strconv.Itoa(i))); err != nil { 1497 t.Fatal(err) 1498 } 1499 } 1500 1501 return nil 1502 }); err != nil { 1503 t.Fatal(err) 1504 } 1505 1506 db.MustCheck() 1507 1508 if err := db.View(func(tx *bolt.Tx) error { 1509 b := tx.Bucket([]byte("foo")) 1510 stats := b.Stats() 1511 if stats.BranchPageN != 0 { 1512 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1513 } else if stats.BranchOverflowN != 0 { 1514 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1515 } else if stats.LeafPageN != 2 { 1516 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1517 } else if stats.LeafOverflowN != 0 { 1518 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1519 } else if stats.KeyN != 122 { 1520 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1521 } else if stats.Depth != 3 { 1522 t.Fatalf("unexpected Depth: %d", stats.Depth) 1523 } else if stats.BranchInuse != 0 { 1524 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1525 } 1526 1527 foo := 16 // foo (pghdr) 1528 foo += 101 * 16 // foo leaf elements 1529 foo += 100*2 + 100*2 // foo leaf key/values 1530 foo += 3 + 16 // foo -> bar key/value 1531 1532 bar := 16 // bar (pghdr) 1533 bar += 11 * 16 // bar leaf elements 1534 bar += 10 + 10 // bar leaf key/values 1535 bar += 3 + 16 // bar -> baz key/value 1536 1537 baz := 16 // baz (inline) (pghdr) 1538 baz += 10 * 16 // baz leaf elements 1539 baz += 10 + 10 // baz leaf key/values 1540 1541 if stats.LeafInuse != foo+bar+baz { 1542 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1543 } 1544 1545 if db.Info().PageSize == 4096 { 1546 if stats.BranchAlloc != 0 { 1547 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1548 } else if stats.LeafAlloc != 8192 { 1549 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1550 } 1551 } 1552 1553 if stats.BucketN != 3 { 1554 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1555 } else if stats.InlineBucketN != 1 { 1556 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1557 } else if stats.InlineBucketInuse != baz { 1558 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1559 } 1560 1561 return nil 1562 }); err != nil { 1563 t.Fatal(err) 1564 } 1565} 1566 1567// Ensure a large bucket can calculate stats. 1568func TestBucket_Stats_Large(t *testing.T) { 1569 if testing.Short() { 1570 t.Skip("skipping test in short mode.") 1571 } 1572 1573 db := MustOpenDB() 1574 defer db.MustClose() 1575 1576 var index int 1577 for i := 0; i < 100; i++ { 1578 // Add bucket with lots of keys. 1579 if err := db.Update(func(tx *bolt.Tx) error { 1580 b, err := tx.CreateBucketIfNotExists([]byte("widgets")) 1581 if err != nil { 1582 t.Fatal(err) 1583 } 1584 for i := 0; i < 1000; i++ { 1585 if err := b.Put([]byte(strconv.Itoa(index)), []byte(strconv.Itoa(index))); err != nil { 1586 t.Fatal(err) 1587 } 1588 index++ 1589 } 1590 return nil 1591 }); err != nil { 1592 t.Fatal(err) 1593 } 1594 } 1595 1596 db.MustCheck() 1597 1598 if err := db.View(func(tx *bolt.Tx) error { 1599 stats := tx.Bucket([]byte("widgets")).Stats() 1600 if stats.BranchPageN != 13 { 1601 t.Fatalf("unexpected BranchPageN: %d", stats.BranchPageN) 1602 } else if stats.BranchOverflowN != 0 { 1603 t.Fatalf("unexpected BranchOverflowN: %d", stats.BranchOverflowN) 1604 } else if stats.LeafPageN != 1196 { 1605 t.Fatalf("unexpected LeafPageN: %d", stats.LeafPageN) 1606 } else if stats.LeafOverflowN != 0 { 1607 t.Fatalf("unexpected LeafOverflowN: %d", stats.LeafOverflowN) 1608 } else if stats.KeyN != 100000 { 1609 t.Fatalf("unexpected KeyN: %d", stats.KeyN) 1610 } else if stats.Depth != 3 { 1611 t.Fatalf("unexpected Depth: %d", stats.Depth) 1612 } else if stats.BranchInuse != 25257 { 1613 t.Fatalf("unexpected BranchInuse: %d", stats.BranchInuse) 1614 } else if stats.LeafInuse != 2596916 { 1615 t.Fatalf("unexpected LeafInuse: %d", stats.LeafInuse) 1616 } 1617 1618 if db.Info().PageSize == 4096 { 1619 if stats.BranchAlloc != 53248 { 1620 t.Fatalf("unexpected BranchAlloc: %d", stats.BranchAlloc) 1621 } else if stats.LeafAlloc != 4898816 { 1622 t.Fatalf("unexpected LeafAlloc: %d", stats.LeafAlloc) 1623 } 1624 } 1625 1626 if stats.BucketN != 1 { 1627 t.Fatalf("unexpected BucketN: %d", stats.BucketN) 1628 } else if stats.InlineBucketN != 0 { 1629 t.Fatalf("unexpected InlineBucketN: %d", stats.InlineBucketN) 1630 } else if stats.InlineBucketInuse != 0 { 1631 t.Fatalf("unexpected InlineBucketInuse: %d", stats.InlineBucketInuse) 1632 } 1633 1634 return nil 1635 }); err != nil { 1636 t.Fatal(err) 1637 } 1638} 1639 1640// Ensure that a bucket can write random keys and values across multiple transactions. 1641func TestBucket_Put_Single(t *testing.T) { 1642 if testing.Short() { 1643 t.Skip("skipping test in short mode.") 1644 } 1645 1646 index := 0 1647 if err := quick.Check(func(items testdata) bool { 1648 db := MustOpenDB() 1649 defer db.MustClose() 1650 1651 m := make(map[string][]byte) 1652 1653 if err := db.Update(func(tx *bolt.Tx) error { 1654 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1655 t.Fatal(err) 1656 } 1657 return nil 1658 }); err != nil { 1659 t.Fatal(err) 1660 } 1661 1662 for _, item := range items { 1663 if err := db.Update(func(tx *bolt.Tx) error { 1664 if err := tx.Bucket([]byte("widgets")).Put(item.Key, item.Value); err != nil { 1665 panic("put error: " + err.Error()) 1666 } 1667 m[string(item.Key)] = item.Value 1668 return nil 1669 }); err != nil { 1670 t.Fatal(err) 1671 } 1672 1673 // Verify all key/values so far. 1674 if err := db.View(func(tx *bolt.Tx) error { 1675 i := 0 1676 for k, v := range m { 1677 value := tx.Bucket([]byte("widgets")).Get([]byte(k)) 1678 if !bytes.Equal(value, v) { 1679 t.Logf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v) 1680 db.CopyTempFile() 1681 t.FailNow() 1682 } 1683 i++ 1684 } 1685 return nil 1686 }); err != nil { 1687 t.Fatal(err) 1688 } 1689 } 1690 1691 index++ 1692 return true 1693 }, qconfig()); err != nil { 1694 t.Error(err) 1695 } 1696} 1697 1698// Ensure that a transaction can insert multiple key/value pairs at once. 1699func TestBucket_Put_Multiple(t *testing.T) { 1700 if testing.Short() { 1701 t.Skip("skipping test in short mode.") 1702 } 1703 1704 if err := quick.Check(func(items testdata) bool { 1705 db := MustOpenDB() 1706 defer db.MustClose() 1707 1708 // Bulk insert all values. 1709 if err := db.Update(func(tx *bolt.Tx) error { 1710 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1711 t.Fatal(err) 1712 } 1713 return nil 1714 }); err != nil { 1715 t.Fatal(err) 1716 } 1717 1718 if err := db.Update(func(tx *bolt.Tx) error { 1719 b := tx.Bucket([]byte("widgets")) 1720 for _, item := range items { 1721 if err := b.Put(item.Key, item.Value); err != nil { 1722 t.Fatal(err) 1723 } 1724 } 1725 return nil 1726 }); err != nil { 1727 t.Fatal(err) 1728 } 1729 1730 // Verify all items exist. 1731 if err := db.View(func(tx *bolt.Tx) error { 1732 b := tx.Bucket([]byte("widgets")) 1733 for _, item := range items { 1734 value := b.Get(item.Key) 1735 if !bytes.Equal(item.Value, value) { 1736 db.CopyTempFile() 1737 t.Fatalf("exp=%x; got=%x", item.Value, value) 1738 } 1739 } 1740 return nil 1741 }); err != nil { 1742 t.Fatal(err) 1743 } 1744 1745 return true 1746 }, qconfig()); err != nil { 1747 t.Error(err) 1748 } 1749} 1750 1751// Ensure that a transaction can delete all key/value pairs and return to a single leaf page. 1752func TestBucket_Delete_Quick(t *testing.T) { 1753 if testing.Short() { 1754 t.Skip("skipping test in short mode.") 1755 } 1756 1757 if err := quick.Check(func(items testdata) bool { 1758 db := MustOpenDB() 1759 defer db.MustClose() 1760 1761 // Bulk insert all values. 1762 if err := db.Update(func(tx *bolt.Tx) error { 1763 if _, err := tx.CreateBucket([]byte("widgets")); err != nil { 1764 t.Fatal(err) 1765 } 1766 return nil 1767 }); err != nil { 1768 t.Fatal(err) 1769 } 1770 1771 if err := db.Update(func(tx *bolt.Tx) error { 1772 b := tx.Bucket([]byte("widgets")) 1773 for _, item := range items { 1774 if err := b.Put(item.Key, item.Value); err != nil { 1775 t.Fatal(err) 1776 } 1777 } 1778 return nil 1779 }); err != nil { 1780 t.Fatal(err) 1781 } 1782 1783 // Remove items one at a time and check consistency. 1784 for _, item := range items { 1785 if err := db.Update(func(tx *bolt.Tx) error { 1786 return tx.Bucket([]byte("widgets")).Delete(item.Key) 1787 }); err != nil { 1788 t.Fatal(err) 1789 } 1790 } 1791 1792 // Anything before our deletion index should be nil. 1793 if err := db.View(func(tx *bolt.Tx) error { 1794 if err := tx.Bucket([]byte("widgets")).ForEach(func(k, v []byte) error { 1795 t.Fatalf("bucket should be empty; found: %06x", trunc(k, 3)) 1796 return nil 1797 }); err != nil { 1798 t.Fatal(err) 1799 } 1800 return nil 1801 }); err != nil { 1802 t.Fatal(err) 1803 } 1804 1805 return true 1806 }, qconfig()); err != nil { 1807 t.Error(err) 1808 } 1809} 1810 1811func ExampleBucket_Put() { 1812 // Open the database. 1813 db, err := bolt.Open(tempfile(), 0666, nil) 1814 if err != nil { 1815 log.Fatal(err) 1816 } 1817 defer os.Remove(db.Path()) 1818 1819 // Start a write transaction. 1820 if err := db.Update(func(tx *bolt.Tx) error { 1821 // Create a bucket. 1822 b, err := tx.CreateBucket([]byte("widgets")) 1823 if err != nil { 1824 return err 1825 } 1826 1827 // Set the value "bar" for the key "foo". 1828 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1829 return err 1830 } 1831 return nil 1832 }); err != nil { 1833 log.Fatal(err) 1834 } 1835 1836 // Read value back in a different read-only transaction. 1837 if err := db.View(func(tx *bolt.Tx) error { 1838 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 1839 fmt.Printf("The value of 'foo' is: %s\n", value) 1840 return nil 1841 }); err != nil { 1842 log.Fatal(err) 1843 } 1844 1845 // Close database to release file lock. 1846 if err := db.Close(); err != nil { 1847 log.Fatal(err) 1848 } 1849 1850 // Output: 1851 // The value of 'foo' is: bar 1852} 1853 1854func ExampleBucket_Delete() { 1855 // Open the database. 1856 db, err := bolt.Open(tempfile(), 0666, nil) 1857 if err != nil { 1858 log.Fatal(err) 1859 } 1860 defer os.Remove(db.Path()) 1861 1862 // Start a write transaction. 1863 if err := db.Update(func(tx *bolt.Tx) error { 1864 // Create a bucket. 1865 b, err := tx.CreateBucket([]byte("widgets")) 1866 if err != nil { 1867 return err 1868 } 1869 1870 // Set the value "bar" for the key "foo". 1871 if err := b.Put([]byte("foo"), []byte("bar")); err != nil { 1872 return err 1873 } 1874 1875 // Retrieve the key back from the database and verify it. 1876 value := b.Get([]byte("foo")) 1877 fmt.Printf("The value of 'foo' was: %s\n", value) 1878 1879 return nil 1880 }); err != nil { 1881 log.Fatal(err) 1882 } 1883 1884 // Delete the key in a different write transaction. 1885 if err := db.Update(func(tx *bolt.Tx) error { 1886 return tx.Bucket([]byte("widgets")).Delete([]byte("foo")) 1887 }); err != nil { 1888 log.Fatal(err) 1889 } 1890 1891 // Retrieve the key again. 1892 if err := db.View(func(tx *bolt.Tx) error { 1893 value := tx.Bucket([]byte("widgets")).Get([]byte("foo")) 1894 if value == nil { 1895 fmt.Printf("The value of 'foo' is now: nil\n") 1896 } 1897 return nil 1898 }); err != nil { 1899 log.Fatal(err) 1900 } 1901 1902 // Close database to release file lock. 1903 if err := db.Close(); err != nil { 1904 log.Fatal(err) 1905 } 1906 1907 // Output: 1908 // The value of 'foo' was: bar 1909 // The value of 'foo' is now: nil 1910} 1911 1912func ExampleBucket_ForEach() { 1913 // Open the database. 1914 db, err := bolt.Open(tempfile(), 0666, nil) 1915 if err != nil { 1916 log.Fatal(err) 1917 } 1918 defer os.Remove(db.Path()) 1919 1920 // Insert data into a bucket. 1921 if err := db.Update(func(tx *bolt.Tx) error { 1922 b, err := tx.CreateBucket([]byte("animals")) 1923 if err != nil { 1924 return err 1925 } 1926 1927 if err := b.Put([]byte("dog"), []byte("fun")); err != nil { 1928 return err 1929 } 1930 if err := b.Put([]byte("cat"), []byte("lame")); err != nil { 1931 return err 1932 } 1933 if err := b.Put([]byte("liger"), []byte("awesome")); err != nil { 1934 return err 1935 } 1936 1937 // Iterate over items in sorted key order. 1938 if err := b.ForEach(func(k, v []byte) error { 1939 fmt.Printf("A %s is %s.\n", k, v) 1940 return nil 1941 }); err != nil { 1942 return err 1943 } 1944 1945 return nil 1946 }); err != nil { 1947 log.Fatal(err) 1948 } 1949 1950 // Close database to release file lock. 1951 if err := db.Close(); err != nil { 1952 log.Fatal(err) 1953 } 1954 1955 // Output: 1956 // A cat is lame. 1957 // A dog is fun. 1958 // A liger is awesome. 1959} 1960