github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/infoschema/infoschema.go (about)

     1  // Copyright 2015 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package infoschema
    15  
    16  import (
    17  	"strings"
    18  	"sync/atomic"
    19  
    20  	"github.com/insionng/yougam/libraries/juju/errors"
    21  	"github.com/insionng/yougam/libraries/pingcap/tidb/kv"
    22  	"github.com/insionng/yougam/libraries/pingcap/tidb/meta/autoid"
    23  	"github.com/insionng/yougam/libraries/pingcap/tidb/model"
    24  	"github.com/insionng/yougam/libraries/pingcap/tidb/mysql"
    25  	"github.com/insionng/yougam/libraries/pingcap/tidb/perfschema"
    26  	"github.com/insionng/yougam/libraries/pingcap/tidb/table"
    27  	"github.com/insionng/yougam/libraries/pingcap/tidb/terror"
    28  	// import table implementation to init table.TableFromMeta
    29  	_ "github.com/insionng/yougam/libraries/pingcap/tidb/table/tables"
    30  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    31  )
    32  
    33  var (
    34  	// ErrDatabaseDropExists returns for dropping a non-existent database.
    35  	ErrDatabaseDropExists = terror.ClassSchema.New(codeDBDropExists, "database doesn't exist")
    36  	// ErrDatabaseNotExists returns for database not exists.
    37  	ErrDatabaseNotExists = terror.ClassSchema.New(codeDatabaseNotExists, "database not exists")
    38  	// ErrTableNotExists returns for table not exists.
    39  	ErrTableNotExists = terror.ClassSchema.New(codeTableNotExists, "table not exists")
    40  	// ErrColumnNotExists returns for column not exists.
    41  	ErrColumnNotExists = terror.ClassSchema.New(codeColumnNotExists, "field not exists")
    42  	// ErrForeignKeyNotMatch returns for foreign key not match.
    43  	ErrForeignKeyNotMatch = terror.ClassSchema.New(codeCannotAddForeign, "foreign key not match")
    44  	// ErrForeignKeyExists returns for foreign key exists.
    45  	ErrForeignKeyExists = terror.ClassSchema.New(codeCannotAddForeign, "foreign key already exists")
    46  	// ErrForeignKeyNotExists returns for foreign key not exists.
    47  	ErrForeignKeyNotExists = terror.ClassSchema.New(codeForeignKeyNotExists, "foreign key not exists")
    48  	// ErrDatabaseExists returns for database already exists.
    49  	ErrDatabaseExists = terror.ClassSchema.New(codeDatabaseExists, "database already exists")
    50  	// ErrTableExists returns for table already exists.
    51  	ErrTableExists = terror.ClassSchema.New(codeTableExists, "table already exists")
    52  	// ErrTableDropExists returns for dropping a non-existent table.
    53  	ErrTableDropExists = terror.ClassSchema.New(codeBadTable, "unknown table")
    54  	// ErrColumnExists returns for column already exists.
    55  	ErrColumnExists = terror.ClassSchema.New(codeColumnExists, "Duplicate column")
    56  	// ErrIndexExists returns for index already exists.
    57  	ErrIndexExists = terror.ClassSchema.New(codeIndexExists, "Duplicate Index")
    58  )
    59  
    60  // InfoSchema is the interface used to retrieve the schema information.
    61  // It works as a in memory cache and doesn't handle any schema change.
    62  // InfoSchema is read-only, and the returned value is a copy.
    63  // TODO: add more methods to retrieve tables and columns.
    64  type InfoSchema interface {
    65  	SchemaByName(schema model.CIStr) (*model.DBInfo, bool)
    66  	SchemaExists(schema model.CIStr) bool
    67  	TableByName(schema, table model.CIStr) (table.Table, error)
    68  	TableExists(schema, table model.CIStr) bool
    69  	ColumnByName(schema, table, column model.CIStr) (*model.ColumnInfo, bool)
    70  	ColumnExists(schema, table, column model.CIStr) bool
    71  	IndexByName(schema, table, index model.CIStr) (*model.IndexInfo, bool)
    72  	SchemaByID(id int64) (*model.DBInfo, bool)
    73  	TableByID(id int64) (table.Table, bool)
    74  	AllocByID(id int64) (autoid.Allocator, bool)
    75  	ColumnByID(id int64) (*model.ColumnInfo, bool)
    76  	ColumnIndicesByID(id int64) ([]*model.IndexInfo, bool)
    77  	AllSchemaNames() []string
    78  	AllSchemas() []*model.DBInfo
    79  	Clone() (result []*model.DBInfo)
    80  	SchemaTables(schema model.CIStr) []table.Table
    81  	SchemaMetaVersion() int64
    82  }
    83  
    84  // Information Schema Name.
    85  const (
    86  	Name = "INFORMATION_SCHEMA"
    87  )
    88  
    89  type infoSchema struct {
    90  	schemaNameToID  map[string]int64
    91  	tableNameToID   map[tableName]int64
    92  	columnNameToID  map[columnName]int64
    93  	schemas         map[int64]*model.DBInfo
    94  	tables          map[int64]table.Table
    95  	tableAllocators map[int64]autoid.Allocator
    96  	columns         map[int64]*model.ColumnInfo
    97  	indices         map[indexName]*model.IndexInfo
    98  	columnIndices   map[int64][]*model.IndexInfo
    99  
   100  	// We should check version when change schema.
   101  	schemaMetaVersion int64
   102  }
   103  
   104  // MockInfoSchema only serves for test.
   105  func MockInfoSchema(tbList []*model.TableInfo) InfoSchema {
   106  	result := &infoSchema{}
   107  	result.schemaNameToID = make(map[string]int64)
   108  	result.tableNameToID = make(map[tableName]int64)
   109  	result.schemas = make(map[int64]*model.DBInfo)
   110  	result.tables = make(map[int64]table.Table)
   111  
   112  	result.schemaNameToID["test"] = 0
   113  	result.schemas[0] = &model.DBInfo{ID: 0, Name: model.NewCIStr("test"), Tables: tbList}
   114  	for i, tb := range tbList {
   115  		result.tableNameToID[tableName{schema: "test", table: tb.Name.L}] = int64(i)
   116  		result.tables[int64(i)] = table.MockTableFromMeta(tb)
   117  	}
   118  	return result
   119  }
   120  
   121  var _ InfoSchema = (*infoSchema)(nil)
   122  
   123  type tableName struct {
   124  	schema string
   125  	table  string
   126  }
   127  
   128  type columnName struct {
   129  	tableName
   130  	name string
   131  }
   132  
   133  type indexName struct {
   134  	tableName
   135  	name string
   136  }
   137  
   138  func (is *infoSchema) SchemaByName(schema model.CIStr) (val *model.DBInfo, ok bool) {
   139  	id, ok := is.schemaNameToID[schema.L]
   140  	if !ok {
   141  		return
   142  	}
   143  	val, ok = is.schemas[id]
   144  	return
   145  }
   146  
   147  func (is *infoSchema) SchemaMetaVersion() int64 {
   148  	return is.schemaMetaVersion
   149  }
   150  
   151  func (is *infoSchema) SchemaExists(schema model.CIStr) bool {
   152  	_, ok := is.schemaNameToID[schema.L]
   153  	return ok
   154  }
   155  
   156  func (is *infoSchema) TableByName(schema, table model.CIStr) (t table.Table, err error) {
   157  	id, ok := is.tableNameToID[tableName{schema: schema.L, table: table.L}]
   158  	if !ok {
   159  		return nil, ErrTableNotExists.Gen("table %s.%s does not exist", schema, table)
   160  	}
   161  	t = is.tables[id]
   162  	return
   163  }
   164  
   165  func (is *infoSchema) TableExists(schema, table model.CIStr) bool {
   166  	_, ok := is.tableNameToID[tableName{schema: schema.L, table: table.L}]
   167  	return ok
   168  }
   169  
   170  func (is *infoSchema) ColumnByName(schema, table, column model.CIStr) (val *model.ColumnInfo, ok bool) {
   171  	id, ok := is.columnNameToID[columnName{tableName: tableName{schema: schema.L, table: table.L}, name: column.L}]
   172  	if !ok {
   173  		return
   174  	}
   175  	val, ok = is.columns[id]
   176  	return
   177  }
   178  
   179  func (is *infoSchema) ColumnExists(schema, table, column model.CIStr) bool {
   180  	_, ok := is.columnNameToID[columnName{tableName: tableName{schema: schema.L, table: table.L}, name: column.L}]
   181  	return ok
   182  }
   183  
   184  func (is *infoSchema) IndexByName(schema, table, index model.CIStr) (val *model.IndexInfo, ok bool) {
   185  	val, ok = is.indices[indexName{tableName: tableName{schema: schema.L, table: table.L}, name: index.L}]
   186  	return
   187  }
   188  
   189  func (is *infoSchema) SchemaByID(id int64) (val *model.DBInfo, ok bool) {
   190  	val, ok = is.schemas[id]
   191  	return
   192  }
   193  
   194  func (is *infoSchema) TableByID(id int64) (val table.Table, ok bool) {
   195  	val, ok = is.tables[id]
   196  	return
   197  }
   198  
   199  func (is *infoSchema) AllocByID(id int64) (val autoid.Allocator, ok bool) {
   200  	val, ok = is.tableAllocators[id]
   201  	return
   202  }
   203  
   204  func (is *infoSchema) ColumnByID(id int64) (val *model.ColumnInfo, ok bool) {
   205  	val, ok = is.columns[id]
   206  	return
   207  }
   208  
   209  func (is *infoSchema) ColumnIndicesByID(id int64) (indices []*model.IndexInfo, ok bool) {
   210  	indices, ok = is.columnIndices[id]
   211  	return
   212  }
   213  
   214  func (is *infoSchema) AllSchemaNames() (names []string) {
   215  	for _, v := range is.schemas {
   216  		names = append(names, v.Name.O)
   217  	}
   218  	return
   219  }
   220  
   221  func (is *infoSchema) AllSchemas() (schemas []*model.DBInfo) {
   222  	for _, v := range is.schemas {
   223  		schemas = append(schemas, v)
   224  	}
   225  	return
   226  }
   227  
   228  func (is *infoSchema) SchemaTables(schema model.CIStr) (tables []table.Table) {
   229  	di, ok := is.SchemaByName(schema)
   230  	if !ok {
   231  		return
   232  	}
   233  	for _, ti := range di.Tables {
   234  		tables = append(tables, is.tables[ti.ID])
   235  	}
   236  	return
   237  }
   238  
   239  func (is *infoSchema) Clone() (result []*model.DBInfo) {
   240  	for _, v := range is.schemas {
   241  		result = append(result, v.Clone())
   242  	}
   243  	return
   244  }
   245  
   246  // Handle handles information schema, including getting and setting.
   247  type Handle struct {
   248  	value     atomic.Value
   249  	store     kv.Storage
   250  	memSchema *memSchemaHandle
   251  }
   252  
   253  // NewHandle creates a new Handle.
   254  func NewHandle(store kv.Storage) (*Handle, error) {
   255  	h := &Handle{
   256  		store: store,
   257  	}
   258  	// init memory tables
   259  	var err error
   260  	h.memSchema, err = newMemSchemaHandle()
   261  	if err != nil {
   262  		return nil, errors.Trace(err)
   263  	}
   264  	return h, nil
   265  }
   266  
   267  // Init memory schemas including infoschema and perfshcema.
   268  func newMemSchemaHandle() (*memSchemaHandle, error) {
   269  	h := &memSchemaHandle{
   270  		nameToTable: make(map[string]table.Table),
   271  	}
   272  	err := initMemoryTables(h)
   273  	if err != nil {
   274  		return nil, errors.Trace(err)
   275  	}
   276  	initMemoryTables(h)
   277  	h.perfHandle, err = perfschema.NewPerfHandle()
   278  	if err != nil {
   279  		return nil, errors.Trace(err)
   280  	}
   281  	return h, nil
   282  }
   283  
   284  // memSchemaHandle is used to store memory schema information.
   285  type memSchemaHandle struct {
   286  	// Information Schema
   287  	isDB          *model.DBInfo
   288  	schemataTbl   table.Table
   289  	tablesTbl     table.Table
   290  	columnsTbl    table.Table
   291  	statisticsTbl table.Table
   292  	charsetTbl    table.Table
   293  	collationsTbl table.Table
   294  	filesTbl      table.Table
   295  	defTbl        table.Table
   296  	profilingTbl  table.Table
   297  	partitionsTbl table.Table
   298  	nameToTable   map[string]table.Table
   299  	// Performance Schema
   300  	perfHandle perfschema.PerfSchema
   301  }
   302  
   303  func initMemoryTables(h *memSchemaHandle) error {
   304  	// Init Information_Schema
   305  	var (
   306  		err error
   307  		tbl table.Table
   308  	)
   309  	dbID := autoid.GenLocalSchemaID()
   310  	isTables := make([]*model.TableInfo, 0, len(tableNameToColumns))
   311  	for name, cols := range tableNameToColumns {
   312  		meta := buildTableMeta(name, cols)
   313  		isTables = append(isTables, meta)
   314  		meta.ID = autoid.GenLocalSchemaID()
   315  		for _, c := range meta.Columns {
   316  			c.ID = autoid.GenLocalSchemaID()
   317  		}
   318  		alloc := autoid.NewMemoryAllocator(dbID)
   319  		tbl, err = createMemoryTable(meta, alloc)
   320  		if err != nil {
   321  			return errors.Trace(err)
   322  		}
   323  		h.nameToTable[meta.Name.L] = tbl
   324  	}
   325  	h.schemataTbl = h.nameToTable[strings.ToLower(tableSchemata)]
   326  	h.tablesTbl = h.nameToTable[strings.ToLower(tableTables)]
   327  	h.columnsTbl = h.nameToTable[strings.ToLower(tableColumns)]
   328  	h.statisticsTbl = h.nameToTable[strings.ToLower(tableStatistics)]
   329  	h.charsetTbl = h.nameToTable[strings.ToLower(tableCharacterSets)]
   330  	h.collationsTbl = h.nameToTable[strings.ToLower(tableCollations)]
   331  
   332  	// CharacterSets/Collations contain static data. Init them now.
   333  	err = insertData(h.charsetTbl, dataForCharacterSets())
   334  	if err != nil {
   335  		return errors.Trace(err)
   336  	}
   337  	err = insertData(h.collationsTbl, dataForColltions())
   338  	if err != nil {
   339  		return errors.Trace(err)
   340  	}
   341  	// create db
   342  	h.isDB = &model.DBInfo{
   343  		ID:      dbID,
   344  		Name:    model.NewCIStr(Name),
   345  		Charset: mysql.DefaultCharset,
   346  		Collate: mysql.DefaultCollationName,
   347  		Tables:  isTables,
   348  	}
   349  	return nil
   350  }
   351  
   352  func insertData(tbl table.Table, rows [][]types.Datum) error {
   353  	for _, r := range rows {
   354  		_, err := tbl.AddRecord(nil, r)
   355  		if err != nil {
   356  			return errors.Trace(err)
   357  		}
   358  	}
   359  	return nil
   360  }
   361  
   362  func refillTable(tbl table.Table, rows [][]types.Datum) error {
   363  	err := tbl.Truncate(nil)
   364  	if err != nil {
   365  		return errors.Trace(err)
   366  	}
   367  	return insertData(tbl, rows)
   368  }
   369  
   370  // Set sets DBInfo to information schema.
   371  func (h *Handle) Set(newInfo []*model.DBInfo, schemaMetaVersion int64) error {
   372  	info := &infoSchema{
   373  		schemaNameToID:    map[string]int64{},
   374  		tableNameToID:     map[tableName]int64{},
   375  		columnNameToID:    map[columnName]int64{},
   376  		schemas:           map[int64]*model.DBInfo{},
   377  		tables:            map[int64]table.Table{},
   378  		tableAllocators:   map[int64]autoid.Allocator{},
   379  		columns:           map[int64]*model.ColumnInfo{},
   380  		indices:           map[indexName]*model.IndexInfo{},
   381  		columnIndices:     map[int64][]*model.IndexInfo{},
   382  		schemaMetaVersion: schemaMetaVersion,
   383  	}
   384  	var err error
   385  	var hasOldInfo bool
   386  	infoschema := h.Get()
   387  	if infoschema != nil {
   388  		hasOldInfo = true
   389  	}
   390  	for _, di := range newInfo {
   391  		info.schemas[di.ID] = di
   392  		info.schemaNameToID[di.Name.L] = di.ID
   393  		for _, t := range di.Tables {
   394  			alloc := autoid.NewAllocator(h.store, di.ID)
   395  			if hasOldInfo {
   396  				val, ok := infoschema.AllocByID(t.ID)
   397  				if ok {
   398  					alloc = val
   399  				}
   400  			}
   401  			info.tableAllocators[t.ID] = alloc
   402  			info.tables[t.ID], err = table.TableFromMeta(alloc, t)
   403  			if err != nil {
   404  				return errors.Trace(err)
   405  			}
   406  			tname := tableName{di.Name.L, t.Name.L}
   407  			info.tableNameToID[tname] = t.ID
   408  			for _, c := range t.Columns {
   409  				info.columns[c.ID] = c
   410  				info.columnNameToID[columnName{tname, c.Name.L}] = c.ID
   411  			}
   412  			for _, idx := range t.Indices {
   413  				info.indices[indexName{tname, idx.Name.L}] = idx
   414  				for _, idxCol := range idx.Columns {
   415  					columnID := t.Columns[idxCol.Offset].ID
   416  					columnIndices := info.columnIndices[columnID]
   417  					info.columnIndices[columnID] = append(columnIndices, idx)
   418  				}
   419  			}
   420  		}
   421  	}
   422  	// Build Information_Schema
   423  	info.schemaNameToID[h.memSchema.isDB.Name.L] = h.memSchema.isDB.ID
   424  	info.schemas[h.memSchema.isDB.ID] = h.memSchema.isDB
   425  	for _, t := range h.memSchema.isDB.Tables {
   426  		tbl, ok := h.memSchema.nameToTable[t.Name.L]
   427  		if !ok {
   428  			return ErrTableNotExists.Gen("table `%s` is missing.", t.Name)
   429  		}
   430  		info.tables[t.ID] = tbl
   431  		tname := tableName{h.memSchema.isDB.Name.L, t.Name.L}
   432  		info.tableNameToID[tname] = t.ID
   433  		for _, c := range t.Columns {
   434  			info.columns[c.ID] = c
   435  			info.columnNameToID[columnName{tname, c.Name.L}] = c.ID
   436  		}
   437  	}
   438  
   439  	// Add Performance_Schema
   440  	psDB := h.memSchema.perfHandle.GetDBMeta()
   441  	info.schemaNameToID[psDB.Name.L] = psDB.ID
   442  	info.schemas[psDB.ID] = psDB
   443  	for _, t := range psDB.Tables {
   444  		tbl, ok := h.memSchema.perfHandle.GetTable(t.Name.O)
   445  		if !ok {
   446  			return ErrTableNotExists.Gen("table `%s` is missing.", t.Name)
   447  		}
   448  		info.tables[t.ID] = tbl
   449  		tname := tableName{psDB.Name.L, t.Name.L}
   450  		info.tableNameToID[tname] = t.ID
   451  		for _, c := range t.Columns {
   452  			info.columns[c.ID] = c
   453  			info.columnNameToID[columnName{tname, c.Name.L}] = c.ID
   454  		}
   455  	}
   456  	// Should refill some tables in Information_Schema.
   457  	// schemata/tables/columns/statistics
   458  	dbNames := make([]string, 0, len(info.schemas))
   459  	dbInfos := make([]*model.DBInfo, 0, len(info.schemas))
   460  	for _, v := range info.schemas {
   461  		dbNames = append(dbNames, v.Name.L)
   462  		dbInfos = append(dbInfos, v)
   463  	}
   464  	err = refillTable(h.memSchema.schemataTbl, dataForSchemata(dbNames))
   465  	if err != nil {
   466  		return errors.Trace(err)
   467  	}
   468  	err = refillTable(h.memSchema.tablesTbl, dataForTables(dbInfos))
   469  	if err != nil {
   470  		return errors.Trace(err)
   471  	}
   472  	err = refillTable(h.memSchema.columnsTbl, dataForColumns(dbInfos))
   473  	if err != nil {
   474  		return errors.Trace(err)
   475  	}
   476  	err = refillTable(h.memSchema.statisticsTbl, dataForStatistics(dbInfos))
   477  	if err != nil {
   478  		return errors.Trace(err)
   479  	}
   480  	h.value.Store(info)
   481  	return nil
   482  }
   483  
   484  // Get gets information schema from Handle.
   485  func (h *Handle) Get() InfoSchema {
   486  	v := h.value.Load()
   487  	schema, _ := v.(InfoSchema)
   488  	return schema
   489  }
   490  
   491  // GetPerfHandle gets performance schema from handle.
   492  func (h *Handle) GetPerfHandle() perfschema.PerfSchema {
   493  	return h.memSchema.perfHandle
   494  }
   495  
   496  // Schema error codes.
   497  const (
   498  	codeDBDropExists      terror.ErrCode = 1008
   499  	codeDatabaseNotExists                = 1049
   500  	codeTableNotExists                   = 1146
   501  	codeColumnNotExists                  = 1054
   502  
   503  	codeCannotAddForeign    = 1215
   504  	codeForeignKeyNotExists = 1091
   505  
   506  	codeDatabaseExists = 1007
   507  	codeTableExists    = 1050
   508  	codeBadTable       = 1051
   509  	codeColumnExists   = 1060
   510  	codeIndexExists    = 1831
   511  )
   512  
   513  func init() {
   514  	schemaMySQLErrCodes := map[terror.ErrCode]uint16{
   515  		codeDBDropExists:        mysql.ErrDBDropExists,
   516  		codeDatabaseNotExists:   mysql.ErrBadDB,
   517  		codeTableNotExists:      mysql.ErrNoSuchTable,
   518  		codeColumnNotExists:     mysql.ErrBadField,
   519  		codeCannotAddForeign:    mysql.ErrCannotAddForeign,
   520  		codeForeignKeyNotExists: mysql.ErrCantDropFieldOrKey,
   521  		codeDatabaseExists:      mysql.ErrDBCreateExists,
   522  		codeTableExists:         mysql.ErrTableExists,
   523  		codeBadTable:            mysql.ErrBadTable,
   524  		codeColumnExists:        mysql.ErrDupFieldName,
   525  		codeIndexExists:         mysql.ErrDupIndex,
   526  	}
   527  	terror.ErrClassToMySQLCodes[terror.ClassSchema] = schemaMySQLErrCodes
   528  }