vitess-gh/go/vt/tabletserver/table_info.go

167 строки
4.5 KiB
Go

// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tabletserver
import (
"fmt"
"strconv"
"strings"
log "github.com/golang/glog"
"github.com/youtube/vitess/go/sqltypes"
"github.com/youtube/vitess/go/sync2"
"github.com/youtube/vitess/go/vt/schema"
)
type TableInfo struct {
*schema.Table
Cache *RowCache
// stats updated by sqlquery.go
hits, absent, misses, invalidations sync2.AtomicInt64
}
func NewTableInfo(conn PoolConnection, tableName string, tableType string, createTime sqltypes.Value, comment string, cachePool *CachePool) (ti *TableInfo) {
if tableName == "dual" {
return &TableInfo{Table: schema.NewTable(tableName)}
}
ti = loadTableInfo(conn, tableName)
ti.initRowCache(conn, tableType, createTime, comment, cachePool)
return ti
}
func loadTableInfo(conn PoolConnection, tableName string) (ti *TableInfo) {
ti = &TableInfo{Table: schema.NewTable(tableName)}
if !ti.fetchColumns(conn) {
return nil
}
if !ti.fetchIndexes(conn) {
return nil
}
return ti
}
func (ti *TableInfo) fetchColumns(conn PoolConnection) bool {
columns, err := conn.ExecuteFetch(fmt.Sprintf("describe %s", ti.Name), 10000, false)
if err != nil {
log.Warningf("%s", err.Error())
return false
}
for _, row := range columns.Rows {
ti.AddColumn(row[0].String(), row[1].String(), row[4], row[5].String())
}
return true
}
func (ti *TableInfo) SetPK(colnames []string) error {
pkIndex := schema.NewIndex("PRIMARY")
colnums := make([]int, len(colnames))
for i, colname := range colnames {
colnums[i] = ti.FindColumn(colname)
if colnums[i] == -1 {
return fmt.Errorf("column %s not found", colname)
}
pkIndex.AddColumn(colname, 1)
}
for _, col := range ti.Columns {
pkIndex.DataColumns = append(pkIndex.DataColumns, col.Name)
}
if len(ti.Indexes) == 0 {
ti.Indexes = make([]*schema.Index, 1)
} else if ti.Indexes[0].Name != "PRIMARY" {
ti.Indexes = append(ti.Indexes, nil)
copy(ti.Indexes[1:], ti.Indexes[:len(ti.Indexes)-1])
} // else we replace the currunt primary key
ti.Indexes[0] = pkIndex
ti.PKColumns = colnums
return nil
}
func (ti *TableInfo) fetchIndexes(conn PoolConnection) bool {
indexes, err := conn.ExecuteFetch(fmt.Sprintf("show index from %s", ti.Name), 10000, false)
if err != nil {
log.Warningf("%s", err.Error())
return false
}
var currentIndex *schema.Index
currentName := ""
for _, row := range indexes.Rows {
indexName := row[2].String()
if currentName != indexName {
currentIndex = ti.AddIndex(indexName)
currentName = indexName
}
var cardinality uint64
if !row[6].IsNull() {
cardinality, err = strconv.ParseUint(row[6].String(), 0, 64)
if err != nil {
log.Warningf("%s", err)
}
}
currentIndex.AddColumn(row[4].String(), cardinality)
}
if len(ti.Indexes) == 0 {
return true
}
pkIndex := ti.Indexes[0]
if pkIndex.Name != "PRIMARY" {
return true
}
ti.PKColumns = make([]int, len(pkIndex.Columns))
for i, pkCol := range pkIndex.Columns {
ti.PKColumns[i] = ti.FindColumn(pkCol)
}
// Primary key contains all table columns
for _, col := range ti.Columns {
pkIndex.DataColumns = append(pkIndex.DataColumns, col.Name)
}
// Secondary indices contain all primary key columns
for i := 1; i < len(ti.Indexes); i++ {
ti.Indexes[i].DataColumns = pkIndex.Columns
}
return true
}
func (ti *TableInfo) initRowCache(conn PoolConnection, tableType string, createTime sqltypes.Value, comment string, cachePool *CachePool) {
if cachePool.IsClosed() {
return
}
if strings.Contains(comment, "vtocc_nocache") {
log.Infof("%s commented as vtocc_nocache. Will not be cached.", ti.Name)
return
}
if tableType == "VIEW" {
log.Infof("%s is a view. Will not be cached.", ti.Name)
return
}
if ti.PKColumns == nil {
log.Infof("Table %s has no primary key. Will not be cached.", ti.Name)
return
}
for _, col := range ti.PKColumns {
if ti.Columns[col].Category == schema.CAT_OTHER {
log.Infof("Table %s pk has unsupported column types. Will not be cached.", ti.Name)
return
}
}
ti.CacheType = schema.CACHE_RW
ti.Cache = NewRowCache(ti, cachePool)
}
func (ti *TableInfo) StatsJSON() string {
if ti.Cache == nil {
return fmt.Sprintf("null")
}
h, a, m, i := ti.Stats()
return fmt.Sprintf("{\"Hits\": %v, \"Absent\": %v, \"Misses\": %v, \"Invalidations\": %v}", h, a, m, i)
}
func (ti *TableInfo) Stats() (hits, absent, misses, invalidations int64) {
return ti.hits.Get(), ti.absent.Get(), ti.misses.Get(), ti.invalidations.Get()
}