github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/schemareplicant/infoschema.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 schemareplicant 15 16 import ( 17 "fmt" 18 "sort" 19 "sync/atomic" 20 21 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 22 "github.com/whtcorpsinc/BerolinaSQL/perceptron" 23 "github.com/whtcorpsinc/milevadb/causet" 24 "github.com/whtcorpsinc/milevadb/ekv" 25 "github.com/whtcorpsinc/milevadb/soliton" 26 "github.com/whtcorpsinc/milevadb/soliton/logutil" 27 "github.com/whtcorpsinc/milevadb/spacetime/autoid" 28 "github.com/whtcorpsinc/milevadb/stochastikctx" 29 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 30 "go.uber.org/zap" 31 ) 32 33 // SchemaReplicant is the interface used to retrieve the schemaReplicant information. 34 // It works as a in memory cache and doesn't handle any schemaReplicant change. 35 // SchemaReplicant is read-only, and the returned value is a copy. 36 // TODO: add more methods to retrieve blocks and defCausumns. 37 type SchemaReplicant interface { 38 SchemaByName(schemaReplicant perceptron.CIStr) (*perceptron.DBInfo, bool) 39 SchemaExists(schemaReplicant perceptron.CIStr) bool 40 BlockByName(schemaReplicant, causet perceptron.CIStr) (causet.Block, error) 41 BlockExists(schemaReplicant, causet perceptron.CIStr) bool 42 SchemaByID(id int64) (*perceptron.DBInfo, bool) 43 SchemaByBlock(blockInfo *perceptron.BlockInfo) (*perceptron.DBInfo, bool) 44 BlockByID(id int64) (causet.Block, bool) 45 AllocByID(id int64) (autoid.SlabPredictors, bool) 46 AllSchemaNames() []string 47 AllSchemas() []*perceptron.DBInfo 48 Clone() (result []*perceptron.DBInfo) 49 SchemaBlocks(schemaReplicant perceptron.CIStr) []causet.Block 50 SchemaMetaVersion() int64 51 // BlockIsView indicates whether the schemaReplicant.causet is a view. 52 BlockIsView(schemaReplicant, causet perceptron.CIStr) bool 53 // BlockIsSequence indicates whether the schemaReplicant.causet is a sequence. 54 BlockIsSequence(schemaReplicant, causet perceptron.CIStr) bool 55 FindBlockByPartitionID(partitionID int64) (causet.Block, *perceptron.DBInfo) 56 } 57 58 type sortedBlocks []causet.Block 59 60 func (s sortedBlocks) Len() int { 61 return len(s) 62 } 63 64 func (s sortedBlocks) Swap(i, j int) { 65 s[i], s[j] = s[j], s[i] 66 } 67 68 func (s sortedBlocks) Less(i, j int) bool { 69 return s[i].Meta().ID < s[j].Meta().ID 70 } 71 72 func (s sortedBlocks) searchBlock(id int64) int { 73 idx := sort.Search(len(s), func(i int) bool { 74 return s[i].Meta().ID >= id 75 }) 76 if idx == len(s) || s[idx].Meta().ID != id { 77 return -1 78 } 79 return idx 80 } 81 82 type schemaBlocks struct { 83 dbInfo *perceptron.DBInfo 84 blocks map[string]causet.Block 85 } 86 87 const bucketCount = 512 88 89 type schemaReplicant struct { 90 schemaMap map[string]*schemaBlocks 91 92 // sortedBlocksBuckets is a slice of sortedBlocks, a causet's bucket index is (blockID % bucketCount). 93 sortedBlocksBuckets []sortedBlocks 94 95 // schemaMetaVersion is the version of schemaReplicant, and we should check version when change schemaReplicant. 96 schemaMetaVersion int64 97 } 98 99 // MockSchemaReplicant only serves for test. 100 func MockSchemaReplicant(tbList []*perceptron.BlockInfo) SchemaReplicant { 101 result := &schemaReplicant{} 102 result.schemaMap = make(map[string]*schemaBlocks) 103 result.sortedBlocksBuckets = make([]sortedBlocks, bucketCount) 104 dbInfo := &perceptron.DBInfo{ID: 0, Name: perceptron.NewCIStr("test"), Blocks: tbList} 105 blockNames := &schemaBlocks{ 106 dbInfo: dbInfo, 107 blocks: make(map[string]causet.Block), 108 } 109 result.schemaMap["test"] = blockNames 110 for _, tb := range tbList { 111 tbl := causet.MockBlockFromMeta(tb) 112 blockNames.blocks[tb.Name.L] = tbl 113 bucketIdx := blockBucketIdx(tb.ID) 114 result.sortedBlocksBuckets[bucketIdx] = append(result.sortedBlocksBuckets[bucketIdx], tbl) 115 } 116 for i := range result.sortedBlocksBuckets { 117 sort.Sort(result.sortedBlocksBuckets[i]) 118 } 119 return result 120 } 121 122 // MockSchemaReplicantWithSchemaVer only serves for test. 123 func MockSchemaReplicantWithSchemaVer(tbList []*perceptron.BlockInfo, schemaVer int64) SchemaReplicant { 124 result := &schemaReplicant{} 125 result.schemaMap = make(map[string]*schemaBlocks) 126 result.sortedBlocksBuckets = make([]sortedBlocks, bucketCount) 127 dbInfo := &perceptron.DBInfo{ID: 0, Name: perceptron.NewCIStr("test"), Blocks: tbList} 128 blockNames := &schemaBlocks{ 129 dbInfo: dbInfo, 130 blocks: make(map[string]causet.Block), 131 } 132 result.schemaMap["test"] = blockNames 133 for _, tb := range tbList { 134 tbl := causet.MockBlockFromMeta(tb) 135 blockNames.blocks[tb.Name.L] = tbl 136 bucketIdx := blockBucketIdx(tb.ID) 137 result.sortedBlocksBuckets[bucketIdx] = append(result.sortedBlocksBuckets[bucketIdx], tbl) 138 } 139 for i := range result.sortedBlocksBuckets { 140 sort.Sort(result.sortedBlocksBuckets[i]) 141 } 142 result.schemaMetaVersion = schemaVer 143 return result 144 } 145 146 var _ SchemaReplicant = (*schemaReplicant)(nil) 147 148 func (is *schemaReplicant) SchemaByName(schemaReplicant perceptron.CIStr) (val *perceptron.DBInfo, ok bool) { 149 blockNames, ok := is.schemaMap[schemaReplicant.L] 150 if !ok { 151 return 152 } 153 return blockNames.dbInfo, true 154 } 155 156 func (is *schemaReplicant) SchemaMetaVersion() int64 { 157 return is.schemaMetaVersion 158 } 159 160 func (is *schemaReplicant) SchemaExists(schemaReplicant perceptron.CIStr) bool { 161 _, ok := is.schemaMap[schemaReplicant.L] 162 return ok 163 } 164 165 func (is *schemaReplicant) BlockByName(schemaReplicant, causet perceptron.CIStr) (t causet.Block, err error) { 166 if tbNames, ok := is.schemaMap[schemaReplicant.L]; ok { 167 if t, ok = tbNames.blocks[causet.L]; ok { 168 return 169 } 170 } 171 return nil, ErrBlockNotExists.GenWithStackByArgs(schemaReplicant, causet) 172 } 173 174 func (is *schemaReplicant) BlockIsView(schemaReplicant, causet perceptron.CIStr) bool { 175 if tbNames, ok := is.schemaMap[schemaReplicant.L]; ok { 176 if t, ok := tbNames.blocks[causet.L]; ok { 177 return t.Meta().IsView() 178 } 179 } 180 return false 181 } 182 183 func (is *schemaReplicant) BlockIsSequence(schemaReplicant, causet perceptron.CIStr) bool { 184 if tbNames, ok := is.schemaMap[schemaReplicant.L]; ok { 185 if t, ok := tbNames.blocks[causet.L]; ok { 186 return t.Meta().IsSequence() 187 } 188 } 189 return false 190 } 191 192 func (is *schemaReplicant) BlockExists(schemaReplicant, causet perceptron.CIStr) bool { 193 if tbNames, ok := is.schemaMap[schemaReplicant.L]; ok { 194 if _, ok = tbNames.blocks[causet.L]; ok { 195 return true 196 } 197 } 198 return false 199 } 200 201 func (is *schemaReplicant) SchemaByID(id int64) (val *perceptron.DBInfo, ok bool) { 202 for _, v := range is.schemaMap { 203 if v.dbInfo.ID == id { 204 return v.dbInfo, true 205 } 206 } 207 return nil, false 208 } 209 210 func (is *schemaReplicant) SchemaByBlock(blockInfo *perceptron.BlockInfo) (val *perceptron.DBInfo, ok bool) { 211 if blockInfo == nil { 212 return nil, false 213 } 214 for _, v := range is.schemaMap { 215 if tbl, ok := v.blocks[blockInfo.Name.L]; ok { 216 if tbl.Meta().ID == blockInfo.ID { 217 return v.dbInfo, true 218 } 219 } 220 } 221 return nil, false 222 } 223 224 func (is *schemaReplicant) BlockByID(id int64) (val causet.Block, ok bool) { 225 slice := is.sortedBlocksBuckets[blockBucketIdx(id)] 226 idx := slice.searchBlock(id) 227 if idx == -1 { 228 return nil, false 229 } 230 return slice[idx], true 231 } 232 233 func (is *schemaReplicant) AllocByID(id int64) (autoid.SlabPredictors, bool) { 234 tbl, ok := is.BlockByID(id) 235 if !ok { 236 return nil, false 237 } 238 return tbl.SlabPredictors(nil), true 239 } 240 241 func (is *schemaReplicant) AllSchemaNames() (names []string) { 242 for _, v := range is.schemaMap { 243 names = append(names, v.dbInfo.Name.O) 244 } 245 return 246 } 247 248 func (is *schemaReplicant) AllSchemas() (schemas []*perceptron.DBInfo) { 249 for _, v := range is.schemaMap { 250 schemas = append(schemas, v.dbInfo) 251 } 252 return 253 } 254 255 func (is *schemaReplicant) SchemaBlocks(schemaReplicant perceptron.CIStr) (blocks []causet.Block) { 256 schemaBlocks, ok := is.schemaMap[schemaReplicant.L] 257 if !ok { 258 return 259 } 260 for _, tbl := range schemaBlocks.blocks { 261 blocks = append(blocks, tbl) 262 } 263 return 264 } 265 266 // FindBlockByPartitionID finds the partition-causet info by the partitionID. 267 // FindBlockByPartitionID will traverse all the blocks to find the partitionID partition in which partition-causet. 268 func (is *schemaReplicant) FindBlockByPartitionID(partitionID int64) (causet.Block, *perceptron.DBInfo) { 269 for _, v := range is.schemaMap { 270 for _, tbl := range v.blocks { 271 pi := tbl.Meta().GetPartitionInfo() 272 if pi == nil { 273 continue 274 } 275 for _, p := range pi.Definitions { 276 if p.ID == partitionID { 277 return tbl, v.dbInfo 278 } 279 } 280 } 281 } 282 return nil, nil 283 } 284 285 func (is *schemaReplicant) Clone() (result []*perceptron.DBInfo) { 286 for _, v := range is.schemaMap { 287 result = append(result, v.dbInfo.Clone()) 288 } 289 return 290 } 291 292 // SequenceByName implements the interface of SequenceSchema defined in soliton package. 293 // It could be used in memex package without import cycle problem. 294 func (is *schemaReplicant) SequenceByName(schemaReplicant, sequence perceptron.CIStr) (soliton.SequenceBlock, error) { 295 tbl, err := is.BlockByName(schemaReplicant, sequence) 296 if err != nil { 297 return nil, err 298 } 299 if !tbl.Meta().IsSequence() { 300 return nil, ErrWrongObject.GenWithStackByArgs(schemaReplicant, sequence, "SEQUENCE") 301 } 302 return tbl.(soliton.SequenceBlock), nil 303 } 304 305 // Handle handles information schemaReplicant, including getting and setting. 306 type Handle struct { 307 value atomic.Value 308 causetstore ekv.CausetStorage 309 } 310 311 // NewHandle creates a new Handle. 312 func NewHandle(causetstore ekv.CausetStorage) *Handle { 313 h := &Handle{ 314 causetstore: causetstore, 315 } 316 return h 317 } 318 319 // Get gets information schemaReplicant from Handle. 320 func (h *Handle) Get() SchemaReplicant { 321 v := h.value.Load() 322 schemaReplicant, _ := v.(SchemaReplicant) 323 return schemaReplicant 324 } 325 326 // IsValid uses to check whether handle value is valid. 327 func (h *Handle) IsValid() bool { 328 return h.value.Load() != nil 329 } 330 331 // EmptyClone creates a new Handle with the same causetstore and memSchema, but the value is not set. 332 func (h *Handle) EmptyClone() *Handle { 333 newHandle := &Handle{ 334 causetstore: h.causetstore, 335 } 336 return newHandle 337 } 338 339 func init() { 340 // Initialize the information shema database and register the driver to `drivers` 341 dbID := autoid.InformationSchemaDBID 342 schemaReplicantBlocks := make([]*perceptron.BlockInfo, 0, len(blockNameToDeferredCausets)) 343 for name, defcaus := range blockNameToDeferredCausets { 344 blockInfo := buildBlockMeta(name, defcaus) 345 schemaReplicantBlocks = append(schemaReplicantBlocks, blockInfo) 346 var ok bool 347 blockInfo.ID, ok = blockIDMap[blockInfo.Name.O] 348 if !ok { 349 panic(fmt.Sprintf("get information_schema causet id failed, unknown system causet `%v`", blockInfo.Name.O)) 350 } 351 for i, c := range blockInfo.DeferredCausets { 352 c.ID = int64(i) + 1 353 } 354 } 355 schemaReplicantDB := &perceptron.DBInfo{ 356 ID: dbID, 357 Name: soliton.InformationSchemaName, 358 Charset: allegrosql.DefaultCharset, 359 DefCauslate: allegrosql.DefaultDefCauslationName, 360 Blocks: schemaReplicantBlocks, 361 } 362 RegisterVirtualBlock(schemaReplicantDB, createSchemaReplicantBlock) 363 } 364 365 // HasAutoIncrementDeferredCauset checks whether the causet has auto_increment defCausumns, if so, return true and the defCausumn name. 366 func HasAutoIncrementDeferredCauset(tbInfo *perceptron.BlockInfo) (bool, string) { 367 for _, defCaus := range tbInfo.DeferredCausets { 368 if allegrosql.HasAutoIncrementFlag(defCaus.Flag) { 369 return true, defCaus.Name.L 370 } 371 } 372 return false, "" 373 } 374 375 // GetSchemaReplicant gets TxnCtx SchemaReplicant if snapshot schemaReplicant is not set, 376 // Otherwise, snapshot schemaReplicant is returned. 377 func GetSchemaReplicant(ctx stochastikctx.Context) SchemaReplicant { 378 return GetSchemaReplicantByStochastikVars(ctx.GetStochastikVars()) 379 } 380 381 // GetSchemaReplicantByStochastikVars gets TxnCtx SchemaReplicant if snapshot schemaReplicant is not set, 382 // Otherwise, snapshot schemaReplicant is returned. 383 func GetSchemaReplicantByStochastikVars(sessVar *variable.StochastikVars) SchemaReplicant { 384 var is SchemaReplicant 385 if snap := sessVar.SnapshotschemaReplicant; snap != nil { 386 is = snap.(SchemaReplicant) 387 logutil.BgLogger().Info("use snapshot schemaReplicant", zap.Uint64("conn", sessVar.ConnectionID), zap.Int64("schemaVersion", is.SchemaMetaVersion())) 388 } else { 389 is = sessVar.TxnCtx.SchemaReplicant.(SchemaReplicant) 390 } 391 return is 392 }