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 }