You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
339 lines
8.7 KiB
339 lines
8.7 KiB
// Copyright 2015 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package infoschema_test
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
|
|
. "github.com/pingcap/check"
|
|
"github.com/pingcap/errors"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/parser/mysql"
|
|
"github.com/pingcap/tidb/infoschema"
|
|
"github.com/pingcap/tidb/kv"
|
|
"github.com/pingcap/tidb/meta"
|
|
"github.com/pingcap/tidb/session"
|
|
"github.com/pingcap/tidb/store/mockstore"
|
|
"github.com/pingcap/tidb/types"
|
|
"github.com/pingcap/tidb/util"
|
|
"github.com/pingcap/tidb/util/testleak"
|
|
"github.com/pingcap/tidb/util/testutil"
|
|
)
|
|
|
|
func TestT(t *testing.T) {
|
|
CustomVerboseFlag = true
|
|
TestingT(t)
|
|
}
|
|
|
|
var _ = Suite(&testSuite{})
|
|
|
|
type testSuite struct {
|
|
}
|
|
|
|
func (*testSuite) TestT(c *C) {
|
|
defer testleak.AfterTest(c)()
|
|
store, err := mockstore.NewMockTikvStore()
|
|
c.Assert(err, IsNil)
|
|
defer store.Close()
|
|
// Make sure it calls perfschema.Init().
|
|
dom, err := session.BootstrapSession(store)
|
|
c.Assert(err, IsNil)
|
|
defer dom.Close()
|
|
|
|
handle := infoschema.NewHandle(store)
|
|
dbName := model.NewCIStr("Test")
|
|
tbName := model.NewCIStr("T")
|
|
colName := model.NewCIStr("A")
|
|
idxName := model.NewCIStr("idx")
|
|
noexist := model.NewCIStr("noexist")
|
|
|
|
colID, err := genGlobalID(store)
|
|
c.Assert(err, IsNil)
|
|
colInfo := &model.ColumnInfo{
|
|
ID: colID,
|
|
Name: colName,
|
|
Offset: 0,
|
|
FieldType: *types.NewFieldType(mysql.TypeLonglong),
|
|
State: model.StatePublic,
|
|
}
|
|
|
|
idxInfo := &model.IndexInfo{
|
|
Name: idxName,
|
|
Table: tbName,
|
|
Columns: []*model.IndexColumn{
|
|
{
|
|
Name: colName,
|
|
Offset: 0,
|
|
Length: 10,
|
|
},
|
|
},
|
|
Unique: true,
|
|
Primary: true,
|
|
State: model.StatePublic,
|
|
}
|
|
|
|
tbID, err := genGlobalID(store)
|
|
c.Assert(err, IsNil)
|
|
tblInfo := &model.TableInfo{
|
|
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: dbID,
|
|
Name: dbName,
|
|
Tables: []*model.TableInfo{tblInfo},
|
|
State: model.StatePublic,
|
|
}
|
|
|
|
dbInfos := []*model.DBInfo{dbInfo}
|
|
err = kv.RunInNewTxn(store, true, func(txn kv.Transaction) error {
|
|
meta.NewMeta(txn).CreateDatabase(dbInfo)
|
|
return errors.Trace(err)
|
|
})
|
|
c.Assert(err, IsNil)
|
|
|
|
builder, err := infoschema.NewBuilder(handle).InitWithDBInfos(dbInfos, 1)
|
|
c.Assert(err, IsNil)
|
|
|
|
txn, err := store.Begin()
|
|
c.Assert(err, IsNil)
|
|
checkApplyCreateNonExistsSchemaDoesNotPanic(c, txn, builder)
|
|
checkApplyCreateNonExistsTableDoesNotPanic(c, txn, builder, dbID)
|
|
txn.Rollback()
|
|
|
|
builder.Build()
|
|
is := handle.Get()
|
|
|
|
schemaNames := is.AllSchemaNames()
|
|
c.Assert(schemaNames, HasLen, 4)
|
|
c.Assert(testutil.CompareUnorderedStringSlice(schemaNames, []string{util.InformationSchemaName.O, util.MetricSchemaName.O, util.PerformanceSchemaName.O, "Test"}), IsTrue)
|
|
|
|
schemas := is.AllSchemas()
|
|
c.Assert(schemas, HasLen, 4)
|
|
schemas = is.Clone()
|
|
c.Assert(schemas, HasLen, 4)
|
|
|
|
c.Assert(is.SchemaExists(dbName), IsTrue)
|
|
c.Assert(is.SchemaExists(noexist), IsFalse)
|
|
|
|
schema, ok := is.SchemaByID(dbID)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(schema, NotNil)
|
|
|
|
schema, ok = is.SchemaByID(tbID)
|
|
c.Assert(ok, IsFalse)
|
|
c.Assert(schema, IsNil)
|
|
|
|
schema, ok = is.SchemaByName(dbName)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(schema, NotNil)
|
|
|
|
schema, ok = is.SchemaByName(noexist)
|
|
c.Assert(ok, IsFalse)
|
|
c.Assert(schema, IsNil)
|
|
|
|
schema, ok = is.SchemaByTable(tblInfo)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(schema, NotNil)
|
|
|
|
noexistTblInfo := &model.TableInfo{ID: 12345, Name: tblInfo.Name}
|
|
schema, ok = is.SchemaByTable(noexistTblInfo)
|
|
c.Assert(ok, IsFalse)
|
|
c.Assert(schema, IsNil)
|
|
|
|
c.Assert(is.TableExists(dbName, tbName), IsTrue)
|
|
c.Assert(is.TableExists(dbName, noexist), IsFalse)
|
|
c.Assert(is.TableIsView(dbName, tbName), IsFalse)
|
|
c.Assert(is.TableIsSequence(dbName, tbName), IsFalse)
|
|
|
|
tb, ok := is.TableByID(tbID)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(tb, NotNil)
|
|
|
|
tb, ok = is.TableByID(dbID)
|
|
c.Assert(ok, IsFalse)
|
|
c.Assert(tb, IsNil)
|
|
|
|
alloc, ok := is.AllocByID(tbID)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(alloc, NotNil)
|
|
|
|
tb, err = is.TableByName(dbName, tbName)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(tb, NotNil)
|
|
|
|
_, err = is.TableByName(dbName, noexist)
|
|
c.Assert(err, NotNil)
|
|
|
|
tbs := is.SchemaTables(dbName)
|
|
c.Assert(tbs, HasLen, 1)
|
|
|
|
tbs = is.SchemaTables(noexist)
|
|
c.Assert(tbs, HasLen, 0)
|
|
|
|
// Make sure partitions table exists
|
|
tb, err = is.TableByName(model.NewCIStr("information_schema"), model.NewCIStr("partitions"))
|
|
c.Assert(err, IsNil)
|
|
c.Assert(tb, NotNil)
|
|
|
|
err = kv.RunInNewTxn(store, true, func(txn kv.Transaction) error {
|
|
meta.NewMeta(txn).CreateTableOrView(dbID, tblInfo)
|
|
return errors.Trace(err)
|
|
})
|
|
c.Assert(err, IsNil)
|
|
txn, err = store.Begin()
|
|
c.Assert(err, IsNil)
|
|
_, err = builder.ApplyDiff(meta.NewMeta(txn), &model.SchemaDiff{Type: model.ActionRenameTable, SchemaID: dbID, TableID: tbID, OldSchemaID: dbID})
|
|
c.Assert(err, IsNil)
|
|
txn.Rollback()
|
|
builder.Build()
|
|
is = handle.Get()
|
|
schema, ok = is.SchemaByID(dbID)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(len(schema.Tables), Equals, 1)
|
|
|
|
emptyHandle := handle.EmptyClone()
|
|
c.Assert(emptyHandle.Get(), IsNil)
|
|
}
|
|
|
|
func (testSuite) TestMockInfoSchema(c *C) {
|
|
tblID := int64(1234)
|
|
tblName := model.NewCIStr("tbl_m")
|
|
tableInfo := &model.TableInfo{
|
|
ID: tblID,
|
|
Name: tblName,
|
|
State: model.StatePublic,
|
|
}
|
|
colInfo := &model.ColumnInfo{
|
|
State: model.StatePublic,
|
|
Offset: 0,
|
|
Name: model.NewCIStr("h"),
|
|
FieldType: *types.NewFieldType(mysql.TypeLong),
|
|
ID: 1,
|
|
}
|
|
tableInfo.Columns = []*model.ColumnInfo{colInfo}
|
|
is := infoschema.MockInfoSchema([]*model.TableInfo{tableInfo})
|
|
tbl, ok := is.TableByID(tblID)
|
|
c.Assert(ok, IsTrue)
|
|
c.Assert(tbl.Meta().Name, Equals, tblName)
|
|
c.Assert(tbl.Cols()[0].ColumnInfo, Equals, colInfo)
|
|
}
|
|
|
|
func checkApplyCreateNonExistsSchemaDoesNotPanic(c *C, txn kv.Transaction, builder *infoschema.Builder) {
|
|
m := meta.NewMeta(txn)
|
|
_, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateSchema, SchemaID: 999})
|
|
c.Assert(infoschema.ErrDatabaseNotExists.Equal(err), IsTrue)
|
|
}
|
|
|
|
func checkApplyCreateNonExistsTableDoesNotPanic(c *C, txn kv.Transaction, builder *infoschema.Builder, dbID int64) {
|
|
m := meta.NewMeta(txn)
|
|
_, err := builder.ApplyDiff(m, &model.SchemaDiff{Type: model.ActionCreateTable, SchemaID: dbID, TableID: 999})
|
|
c.Assert(infoschema.ErrTableNotExists.Equal(err), IsTrue)
|
|
}
|
|
|
|
// TestConcurrent makes sure it is safe to concurrently create handle on multiple stores.
|
|
func (testSuite) TestConcurrent(c *C) {
|
|
defer testleak.AfterTest(c)()
|
|
storeCount := 5
|
|
stores := make([]kv.Storage, storeCount)
|
|
for i := 0; i < storeCount; i++ {
|
|
store, err := mockstore.NewMockTikvStore()
|
|
c.Assert(err, IsNil)
|
|
stores[i] = store
|
|
}
|
|
defer func() {
|
|
for _, store := range stores {
|
|
store.Close()
|
|
}
|
|
}()
|
|
var wg sync.WaitGroup
|
|
wg.Add(storeCount)
|
|
for _, store := range stores {
|
|
go func(s kv.Storage) {
|
|
defer wg.Done()
|
|
_ = infoschema.NewHandle(s)
|
|
}(store)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
// TestInfoTables makes sure that all tables of information_schema could be found in infoschema handle.
|
|
func (*testSuite) TestInfoTables(c *C) {
|
|
defer testleak.AfterTest(c)()
|
|
store, err := mockstore.NewMockTikvStore()
|
|
c.Assert(err, IsNil)
|
|
defer store.Close()
|
|
handle := infoschema.NewHandle(store)
|
|
builder, err := infoschema.NewBuilder(handle).InitWithDBInfos(nil, 0)
|
|
c.Assert(err, IsNil)
|
|
builder.Build()
|
|
is := handle.Get()
|
|
c.Assert(is, NotNil)
|
|
|
|
infoTables := []string{
|
|
"SCHEMATA",
|
|
"TABLES",
|
|
"COLUMNS",
|
|
"STATISTICS",
|
|
"CHARACTER_SETS",
|
|
"COLLATIONS",
|
|
"FILES",
|
|
"PROFILING",
|
|
"PARTITIONS",
|
|
"KEY_COLUMN_USAGE",
|
|
"REFERENTIAL_CONSTRAINTS",
|
|
"SESSION_VARIABLES",
|
|
"PLUGINS",
|
|
"TABLE_CONSTRAINTS",
|
|
"TRIGGERS",
|
|
"USER_PRIVILEGES",
|
|
"ENGINES",
|
|
"VIEWS",
|
|
"ROUTINES",
|
|
"SCHEMA_PRIVILEGES",
|
|
"COLUMN_PRIVILEGES",
|
|
"TABLE_PRIVILEGES",
|
|
"PARAMETERS",
|
|
"EVENTS",
|
|
"GLOBAL_STATUS",
|
|
"GLOBAL_VARIABLES",
|
|
"SESSION_STATUS",
|
|
"OPTIMIZER_TRACE",
|
|
"TABLESPACES",
|
|
"COLLATION_CHARACTER_SET_APPLICABILITY",
|
|
"PROCESSLIST",
|
|
}
|
|
for _, t := range infoTables {
|
|
tb, err1 := is.TableByName(util.InformationSchemaName, model.NewCIStr(t))
|
|
c.Assert(err1, IsNil)
|
|
c.Assert(tb, 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)
|
|
}
|
|
|