table/tables: use red black tree to implement memory table.
This commit is contained in:
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@ -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"
|
||||
|
||||
39
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/avgvar.go
generated
vendored
Normal file
39
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/avgvar.go
generated
vendored
Normal 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()) }
|
||||
93
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/iterator.go
generated
vendored
Normal file
93
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/iterator.go
generated
vendored
Normal 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)
|
||||
}
|
||||
76
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/iterator_test.go
generated
vendored
Normal file
76
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/iterator_test.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
46
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb-stats.go
generated
vendored
Normal file
46
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb-stats.go
generated
vendored
Normal 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)
|
||||
}
|
||||
}
|
||||
456
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb.go
generated
vendored
Normal file
456
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb.go
generated
vendored
Normal 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
|
||||
}
|
||||
239
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb_test.go
generated
vendored
Normal file
239
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/llrb_test.go
generated
vendored
Normal 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
|
||||
})
|
||||
}
|
||||
17
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/util.go
generated
vendored
Normal file
17
Godeps/_workspace/src/github.com/petar/GoLLRB/llrb/util.go
generated
vendored
Normal 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)
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user