table/tables: use red black tree to implement memory table.

This commit is contained in:
Ewan Chou
2016-02-25 11:41:49 +08:00
parent cc298dd6ea
commit 3cc6bf035f
15 changed files with 1127 additions and 167 deletions

4
Godeps/Godeps.json generated
View File

@ -35,6 +35,10 @@
"ImportPath": "github.com/peterh/liner",
"Rev": "1bb0d1c1a25ed393d8feb09bab039b2b1b1fbced"
},
{
"ImportPath": "github.com/petar/GoLLRB/llrb",
"Rev": "53be0d36a84c2a886ca057d34b6aa4468df9ccb4"
},
{
"ImportPath": "github.com/pingcap/check",
"Rev": "ce8a2f822ab1e245a4eefcef2996531c79c943f1"

View File

@ -0,0 +1,39 @@
// Copyright 2010 Petar Maymounkov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package llrb
import "math"
// avgVar maintains the average and variance of a stream of numbers
// in a space-efficient manner.
type avgVar struct {
count int64
sum, sumsq float64
}
func (av *avgVar) Init() {
av.count = 0
av.sum = 0.0
av.sumsq = 0.0
}
func (av *avgVar) Add(sample float64) {
av.count++
av.sum += sample
av.sumsq += sample * sample
}
func (av *avgVar) GetCount() int64 { return av.count }
func (av *avgVar) GetAvg() float64 { return av.sum / float64(av.count) }
func (av *avgVar) GetTotal() float64 { return av.sum }
func (av *avgVar) GetVar() float64 {
a := av.GetAvg()
return av.sumsq/float64(av.count) - a*a
}
func (av *avgVar) GetStdDev() float64 { return math.Sqrt(av.GetVar()) }

View File

@ -0,0 +1,93 @@
package llrb
type ItemIterator func(i Item) bool
//func (t *Tree) Ascend(iterator ItemIterator) {
// t.AscendGreaterOrEqual(Inf(-1), iterator)
//}
func (t *LLRB) AscendRange(greaterOrEqual, lessThan Item, iterator ItemIterator) {
t.ascendRange(t.root, greaterOrEqual, lessThan, iterator)
}
func (t *LLRB) ascendRange(h *Node, inf, sup Item, iterator ItemIterator) bool {
if h == nil {
return true
}
if !less(h.Item, sup) {
return t.ascendRange(h.Left, inf, sup, iterator)
}
if less(h.Item, inf) {
return t.ascendRange(h.Right, inf, sup, iterator)
}
if !t.ascendRange(h.Left, inf, sup, iterator) {
return false
}
if !iterator(h.Item) {
return false
}
return t.ascendRange(h.Right, inf, sup, iterator)
}
// AscendGreaterOrEqual will call iterator once for each element greater or equal to
// pivot in ascending order. It will stop whenever the iterator returns false.
func (t *LLRB) AscendGreaterOrEqual(pivot Item, iterator ItemIterator) {
t.ascendGreaterOrEqual(t.root, pivot, iterator)
}
func (t *LLRB) ascendGreaterOrEqual(h *Node, pivot Item, iterator ItemIterator) bool {
if h == nil {
return true
}
if !less(h.Item, pivot) {
if !t.ascendGreaterOrEqual(h.Left, pivot, iterator) {
return false
}
if !iterator(h.Item) {
return false
}
}
return t.ascendGreaterOrEqual(h.Right, pivot, iterator)
}
func (t *LLRB) AscendLessThan(pivot Item, iterator ItemIterator) {
t.ascendLessThan(t.root, pivot, iterator)
}
func (t *LLRB) ascendLessThan(h *Node, pivot Item, iterator ItemIterator) bool {
if h == nil {
return true
}
if !t.ascendLessThan(h.Left, pivot, iterator) {
return false
}
if !iterator(h.Item) {
return false
}
if less(h.Item, pivot) {
return t.ascendLessThan(h.Left, pivot, iterator)
}
return true
}
// DescendLessOrEqual will call iterator once for each element less than the
// pivot in descending order. It will stop whenever the iterator returns false.
func (t *LLRB) DescendLessOrEqual(pivot Item, iterator ItemIterator) {
t.descendLessOrEqual(t.root, pivot, iterator)
}
func (t *LLRB) descendLessOrEqual(h *Node, pivot Item, iterator ItemIterator) bool {
if h == nil {
return true
}
if less(h.Item, pivot) || !less(pivot, h.Item) {
if !t.descendLessOrEqual(h.Right, pivot, iterator) {
return false
}
if !iterator(h.Item) {
return false
}
}
return t.descendLessOrEqual(h.Left, pivot, iterator)
}

View File

@ -0,0 +1,76 @@
package llrb
import (
"reflect"
"testing"
)
func TestAscendGreaterOrEqual(t *testing.T) {
tree := New()
tree.InsertNoReplace(Int(4))
tree.InsertNoReplace(Int(6))
tree.InsertNoReplace(Int(1))
tree.InsertNoReplace(Int(3))
var ary []Item
tree.AscendGreaterOrEqual(Int(-1), func(i Item) bool {
ary = append(ary, i)
return true
})
expected := []Item{Int(1), Int(3), Int(4), Int(6)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
ary = nil
tree.AscendGreaterOrEqual(Int(3), func(i Item) bool {
ary = append(ary, i)
return true
})
expected = []Item{Int(3), Int(4), Int(6)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
ary = nil
tree.AscendGreaterOrEqual(Int(2), func(i Item) bool {
ary = append(ary, i)
return true
})
expected = []Item{Int(3), Int(4), Int(6)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
}
func TestDescendLessOrEqual(t *testing.T) {
tree := New()
tree.InsertNoReplace(Int(4))
tree.InsertNoReplace(Int(6))
tree.InsertNoReplace(Int(1))
tree.InsertNoReplace(Int(3))
var ary []Item
tree.DescendLessOrEqual(Int(10), func(i Item) bool {
ary = append(ary, i)
return true
})
expected := []Item{Int(6), Int(4), Int(3), Int(1)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
ary = nil
tree.DescendLessOrEqual(Int(4), func(i Item) bool {
ary = append(ary, i)
return true
})
expected = []Item{Int(4), Int(3), Int(1)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
ary = nil
tree.DescendLessOrEqual(Int(5), func(i Item) bool {
ary = append(ary, i)
return true
})
expected = []Item{Int(4), Int(3), Int(1)}
if !reflect.DeepEqual(ary, expected) {
t.Errorf("expected %v but got %v", expected, ary)
}
}

View File

@ -0,0 +1,46 @@
// Copyright 2010 Petar Maymounkov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package llrb
// GetHeight() returns an item in the tree with key @key, and it's height in the tree
func (t *LLRB) GetHeight(key Item) (result Item, depth int) {
return t.getHeight(t.root, key)
}
func (t *LLRB) getHeight(h *Node, item Item) (Item, int) {
if h == nil {
return nil, 0
}
if less(item, h.Item) {
result, depth := t.getHeight(h.Left, item)
return result, depth + 1
}
if less(h.Item, item) {
result, depth := t.getHeight(h.Right, item)
return result, depth + 1
}
return h.Item, 0
}
// HeightStats() returns the average and standard deviation of the height
// of elements in the tree
func (t *LLRB) HeightStats() (avg, stddev float64) {
av := &avgVar{}
heightStats(t.root, 0, av)
return av.GetAvg(), av.GetStdDev()
}
func heightStats(h *Node, d int, av *avgVar) {
if h == nil {
return
}
av.Add(float64(d))
if h.Left != nil {
heightStats(h.Left, d+1, av)
}
if h.Right != nil {
heightStats(h.Right, d+1, av)
}
}

View File

@ -0,0 +1,456 @@
// Copyright 2010 Petar Maymounkov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// A Left-Leaning Red-Black (LLRB) implementation of 2-3 balanced binary search trees,
// based on the following work:
//
// http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf
// http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
// http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java
//
// 2-3 trees (and the run-time equivalent 2-3-4 trees) are the de facto standard BST
// algoritms found in implementations of Python, Java, and other libraries. The LLRB
// implementation of 2-3 trees is a recent improvement on the traditional implementation,
// observed and documented by Robert Sedgewick.
//
package llrb
// Tree is a Left-Leaning Red-Black (LLRB) implementation of 2-3 trees
type LLRB struct {
count int
root *Node
}
type Node struct {
Item
Left, Right *Node // Pointers to left and right child nodes
Black bool // If set, the color of the link (incoming from the parent) is black
// In the LLRB, new nodes are always red, hence the zero-value for node
}
type Item interface {
Less(than Item) bool
}
//
func less(x, y Item) bool {
if x == pinf {
return false
}
if x == ninf {
return true
}
return x.Less(y)
}
// Inf returns an Item that is "bigger than" any other item, if sign is positive.
// Otherwise it returns an Item that is "smaller than" any other item.
func Inf(sign int) Item {
if sign == 0 {
panic("sign")
}
if sign > 0 {
return pinf
}
return ninf
}
var (
ninf = nInf{}
pinf = pInf{}
)
type nInf struct{}
func (nInf) Less(Item) bool {
return true
}
type pInf struct{}
func (pInf) Less(Item) bool {
return false
}
// New() allocates a new tree
func New() *LLRB {
return &LLRB{}
}
// SetRoot sets the root node of the tree.
// It is intended to be used by functions that deserialize the tree.
func (t *LLRB) SetRoot(r *Node) {
t.root = r
}
// Root returns the root node of the tree.
// It is intended to be used by functions that serialize the tree.
func (t *LLRB) Root() *Node {
return t.root
}
// Len returns the number of nodes in the tree.
func (t *LLRB) Len() int { return t.count }
// Has returns true if the tree contains an element whose order is the same as that of key.
func (t *LLRB) Has(key Item) bool {
return t.Get(key) != nil
}
// Get retrieves an element from the tree whose order is the same as that of key.
func (t *LLRB) Get(key Item) Item {
h := t.root
for h != nil {
switch {
case less(key, h.Item):
h = h.Left
case less(h.Item, key):
h = h.Right
default:
return h.Item
}
}
return nil
}
// Min returns the minimum element in the tree.
func (t *LLRB) Min() Item {
h := t.root
if h == nil {
return nil
}
for h.Left != nil {
h = h.Left
}
return h.Item
}
// Max returns the maximum element in the tree.
func (t *LLRB) Max() Item {
h := t.root
if h == nil {
return nil
}
for h.Right != nil {
h = h.Right
}
return h.Item
}
func (t *LLRB) ReplaceOrInsertBulk(items ...Item) {
for _, i := range items {
t.ReplaceOrInsert(i)
}
}
func (t *LLRB) InsertNoReplaceBulk(items ...Item) {
for _, i := range items {
t.InsertNoReplace(i)
}
}
// ReplaceOrInsert inserts item into the tree. If an existing
// element has the same order, it is removed from the tree and returned.
func (t *LLRB) ReplaceOrInsert(item Item) Item {
if item == nil {
panic("inserting nil item")
}
var replaced Item
t.root, replaced = t.replaceOrInsert(t.root, item)
t.root.Black = true
if replaced == nil {
t.count++
}
return replaced
}
func (t *LLRB) replaceOrInsert(h *Node, item Item) (*Node, Item) {
if h == nil {
return newNode(item), nil
}
h = walkDownRot23(h)
var replaced Item
if less(item, h.Item) { // BUG
h.Left, replaced = t.replaceOrInsert(h.Left, item)
} else if less(h.Item, item) {
h.Right, replaced = t.replaceOrInsert(h.Right, item)
} else {
replaced, h.Item = h.Item, item
}
h = walkUpRot23(h)
return h, replaced
}
// InsertNoReplace inserts item into the tree. If an existing
// element has the same order, both elements remain in the tree.
func (t *LLRB) InsertNoReplace(item Item) {
if item == nil {
panic("inserting nil item")
}
t.root = t.insertNoReplace(t.root, item)
t.root.Black = true
t.count++
}
func (t *LLRB) insertNoReplace(h *Node, item Item) *Node {
if h == nil {
return newNode(item)
}
h = walkDownRot23(h)
if less(item, h.Item) {
h.Left = t.insertNoReplace(h.Left, item)
} else {
h.Right = t.insertNoReplace(h.Right, item)
}
return walkUpRot23(h)
}
// Rotation driver routines for 2-3 algorithm
func walkDownRot23(h *Node) *Node { return h }
func walkUpRot23(h *Node) *Node {
if isRed(h.Right) && !isRed(h.Left) {
h = rotateLeft(h)
}
if isRed(h.Left) && isRed(h.Left.Left) {
h = rotateRight(h)
}
if isRed(h.Left) && isRed(h.Right) {
flip(h)
}
return h
}
// Rotation driver routines for 2-3-4 algorithm
func walkDownRot234(h *Node) *Node {
if isRed(h.Left) && isRed(h.Right) {
flip(h)
}
return h
}
func walkUpRot234(h *Node) *Node {
if isRed(h.Right) && !isRed(h.Left) {
h = rotateLeft(h)
}
if isRed(h.Left) && isRed(h.Left.Left) {
h = rotateRight(h)
}
return h
}
// DeleteMin deletes the minimum element in the tree and returns the
// deleted item or nil otherwise.
func (t *LLRB) DeleteMin() Item {
var deleted Item
t.root, deleted = deleteMin(t.root)
if t.root != nil {
t.root.Black = true
}
if deleted != nil {
t.count--
}
return deleted
}
// deleteMin code for LLRB 2-3 trees
func deleteMin(h *Node) (*Node, Item) {
if h == nil {
return nil, nil
}
if h.Left == nil {
return nil, h.Item
}
if !isRed(h.Left) && !isRed(h.Left.Left) {
h = moveRedLeft(h)
}
var deleted Item
h.Left, deleted = deleteMin(h.Left)
return fixUp(h), deleted
}
// DeleteMax deletes the maximum element in the tree and returns
// the deleted item or nil otherwise
func (t *LLRB) DeleteMax() Item {
var deleted Item
t.root, deleted = deleteMax(t.root)
if t.root != nil {
t.root.Black = true
}
if deleted != nil {
t.count--
}
return deleted
}
func deleteMax(h *Node) (*Node, Item) {
if h == nil {
return nil, nil
}
if isRed(h.Left) {
h = rotateRight(h)
}
if h.Right == nil {
return nil, h.Item
}
if !isRed(h.Right) && !isRed(h.Right.Left) {
h = moveRedRight(h)
}
var deleted Item
h.Right, deleted = deleteMax(h.Right)
return fixUp(h), deleted
}
// Delete deletes an item from the tree whose key equals key.
// The deleted item is return, otherwise nil is returned.
func (t *LLRB) Delete(key Item) Item {
var deleted Item
t.root, deleted = t.delete(t.root, key)
if t.root != nil {
t.root.Black = true
}
if deleted != nil {
t.count--
}
return deleted
}
func (t *LLRB) delete(h *Node, item Item) (*Node, Item) {
var deleted Item
if h == nil {
return nil, nil
}
if less(item, h.Item) {
if h.Left == nil { // item not present. Nothing to delete
return h, nil
}
if !isRed(h.Left) && !isRed(h.Left.Left) {
h = moveRedLeft(h)
}
h.Left, deleted = t.delete(h.Left, item)
} else {
if isRed(h.Left) {
h = rotateRight(h)
}
// If @item equals @h.Item and no right children at @h
if !less(h.Item, item) && h.Right == nil {
return nil, h.Item
}
// PETAR: Added 'h.Right != nil' below
if h.Right != nil && !isRed(h.Right) && !isRed(h.Right.Left) {
h = moveRedRight(h)
}
// If @item equals @h.Item, and (from above) 'h.Right != nil'
if !less(h.Item, item) {
var subDeleted Item
h.Right, subDeleted = deleteMin(h.Right)
if subDeleted == nil {
panic("logic")
}
deleted, h.Item = h.Item, subDeleted
} else { // Else, @item is bigger than @h.Item
h.Right, deleted = t.delete(h.Right, item)
}
}
return fixUp(h), deleted
}
// Internal node manipulation routines
func newNode(item Item) *Node { return &Node{Item: item} }
func isRed(h *Node) bool {
if h == nil {
return false
}
return !h.Black
}
func rotateLeft(h *Node) *Node {
x := h.Right
if x.Black {
panic("rotating a black link")
}
h.Right = x.Left
x.Left = h
x.Black = h.Black
h.Black = false
return x
}
func rotateRight(h *Node) *Node {
x := h.Left
if x.Black {
panic("rotating a black link")
}
h.Left = x.Right
x.Right = h
x.Black = h.Black
h.Black = false
return x
}
// REQUIRE: Left and Right children must be present
func flip(h *Node) {
h.Black = !h.Black
h.Left.Black = !h.Left.Black
h.Right.Black = !h.Right.Black
}
// REQUIRE: Left and Right children must be present
func moveRedLeft(h *Node) *Node {
flip(h)
if isRed(h.Right.Left) {
h.Right = rotateRight(h.Right)
h = rotateLeft(h)
flip(h)
}
return h
}
// REQUIRE: Left and Right children must be present
func moveRedRight(h *Node) *Node {
flip(h)
if isRed(h.Left.Left) {
h = rotateRight(h)
flip(h)
}
return h
}
func fixUp(h *Node) *Node {
if isRed(h.Right) {
h = rotateLeft(h)
}
if isRed(h.Left) && isRed(h.Left.Left) {
h = rotateRight(h)
}
if isRed(h.Left) && isRed(h.Right) {
flip(h)
}
return h
}

View File

@ -0,0 +1,239 @@
// Copyright 2010 Petar Maymounkov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package llrb
import (
"math"
"math/rand"
"testing"
)
func TestCases(t *testing.T) {
tree := New()
tree.ReplaceOrInsert(Int(1))
tree.ReplaceOrInsert(Int(1))
if tree.Len() != 1 {
t.Errorf("expecting len 1")
}
if !tree.Has(Int(1)) {
t.Errorf("expecting to find key=1")
}
tree.Delete(Int(1))
if tree.Len() != 0 {
t.Errorf("expecting len 0")
}
if tree.Has(Int(1)) {
t.Errorf("not expecting to find key=1")
}
tree.Delete(Int(1))
if tree.Len() != 0 {
t.Errorf("expecting len 0")
}
if tree.Has(Int(1)) {
t.Errorf("not expecting to find key=1")
}
}
func TestReverseInsertOrder(t *testing.T) {
tree := New()
n := 100
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(n - i))
}
i := 0
tree.AscendGreaterOrEqual(Int(0), func(item Item) bool {
i++
if item.(Int) != Int(i) {
t.Errorf("bad order: got %d, expect %d", item.(Int), i)
}
return true
})
}
func TestRange(t *testing.T) {
tree := New()
order := []String{
"ab", "aba", "abc", "a", "aa", "aaa", "b", "a-", "a!",
}
for _, i := range order {
tree.ReplaceOrInsert(i)
}
k := 0
tree.AscendRange(String("ab"), String("ac"), func(item Item) bool {
if k > 3 {
t.Fatalf("returned more items than expected")
}
i1 := order[k]
i2 := item.(String)
if i1 != i2 {
t.Errorf("expecting %s, got %s", i1, i2)
}
k++
return true
})
}
func TestRandomInsertOrder(t *testing.T) {
tree := New()
n := 1000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
j := 0
tree.AscendGreaterOrEqual(Int(0), func(item Item) bool {
if item.(Int) != Int(j) {
t.Fatalf("bad order")
}
j++
return true
})
}
func TestRandomReplace(t *testing.T) {
tree := New()
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
perm = rand.Perm(n)
for i := 0; i < n; i++ {
if replaced := tree.ReplaceOrInsert(Int(perm[i])); replaced == nil || replaced.(Int) != Int(perm[i]) {
t.Errorf("error replacing")
}
}
}
func TestRandomInsertSequentialDelete(t *testing.T) {
tree := New()
n := 1000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
for i := 0; i < n; i++ {
tree.Delete(Int(i))
}
}
func TestRandomInsertDeleteNonExistent(t *testing.T) {
tree := New()
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
if tree.Delete(Int(200)) != nil {
t.Errorf("deleted non-existent item")
}
if tree.Delete(Int(-2)) != nil {
t.Errorf("deleted non-existent item")
}
for i := 0; i < n; i++ {
if u := tree.Delete(Int(i)); u == nil || u.(Int) != Int(i) {
t.Errorf("delete failed")
}
}
if tree.Delete(Int(200)) != nil {
t.Errorf("deleted non-existent item")
}
if tree.Delete(Int(-2)) != nil {
t.Errorf("deleted non-existent item")
}
}
func TestRandomInsertPartialDeleteOrder(t *testing.T) {
tree := New()
n := 100
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
for i := 1; i < n-1; i++ {
tree.Delete(Int(i))
}
j := 0
tree.AscendGreaterOrEqual(Int(0), func(item Item) bool {
switch j {
case 0:
if item.(Int) != Int(0) {
t.Errorf("expecting 0")
}
case 1:
if item.(Int) != Int(n-1) {
t.Errorf("expecting %d", n-1)
}
}
j++
return true
})
}
func TestRandomInsertStats(t *testing.T) {
tree := New()
n := 100000
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.ReplaceOrInsert(Int(perm[i]))
}
avg, _ := tree.HeightStats()
expAvg := math.Log2(float64(n)) - 1.5
if math.Abs(avg-expAvg) >= 2.0 {
t.Errorf("too much deviation from expected average height")
}
}
func BenchmarkInsert(b *testing.B) {
tree := New()
for i := 0; i < b.N; i++ {
tree.ReplaceOrInsert(Int(b.N - i))
}
}
func BenchmarkDelete(b *testing.B) {
b.StopTimer()
tree := New()
for i := 0; i < b.N; i++ {
tree.ReplaceOrInsert(Int(b.N - i))
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tree.Delete(Int(i))
}
}
func BenchmarkDeleteMin(b *testing.B) {
b.StopTimer()
tree := New()
for i := 0; i < b.N; i++ {
tree.ReplaceOrInsert(Int(b.N - i))
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tree.DeleteMin()
}
}
func TestInsertNoReplace(t *testing.T) {
tree := New()
n := 1000
for q := 0; q < 2; q++ {
perm := rand.Perm(n)
for i := 0; i < n; i++ {
tree.InsertNoReplace(Int(perm[i]))
}
}
j := 0
tree.AscendGreaterOrEqual(Int(0), func(item Item) bool {
if item.(Int) != Int(j/2) {
t.Fatalf("bad order")
}
j++
return true
})
}

View File

@ -0,0 +1,17 @@
// Copyright 2010 Petar Maymounkov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package llrb
type Int int
func (x Int) Less(than Item) bool {
return x < than.(Int)
}
type String string
func (x String) Less(than Item) bool {
return x < than.(String)
}

View File

@ -69,10 +69,6 @@ type infoSchema struct {
// We should check version when change schema.
schemaMetaVersion int64
// memory tables
isDB map[string]table.Table // Information_Schema tables
psDB map[string]table.Table // Performance_Schema tables
}
var _ InfoSchema = (*infoSchema)(nil)
@ -222,7 +218,6 @@ func genGlobalID(store kv.Storage) (int64, error) {
}
var (
// memory tables
// Information_Schema
isDB *model.DBInfo
schemataTbl table.Table
@ -235,7 +230,6 @@ var (
defTbl table.Table
profilingTbl table.Table
nameToTable map[string]table.Table
// TODO: Performance_Schema
)
func setColumnID(meta *model.TableInfo, store kv.Storage) error {
@ -246,22 +240,21 @@ func setColumnID(meta *model.TableInfo, store kv.Storage) error {
return errors.Trace(err)
}
}
return err
return nil
}
func initMemoryTables(store kv.Storage) error {
// build data
dbID := int64(0)
alloc := autoid.NewMemoryAllocator(dbID)
nameToTable = make(map[string]table.Table)
// Schemata
isTables := make([]*model.TableInfo, 0, 8)
// initTable For each data
// Init Information_Schema
var (
err error
tbl table.Table
)
dbID, err := genGlobalID(store)
if err != nil {
return errors.Trace(err)
}
nameToTable = make(map[string]table.Table)
isTables := make([]*model.TableInfo, 0, len(tableNameToColumns))
for name, cols := range tableNameToColumns {
meta := buildTableMeta(name, cols)
isTables = append(isTables, meta)
@ -273,13 +266,13 @@ func initMemoryTables(store kv.Storage) error {
if err != nil {
return errors.Trace(err)
}
alloc := autoid.NewMemoryAllocator(dbID)
tbl, err = createMemoryTable(meta, alloc)
if err != nil {
return errors.Trace(err)
}
nameToTable[meta.Name.L] = tbl
}
// Set global variables
schemataTbl = nameToTable[strings.ToLower(tableSchemata)]
tablesTbl = nameToTable[strings.ToLower(tableTables)]
columnsTbl = nameToTable[strings.ToLower(tableColumns)]
@ -287,8 +280,7 @@ func initMemoryTables(store kv.Storage) error {
charsetTbl = nameToTable[strings.ToLower(tableCharacterSets)]
collationsTbl = nameToTable[strings.ToLower(tableCollations)]
// Some tables have static data. Init them now.
// charset
// CharacterSets/Collations contain static data. Init them now.
err = insertData(charsetTbl, dataForCharacterSets())
if err != nil {
return errors.Trace(err)
@ -299,13 +291,13 @@ func initMemoryTables(store kv.Storage) error {
}
// create db
isDB = &model.DBInfo{
ID: dbID,
Name: model.NewCIStr(Name),
Charset: mysql.DefaultCharset,
Collate: mysql.DefaultCollationName,
Tables: isTables,
}
isDB.ID, err = genGlobalID(store)
return errors.Trace(err)
return nil
}
func insertData(tbl table.Table, rows [][]interface{}) error {
@ -365,13 +357,13 @@ func (h *Handle) Set(newInfo []*model.DBInfo, schemaMetaVersion int64) error {
}
}
}
// add Information_Schema
// Build Information_Schema
info.schemaNameToID[isDB.Name.L] = isDB.ID
info.schemas[isDB.ID] = isDB
for _, t := range isDB.Tables {
tbl, ok := nameToTable[t.Name.L]
if !ok {
return errors.New("Miss table")
return errors.Errorf("table `%s` is missing.", t.Name)
}
info.tables[t.ID] = tbl
tname := tableName{isDB.Name.L, t.Name.L}
@ -381,29 +373,26 @@ func (h *Handle) Set(newInfo []*model.DBInfo, schemaMetaVersion int64) error {
info.columnNameToID[columnName{tname, c.Name.L}] = c.ID
}
}
// Should refill some tables in Information_Schema
// Should refill some tables in Information_Schema.
// schemata/tables/columns/statistics
dbNames := make([]string, 0, len(info.schemas))
dbInfos := make([]*model.DBInfo, 0, len(info.schemas))
for _, v := range info.schemas {
dbNames = append(dbNames, v.Name.L)
dbInfos = append(dbInfos, v)
}
// Refill schemata
err = refillTable(schemataTbl, dataForSchemata(dbNames))
if err != nil {
return errors.Trace(err)
}
// Refill tables
err = refillTable(tablesTbl, dataForTables(dbInfos))
if err != nil {
return errors.Trace(err)
}
// Refill columns
err = refillTable(columnsTbl, dataForColumns(dbInfos))
if err != nil {
return errors.Trace(err)
}
// Refill statistics
err = refillTable(statisticsTbl, dataForStatistics(dbInfos))
if err != nil {
return errors.Trace(err)

View File

@ -16,8 +16,11 @@ package infoschema_test
import (
"testing"
"github.com/juju/errors"
. "github.com/pingcap/check"
"github.com/pingcap/tidb/infoschema"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta"
"github.com/pingcap/tidb/model"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/store/localstore"
@ -48,8 +51,10 @@ func (*testSuite) TestT(c *C) {
idxName := model.NewCIStr("idx")
noexist := model.NewCIStr("noexist")
colID, err := genGlobalID(store)
c.Assert(err, IsNil)
colInfo := &model.ColumnInfo{
ID: 3,
ID: colID,
Name: colName,
Offset: 0,
FieldType: *types.NewFieldType(mysql.TypeLonglong),
@ -71,16 +76,20 @@ func (*testSuite) TestT(c *C) {
State: model.StatePublic,
}
tbID, err := genGlobalID(store)
c.Assert(err, IsNil)
tblInfo := &model.TableInfo{
ID: 2,
ID: tbID,
Name: tbName,
Columns: []*model.ColumnInfo{colInfo},
Indices: []*model.IndexInfo{idxInfo},
State: model.StatePublic,
}
dbID, err := genGlobalID(store)
c.Assert(err, IsNil)
dbInfo := &model.DBInfo{
ID: 1,
ID: dbID,
Name: dbName,
Tables: []*model.TableInfo{tblInfo},
State: model.StatePublic,
@ -103,11 +112,11 @@ func (*testSuite) TestT(c *C) {
c.Assert(is.SchemaExists(dbName), IsTrue)
c.Assert(is.SchemaExists(noexist), IsFalse)
schema, ok := is.SchemaByID(1)
schema, ok := is.SchemaByID(dbID)
c.Assert(ok, IsTrue)
c.Assert(schema, NotNil)
schema, ok = is.SchemaByID(2)
schema, ok = is.SchemaByID(tbID)
c.Assert(ok, IsFalse)
c.Assert(schema, IsNil)
@ -122,11 +131,11 @@ func (*testSuite) TestT(c *C) {
c.Assert(is.TableExists(dbName, tbName), IsTrue)
c.Assert(is.TableExists(dbName, noexist), IsFalse)
tb, ok := is.TableByID(2)
tb, ok := is.TableByID(tbID)
c.Assert(ok, IsTrue)
c.Assert(tb, NotNil)
tb, ok = is.TableByID(100)
tb, ok = is.TableByID(dbID)
c.Assert(ok, IsFalse)
c.Assert(tb, IsNil)
@ -140,10 +149,14 @@ func (*testSuite) TestT(c *C) {
c.Assert(is.ColumnExists(dbName, tbName, colName), IsTrue)
c.Assert(is.ColumnExists(dbName, tbName, noexist), IsFalse)
col, ok := is.ColumnByID(3)
col, ok := is.ColumnByID(colID)
c.Assert(ok, IsTrue)
c.Assert(col, NotNil)
col, ok = is.ColumnByID(dbID)
c.Assert(ok, IsFalse)
c.Assert(col, IsNil)
col, ok = is.ColumnByName(dbName, tbName, colName)
c.Assert(ok, IsTrue)
c.Assert(col, NotNil)
@ -152,7 +165,7 @@ func (*testSuite) TestT(c *C) {
c.Assert(ok, IsFalse)
c.Assert(col, IsNil)
indices, ok := is.ColumnIndicesByID(3)
indices, ok := is.ColumnIndicesByID(colID)
c.Assert(ok, IsTrue)
c.Assert(len(indices), Equals, 1)
@ -166,3 +179,13 @@ func (*testSuite) TestT(c *C) {
c.Assert(ok, IsTrue)
c.Assert(idx, NotNil)
}
func genGlobalID(store kv.Storage) (int64, error) {
var globalID int64
err := kv.RunInNewTxn(store, true, func(txn kv.Transaction) error {
var err error
globalID, err = meta.NewMeta(txn).GenGlobalID()
return errors.Trace(err)
})
return globalID, errors.Trace(err)
}

View File

@ -27,10 +27,6 @@ import (
"github.com/pingcap/tidb/util/types"
)
/*
* 1. Create tables for Information_Schema
* 2. Fill data for each table.
*/
const (
tableSchemata = "SCHEMATA"
tableTables = "TABLES"
@ -52,7 +48,6 @@ type columnInfo struct {
elems []string
}
//func buildColumnInfo(tableName, name string, tp byte, size int) *model.ColumnInfo {
func buildColumnInfo(tableName string, col columnInfo) *model.ColumnInfo {
mCharset := charset.CharsetBin
mCollation := charset.CharsetBin
@ -77,7 +72,7 @@ func buildColumnInfo(tableName string, col columnInfo) *model.ColumnInfo {
}
func buildTableMeta(tableName string, cs []columnInfo) *model.TableInfo {
cols := make([]*model.ColumnInfo, 0, 5)
cols := make([]*model.ColumnInfo, 0, len(cs))
for _, c := range cs {
cols = append(cols, buildColumnInfo(tableName, c))
}
@ -314,7 +309,7 @@ func dataForColumns(schemas []*model.DBInfo) [][]interface{} {
rows := [][]interface{}{}
for _, schema := range schemas {
for _, table := range schema.Tables {
rs := fetchColumnsInTable(schema, table)
rs := dataForColumnsInTable(schema, table)
for _, r := range rs {
rows = append(rows, r)
}
@ -323,7 +318,7 @@ func dataForColumns(schemas []*model.DBInfo) [][]interface{} {
return rows
}
func fetchColumnsInTable(schema *model.DBInfo, table *model.TableInfo) [][]interface{} {
func dataForColumnsInTable(schema *model.DBInfo, table *model.TableInfo) [][]interface{} {
rows := [][]interface{}{}
for i, col := range table.Columns {
colLen := col.Flen
@ -371,7 +366,7 @@ func dataForStatistics(schemas []*model.DBInfo) [][]interface{} {
rows := [][]interface{}{}
for _, schema := range schemas {
for _, table := range schema.Tables {
rs := fetchStatisticsInTable(schema, table)
rs := dataForStatisticsInTable(schema, table)
for _, r := range rs {
rows = append(rows, r)
}
@ -380,7 +375,7 @@ func dataForStatistics(schemas []*model.DBInfo) [][]interface{} {
return rows
}
func fetchStatisticsInTable(schema *model.DBInfo, table *model.TableInfo) [][]interface{} {
func dataForStatisticsInTable(schema *model.DBInfo, table *model.TableInfo) [][]interface{} {
rows := [][]interface{}{}
if table.PKIsHandle {
for _, col := range table.Columns {
@ -417,7 +412,6 @@ func fetchStatisticsInTable(schema *model.DBInfo, table *model.TableInfo) [][]in
nonUnique = "0"
}
for i, key := range index.Columns {
//col, _ := is.ColumnByName(schema.Name, table.Name, key.Name)
col := nameToCol[key.Name.L]
nullable := "YES"
if mysql.HasNotNullFlag(col.Flag) {

View File

@ -78,11 +78,10 @@ var (
)
type memoryAllocator struct {
mu sync.Mutex
store kv.Storage
base int64
end int64
dbID int64
mu sync.Mutex
base int64
end int64
dbID int64
}
// Alloc allocs the next autoID for table with tableID.
@ -101,7 +100,6 @@ func (alloc *memoryAllocator) Alloc(tableID int64) (int64, error) {
memIDLock.Unlock()
}
alloc.base++
log.Debugf("[kv] Alloc id %d, table ID:%d, from %p, database ID:%d", alloc.base, tableID, alloc, alloc.dbID)
return alloc.base, nil
}

View File

@ -18,39 +18,81 @@
package tables
import (
"sync"
"github.com/juju/errors"
"github.com/ngaut/log"
"github.com/petar/GoLLRB/llrb"
"github.com/pingcap/tidb/column"
"github.com/pingcap/tidb/context"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta/autoid"
"github.com/pingcap/tidb/model"
"github.com/pingcap/tidb/table"
"github.com/pingcap/tidb/util/types"
)
var store kv.Storage
var (
errRowNotFound = errors.New("Can not find the row")
)
type itemKey int64
type itemPair struct {
handle itemKey
data []interface{}
}
func (r *itemPair) Less(item llrb.Item) bool {
switch x := item.(type) {
case itemKey:
return r.handle < x
case *itemPair:
return r.handle < x.handle
}
log.Errorf("invalid type %T", item)
return true
}
func (k itemKey) Less(item llrb.Item) bool {
switch x := item.(type) {
case itemKey:
return k < x
case *itemPair:
return k < x.handle
}
log.Errorf("invalid type %T", item)
return true
}
// MemoryTable implements table.Table interface.
type MemoryTable struct {
ID int64
Name model.CIStr
Columns []*column.Col
ID int64
Name model.CIStr
Columns []*column.Col
pkHandleCol *column.Col
recordPrefix kv.Key
alloc autoid.Allocator
meta *model.TableInfo
//store kv.Storage
rows [][]interface{}
tree *llrb.LLRB
mu sync.RWMutex
}
// MemoryTableFromMeta creates a Table instance from model.TableInfo.
func MemoryTableFromMeta(alloc autoid.Allocator, tblInfo *model.TableInfo) (table.Table, error) {
columns := make([]*column.Col, 0, len(tblInfo.Columns))
var pkHandleColumn *column.Col
for _, colInfo := range tblInfo.Columns {
col := &column.Col{ColumnInfo: *colInfo}
columns = append(columns, col)
if col.IsPKHandleColumn(tblInfo) {
pkHandleColumn = col
}
}
t := newMemoryTable(tblInfo.ID, tblInfo.Name.O, columns, alloc)
t.pkHandleCol = pkHandleColumn
t.meta = tblInfo
return t, nil
}
@ -64,58 +106,23 @@ func newMemoryTable(tableID int64, tableName string, cols []*column.Col, alloc a
alloc: alloc,
Columns: cols,
recordPrefix: genTableRecordPrefix(tableID),
rows: [][]interface{}{},
tree: llrb.New(),
}
return t
}
/*
type iterator struct {
rows [][]interface{}
cursor int
tid int64
}
func (it *iterator) Valid() bool {
if it.cursor < 0 || it.cursor >= len(it.rows) {
return false
}
return true
}
func (it *iterator) Key() kv.Key {
return EncodeRecordKey(it.tid, int64(it.cursor), 0)
}
func (it *iterator) Value() []byte {
return nil
}
func (it *iterator) Next() error {
it.cursor++
return nil
}
func (it *iterator) Close() {
it.cursor = -1
return
}
*/
// Seek seeks the handle
func (t *MemoryTable) Seek(ctx context.Context, handle int64) (int64, bool, error) {
if handle < 0 {
handle = 0
}
if handle >= int64(len(t.rows)) {
return 0, false, nil
}
return handle, true, nil
}
// TableID implements table.Table TableID interface.
func (t *MemoryTable) TableID() int64 {
return t.ID
var found bool
var result int64
t.mu.RLock()
t.tree.AscendGreaterOrEqual(itemKey(handle), func(item llrb.Item) bool {
found = true
result = int64(item.(*itemPair).handle)
return false
})
t.mu.RUnlock()
return result, found, nil
}
// Indices implements table.Table Indices interface.
@ -123,16 +130,6 @@ func (t *MemoryTable) Indices() []*column.IndexedCol {
return nil
}
// AddIndex implements table.Table AddIndex interface.
func (t *MemoryTable) AddIndex(idxCol *column.IndexedCol) {
return
}
// TableName implements table.Table TableName interface.
func (t *MemoryTable) TableName() model.CIStr {
return t.Name
}
// Meta implements table.Table Meta interface.
func (t *MemoryTable) Meta() *model.TableInfo {
return t.meta
@ -167,54 +164,60 @@ func (t *MemoryTable) FirstKey() kv.Key {
return t.RecordKey(0, nil)
}
// FindIndexByColName implements table.Table FindIndexByColName interface.
func (t *MemoryTable) FindIndexByColName(name string) *column.IndexedCol {
return nil
}
// Truncate implements table.Table Truncate interface.
func (t *MemoryTable) Truncate(ctx context.Context) error {
t.rows = [][]interface{}{}
t.tree = llrb.New()
return nil
}
// UpdateRecord implements table.Table UpdateRecord interface.
func (t *MemoryTable) UpdateRecord(ctx context.Context, h int64, oldData []interface{}, newData []interface{}, touched map[int]bool) error {
// Unsupport
return nil
}
// SetColValue implements table.Table SetColValue interface.
func (t *MemoryTable) SetColValue(rm kv.RetrieverMutator, key []byte, data interface{}) error {
t.mu.Lock()
defer t.mu.Unlock()
item := t.tree.Get(itemKey(h))
if item == nil {
return errRowNotFound
}
pair := item.(*itemPair)
pair.data = newData
return nil
}
// AddRecord implements table.Table AddRecord interface.
func (t *MemoryTable) AddRecord(ctx context.Context, r []interface{}) (recordID int64, err error) {
recordID = int64(len(t.rows))
t.rows = append(t.rows, r)
if t.pkHandleCol != nil {
recordID, err = types.ToInt64(r[t.pkHandleCol.Offset])
if err != nil {
return 0, errors.Trace(err)
}
} else {
recordID, err = t.alloc.Alloc(t.ID)
if err != nil {
return 0, errors.Trace(err)
}
}
item := &itemPair{
handle: itemKey(recordID),
data: r,
}
t.mu.Lock()
defer t.mu.Unlock()
if t.tree.Get(itemKey(recordID)) != nil {
return 0, kv.ErrKeyExists
}
t.tree.ReplaceOrInsert(item)
return
}
// EncodeValue implements table.Table EncodeValue interface.
func (t *MemoryTable) EncodeValue(raw interface{}) ([]byte, error) {
return nil, nil
}
// DecodeValue implements table.Table DecodeValue interface.
func (t *MemoryTable) DecodeValue(data []byte, col *column.Col) (interface{}, error) {
return nil, nil
}
// RowWithCols implements table.Table RowWithCols interface.
func (t *MemoryTable) RowWithCols(ctx context.Context, h int64, cols []*column.Col) ([]interface{}, error) {
if h >= int64(len(t.rows)) || h < 0 {
return nil, errors.New("Can not find the row")
}
row := t.rows[h]
if row == nil {
return nil, errors.New("Can not find row")
t.mu.RLock()
defer t.mu.RUnlock()
item := t.tree.Get(itemKey(h))
if item == nil {
return nil, errRowNotFound
}
row := item.(*itemPair).data
v := make([]interface{}, len(cols))
for i, col := range cols {
v[i] = row[col.Offset]
@ -238,20 +241,9 @@ func (t *MemoryTable) LockRow(ctx context.Context, h int64, forRead bool) error
// RemoveRecord implements table.Table RemoveRecord interface.
func (t *MemoryTable) RemoveRecord(ctx context.Context, h int64, r []interface{}) error {
if h >= int64(len(t.rows)) || h < 0 {
return errors.New("Can not find the row")
}
t.rows[h] = nil
return nil
}
// RemoveRowIndex implements table.Table RemoveRowIndex interface.
func (t *MemoryTable) RemoveRowIndex(rm kv.RetrieverMutator, h int64, vals []interface{}, idx *column.IndexedCol) error {
return nil
}
// BuildIndexForRow implements table.Table BuildIndexForRow interface.
func (t *MemoryTable) BuildIndexForRow(rm kv.RetrieverMutator, h int64, vals []interface{}, idx *column.IndexedCol) error {
t.mu.Lock()
t.tree.Delete(itemKey(h))
t.mu.Unlock()
return nil
}

View File

@ -1,4 +1,4 @@
// Copyright 2015 PingCAP, Inc.
// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -74,9 +74,9 @@ func (ts *testMemoryTableSuite) SetUpSuite(c *C) {
func (ts *testMemoryTableSuite) TestMemoryBasic(c *C) {
ctx := ts.se.(context.Context)
tb := ts.tbl
c.Assert(tb.Meta(), NotNil)
c.Assert(tb.Meta().ID, Greater, int64(0))
c.Assert(tb.Meta().Name.L, Equals, "t")
c.Assert(tb.Meta(), NotNil)
c.Assert(tb.Indices(), IsNil)
c.Assert(string(tb.FirstKey()), Not(Equals), "")
c.Assert(string(tb.RecordPrefix()), Not(Equals), "")
@ -87,7 +87,6 @@ func (ts *testMemoryTableSuite) TestMemoryBasic(c *C) {
rid, err := tb.AddRecord(ctx, []interface{}{1, "abc"})
c.Assert(err, IsNil)
c.Assert(rid, Equals, int64(0))
row, err := tb.Row(ctx, rid)
c.Assert(err, IsNil)
c.Assert(len(row), Equals, 2)

View File

@ -107,11 +107,6 @@ func newTable(tableID int64, cols []*column.Col, alloc autoid.Allocator) *Table
return t
}
// TableID implements table.Table TableID interface.
func (t *Table) TableID() int64 {
return t.ID
}
// Indices implements table.Table Indices interface.
func (t *Table) Indices() []*column.IndexedCol {
return t.indices