github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/catalog/database.go (about) 1 // Copyright 2021 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package catalog 16 17 import ( 18 "bytes" 19 "fmt" 20 "io" 21 "sort" 22 "sync" 23 "unsafe" 24 25 pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog" 26 "github.com/matrixorigin/matrixone/pkg/common/moerr" 27 "github.com/matrixorigin/matrixone/pkg/container/types" 28 "github.com/matrixorigin/matrixone/pkg/logutil" 29 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 30 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 31 ) 32 33 type accessInfo struct { 34 TenantID, UserID, RoleID uint32 35 CreateAt types.Timestamp 36 } 37 38 const ( 39 AccessInfoSize int64 = int64(unsafe.Sizeof(accessInfo{})) 40 ) 41 42 func EncodeAccessInfo(ai *accessInfo) []byte { 43 return unsafe.Slice((*byte)(unsafe.Pointer(ai)), AccessInfoSize) 44 } 45 46 func (ai *accessInfo) WriteTo(w io.Writer) (n int64, err error) { 47 w.Write(EncodeAccessInfo(ai)) 48 return AccessInfoSize, nil 49 } 50 51 func (ai *accessInfo) ReadFrom(r io.Reader) (n int64, err error) { 52 r.Read(EncodeAccessInfo(ai)) 53 return AccessInfoSize, nil 54 } 55 56 func dbVisibilityFn[T *DBEntry](n *common.GenericDLNode[*DBEntry], txn txnif.TxnReader) (visible, dropped bool, name string) { 57 db := n.GetPayload() 58 visible, dropped = db.GetVisibility(txn) 59 return 60 } 61 62 type DBEntry struct { 63 ID uint64 64 *BaseEntryImpl[*EmptyMVCCNode] 65 catalog *Catalog 66 *DBNode 67 fullName string 68 isSys bool 69 70 entries map[uint64]*common.GenericDLNode[*TableEntry] 71 // nameNodes[ABC] is a linked list of all table entries had been once named as ABC 72 nameNodes map[string]*nodeList[*TableEntry] 73 link *common.GenericSortedDList[*TableEntry] 74 75 nodesMu sync.RWMutex 76 } 77 78 func (entry *TableEntry) Less(b *TableEntry) int { 79 return CompareUint64(entry.ID, b.ID) 80 } 81 82 func NewDBEntryWithID(catalog *Catalog, name string, createSql, datTyp string, id uint64, txn txnif.AsyncTxn) *DBEntry { 83 e := &DBEntry{ 84 ID: id, 85 BaseEntryImpl: NewBaseEntry( 86 func() *EmptyMVCCNode { return &EmptyMVCCNode{} }), 87 catalog: catalog, 88 DBNode: &DBNode{ 89 name: name, 90 createSql: createSql, 91 datType: datTyp, 92 }, 93 entries: make(map[uint64]*common.GenericDLNode[*TableEntry]), 94 nameNodes: make(map[string]*nodeList[*TableEntry]), 95 link: common.NewGenericSortedDList((*TableEntry).Less), 96 } 97 if txn != nil { 98 // Only in unit test, txn can be nil 99 e.acInfo.TenantID = txn.GetTenantID() 100 e.acInfo.UserID, e.acInfo.RoleID = txn.GetUserAndRoleID() 101 } 102 e.CreateWithTxn(txn, &EmptyMVCCNode{}) 103 e.acInfo.CreateAt = types.CurrentTimestamp() 104 return e 105 } 106 107 func NewSystemDBEntry(catalog *Catalog) *DBEntry { 108 entry := &DBEntry{ 109 ID: pkgcatalog.MO_CATALOG_ID, 110 BaseEntryImpl: NewBaseEntry( 111 func() *EmptyMVCCNode { 112 return &EmptyMVCCNode{} 113 }), 114 catalog: catalog, 115 DBNode: &DBNode{ 116 name: pkgcatalog.MO_CATALOG, 117 createSql: "create database " + pkgcatalog.MO_CATALOG, 118 }, 119 entries: make(map[uint64]*common.GenericDLNode[*TableEntry]), 120 nameNodes: make(map[string]*nodeList[*TableEntry]), 121 link: common.NewGenericSortedDList((*TableEntry).Less), 122 isSys: true, 123 } 124 entry.CreateWithTS(types.SystemDBTS, &EmptyMVCCNode{}) 125 return entry 126 } 127 128 func NewReplayDBEntry() *DBEntry { 129 entry := &DBEntry{ 130 BaseEntryImpl: NewReplayBaseEntry( 131 func() *EmptyMVCCNode { return &EmptyMVCCNode{} }), 132 entries: make(map[uint64]*common.GenericDLNode[*TableEntry]), 133 nameNodes: make(map[string]*nodeList[*TableEntry]), 134 link: common.NewGenericSortedDList((*TableEntry).Less), 135 } 136 return entry 137 } 138 func (e *DBEntry) GetID() uint64 { return e.ID } 139 func (e *DBEntry) IsSystemDB() bool { return e.isSys } 140 func (e *DBEntry) CoarseTableCnt() int { 141 e.RLock() 142 defer e.RUnlock() 143 return len(e.entries) 144 } 145 146 func (e *DBEntry) Less(b *DBEntry) int { 147 return CompareUint64(e.ID, b.ID) 148 } 149 150 func (e *DBEntry) GetTenantID() uint32 { return e.acInfo.TenantID } 151 func (e *DBEntry) GetUserID() uint32 { return e.acInfo.UserID } 152 func (e *DBEntry) GetRoleID() uint32 { return e.acInfo.RoleID } 153 func (e *DBEntry) GetCreateAt() types.Timestamp { return e.acInfo.CreateAt } 154 func (e *DBEntry) GetName() string { return e.name } 155 func (e *DBEntry) GetCreateSql() string { return e.createSql } 156 func (e *DBEntry) IsSubscription() bool { 157 return e.datType == pkgcatalog.SystemDBTypeSubscription 158 } 159 func (e *DBEntry) GetDatType() string { return e.datType } 160 func (e *DBEntry) GetFullName() string { 161 if len(e.fullName) == 0 { 162 e.fullName = genDBFullName(e.acInfo.TenantID, e.name) 163 } 164 return e.fullName 165 } 166 167 func (e *DBEntry) String() string { 168 e.RLock() 169 defer e.RUnlock() 170 return e.StringLocked() 171 } 172 173 func (e *DBEntry) StringLocked() string { 174 return e.StringWithlevelLocked(common.PPL1) 175 } 176 func (e *DBEntry) StringWithLevel(level common.PPLevel) string { 177 e.RLock() 178 defer e.RUnlock() 179 return e.StringWithlevelLocked(level) 180 } 181 182 func (e *DBEntry) StringWithlevelLocked(level common.PPLevel) string { 183 if level <= common.PPL1 { 184 return fmt.Sprintf("DB[%d][name=%s][C@%s,D@%s]", 185 e.ID, e.GetFullName(), e.GetCreatedAtLocked().ToString(), e.GetDeleteAtLocked().ToString()) 186 } 187 return fmt.Sprintf("DB%s[name=%s, id=%d]", e.BaseEntryImpl.StringLocked(), e.GetFullName(), e.ID) 188 } 189 190 func (e *DBEntry) MakeTableIt(reverse bool) *common.GenericSortedDListIt[*TableEntry] { 191 e.RLock() 192 defer e.RUnlock() 193 return common.NewGenericSortedDListIt(e.RWMutex, e.link, reverse) 194 } 195 196 func (e *DBEntry) PPString(level common.PPLevel, depth int, prefix string) string { 197 var w bytes.Buffer 198 _, _ = w.WriteString(fmt.Sprintf("%s%s%s", common.RepeatStr("\t", depth), prefix, e.StringWithLevel(level))) 199 if level == common.PPL0 { 200 return w.String() 201 } 202 it := e.MakeTableIt(true) 203 for it.Valid() { 204 table := it.Get().GetPayload() 205 _ = w.WriteByte('\n') 206 _, _ = w.WriteString(table.PPString(level, depth+1, "")) 207 it.Next() 208 } 209 return w.String() 210 } 211 func (e *DBEntry) AsCommonID() *common.ID { 212 return &common.ID{ 213 DbID: e.ID, 214 } 215 } 216 func (e *DBEntry) GetObjectEntryByID(id *common.ID) (obj *ObjectEntry, err error) { 217 e.RLock() 218 table, err := e.GetTableEntryByID(id.TableID) 219 e.RUnlock() 220 if err != nil { 221 return 222 } 223 obj, err = table.GetObjectByID(id.ObjectID()) 224 return 225 } 226 227 func (e *DBEntry) GetItemNodeByIDLocked(id uint64) *common.GenericDLNode[*TableEntry] { 228 return e.entries[id] 229 } 230 231 func (e *DBEntry) GetTableEntryByID(id uint64) (table *TableEntry, err error) { 232 e.RLock() 233 defer e.RUnlock() 234 node := e.entries[id] 235 if node == nil { 236 return nil, moerr.GetOkExpectedEOB() 237 } 238 table = node.GetPayload() 239 return 240 } 241 242 func (e *DBEntry) txnGetNodeByName( 243 tenantID uint32, 244 name string, 245 txn txnif.TxnReader) (*common.GenericDLNode[*TableEntry], error) { 246 e.RLock() 247 defer e.RUnlock() 248 fullName := genTblFullName(tenantID, name) 249 node := e.nameNodes[fullName] 250 if node == nil { 251 return nil, moerr.GetOkExpectedEOB() 252 } 253 return node.TxnGetNodeLocked(txn, name) 254 } 255 256 func (e *DBEntry) TxnGetTableEntryByName(name string, txn txnif.AsyncTxn) (entry *TableEntry, err error) { 257 n, err := e.txnGetNodeByName(txn.GetTenantID(), name, txn) 258 if err != nil { 259 return 260 } 261 entry = n.GetPayload() 262 return 263 } 264 265 func (e *DBEntry) GetTableEntryByName( 266 tenantID uint32, 267 name string, 268 txn txnif.TxnReader) (entry *TableEntry, err error) { 269 n, err := e.txnGetNodeByName(tenantID, name, txn) 270 if err != nil { 271 return 272 } 273 entry = n.GetPayload() 274 return 275 } 276 277 func (e *DBEntry) TxnGetTableEntryByID(id uint64, txn txnif.AsyncTxn) (entry *TableEntry, err error) { 278 entry, err = e.GetTableEntryByID(id) 279 if err != nil { 280 return 281 } 282 //check whether visible and dropped. 283 visible, dropped := entry.GetVisibility(txn) 284 if !visible || dropped { 285 return nil, moerr.GetOkExpectedEOB() 286 } 287 return 288 } 289 290 // Catalog entry is dropped in following steps: 291 // 1. Locate the record by timestamp 292 // 2. Check conflication. 293 // 2.1 Wait for the related txn if need. 294 // 2.2 w-w conflict when 1. there's an active txn; or 295 // 2. the CommitTS of the latest related txn is larger than StartTS of write txn 296 // 297 // 3. Check duplicate/not found. 298 // If the entry has already been dropped, return ErrNotFound. 299 func (e *DBEntry) DropTableEntry(name string, txn txnif.AsyncTxn) (newEntry bool, deleted *TableEntry, err error) { 300 tn, err := e.txnGetNodeByName(txn.GetTenantID(), name, txn) 301 if err != nil { 302 return 303 } 304 entry := tn.GetPayload() 305 entry.Lock() 306 defer entry.Unlock() 307 newEntry, err = entry.DropEntryLocked(txn) 308 if err == nil { 309 deleted = entry 310 } 311 return 312 } 313 314 func (e *DBEntry) DropTableEntryByID(id uint64, txn txnif.AsyncTxn) (newEntry bool, deleted *TableEntry, err error) { 315 entry, err := e.GetTableEntryByID(id) 316 if err != nil { 317 return 318 } 319 320 entry.Lock() 321 defer entry.Unlock() 322 newEntry, err = entry.DropEntryLocked(txn) 323 if err == nil { 324 deleted = entry 325 } 326 return 327 } 328 329 func (e *DBEntry) CreateTableEntry(schema *Schema, txn txnif.AsyncTxn, dataFactory TableDataFactory) (created *TableEntry, err error) { 330 e.Lock() 331 defer e.Unlock() 332 created = NewTableEntry(e, schema, txn, dataFactory) 333 err = e.AddEntryLocked(created, txn, false) 334 335 return created, err 336 } 337 338 func (e *DBEntry) CreateTableEntryWithTableId(schema *Schema, txn txnif.AsyncTxn, dataFactory TableDataFactory, tableId uint64) (created *TableEntry, err error) { 339 e.Lock() 340 defer e.Unlock() 341 if tableId < pkgcatalog.MO_RESERVED_MAX { 342 return nil, moerr.NewInternalErrorNoCtx("reserved table ID %d", tableId) 343 } 344 //Deduplicate for tableId 345 if _, exist := e.entries[tableId]; exist { 346 return nil, moerr.GetOkExpectedDup() 347 } 348 created = NewTableEntryWithTableId(e, schema, txn, dataFactory, tableId) 349 err = e.AddEntryLocked(created, txn, false) 350 351 return created, err 352 } 353 354 // For test only 355 func (e *DBEntry) PrettyNameIndex() string { 356 buf := &bytes.Buffer{} 357 buf.WriteString(fmt.Sprintf("[%d]NameIndex:\n", len(e.nameNodes))) 358 // iterate all nodes in nameNodes, collect node ids to a string 359 ids := make([]uint64, 0) 360 // sort e.nameNodes by name 361 names := make([]string, 0, len(e.nameNodes)) 362 for name := range e.nameNodes { 363 names = append(names, name) 364 } 365 sort.Strings(names) 366 for _, name := range names { 367 node := e.nameNodes[name] 368 ids = ids[:0] 369 node.ForEachNodes(func(nn *nameNode[*TableEntry]) bool { 370 ids = append(ids, nn.id) 371 return true 372 }) 373 buf.WriteString(fmt.Sprintf(" %s: %v\n", name, ids)) 374 } 375 return buf.String() 376 } 377 378 func (e *DBEntry) RenameTableInTxn(old, new string, tid uint64, tenantID uint32, txn txnif.TxnReader, first bool) error { 379 e.Lock() 380 defer e.Unlock() 381 // if alter again and again in the same txn, previous temp name should be deleted. 382 if !first { 383 e.removeNameIndexLocked(genTblFullName(tenantID, old), tid) 384 } 385 386 newFullName := genTblFullName(tenantID, new) 387 if err := e.checkAddNameConflictLocked(new, tid, e.nameNodes[newFullName], txn); err != nil { 388 return err 389 } 390 // make sure every name node has up to one table id. check case alter A -> B -> A 391 e.removeNameIndexLocked(newFullName, tid) 392 // add to the head of linked list 393 e.addNameIndexLocked(newFullName, tid) 394 395 return nil 396 } 397 398 func (e *DBEntry) addNameIndexLocked(fullname string, tid uint64) { 399 node := e.nameNodes[fullname] 400 if node == nil { 401 nn := newNodeList(e.GetItemNodeByIDLocked, 402 tableVisibilityFn[*TableEntry], 403 &e.nodesMu, 404 fullname) 405 e.nameNodes[fullname] = nn 406 nn.CreateNode(tid) 407 } else { 408 node.CreateNode(tid) 409 } 410 } 411 412 func (e *DBEntry) removeNameIndexLocked(fullname string, tid uint64) { 413 nn := e.nameNodes[fullname] 414 if nn == nil { 415 return 416 } 417 if _, empty := nn.DeleteNode(tid); empty { 418 delete(e.nameNodes, fullname) 419 } 420 } 421 422 func (e *DBEntry) RollbackRenameTable(fullname string, tid uint64) { 423 e.Lock() 424 defer e.Unlock() 425 e.removeNameIndexLocked(fullname, tid) 426 } 427 428 func (e *DBEntry) RemoveEntry(table *TableEntry) (err error) { 429 logutil.Info("[Catalog]", common.OperationField("remove"), 430 common.OperandField(table.String())) 431 e.Lock() 432 defer e.Unlock() 433 if n, ok := e.entries[table.ID]; !ok { 434 return moerr.GetOkExpectedEOB() 435 } else { 436 table.RLock() 437 defer table.RUnlock() 438 prevname := "" 439 // clean all name because RemoveEntry can be called by GC、 440 table.LoopChainLocked(func(m *MVCCNode[*TableMVCCNode]) bool { 441 if prevname == m.BaseNode.Schema.Name { 442 return true 443 } 444 prevname = m.BaseNode.Schema.Name 445 tenantID := m.BaseNode.Schema.AcInfo.TenantID 446 fullname := genTblFullName(tenantID, prevname) 447 nn := e.nameNodes[fullname] 448 if nn == nil { 449 return true 450 } 451 nn.DeleteNode(table.ID) 452 if nn.Length() == 0 { 453 delete(e.nameNodes, fullname) 454 } 455 return true 456 }) 457 e.link.Delete(n) 458 delete(e.entries, table.ID) 459 } 460 return 461 } 462 463 // Catalog entry is created in following steps: 464 // 1. Locate the record. Creating always gets the latest DBEntry. 465 // 2.1 If there doesn't exist a DBEntry, add new entry and return. 466 // 2.2 If there exists a DBEntry: 467 // 2.2.1 Check conflication. 468 // 1. Wait for the related txn if need. 469 // 2. w-w conflict when: there's an active txn; or 470 // he CommitTS of the latest related txn is larger than StartTS of write txn 471 // 472 // 2.2.2 Check duplicate/not found. 473 // If the entry hasn't been dropped, return ErrDuplicate. 474 func (e *DBEntry) AddEntryLocked(table *TableEntry, txn txnif.TxnReader, skipDedup bool) (err error) { 475 fullName := table.GetFullName() 476 nn := e.nameNodes[fullName] 477 if nn == nil { 478 n := e.link.Insert(table) 479 e.entries[table.ID] = n 480 481 nn := newNodeList(e.GetItemNodeByIDLocked, 482 tableVisibilityFn[*TableEntry], 483 &e.nodesMu, 484 fullName) 485 e.nameNodes[fullName] = nn 486 487 nn.CreateNode(table.ID) 488 } else { 489 if !skipDedup { 490 name := table.GetLastestSchemaLocked().Name 491 err = e.checkAddNameConflictLocked(name, table.ID, nn, txn) 492 if err != nil { 493 return 494 } 495 } 496 n := e.link.Insert(table) 497 e.entries[table.ID] = n 498 nn.CreateNode(table.ID) 499 } 500 return 501 } 502 503 func (e *DBEntry) checkAddNameConflictLocked(name string, tid uint64, nn *nodeList[*TableEntry], txn txnif.TxnReader) (err error) { 504 if nn == nil { 505 return nil 506 } 507 node := nn.GetNode() 508 if node == nil { 509 return nil 510 } 511 // check ww conflict 512 tbl := nn.GetNode().GetPayload() 513 // skip the same table entry 514 if tbl.ID == tid { 515 return nil 516 } 517 if err = tbl.ConflictCheck(txn); err != nil { 518 return 519 } 520 // check name dup 521 if txn == nil { 522 // replay checkpoint 523 return nil 524 } 525 if existEntry, _ := nn.TxnGetNodeLocked(txn, name); existEntry != nil { 526 return moerr.GetOkExpectedDup() 527 } 528 return nil 529 } 530 531 func (e *DBEntry) MakeCommand(id uint32) (txnif.TxnCmd, error) { 532 cmdType := IOET_WALTxnCommand_Database 533 e.RLock() 534 defer e.RUnlock() 535 return newDBCmd(id, cmdType, e), nil 536 } 537 538 func (e *DBEntry) Set1PC() { 539 e.GetLatestNodeLocked().Set1PC() 540 } 541 func (e *DBEntry) Is1PC() bool { 542 return e.GetLatestNodeLocked().Is1PC() 543 } 544 func (e *DBEntry) GetCatalog() *Catalog { return e.catalog } 545 546 func (e *DBEntry) RecurLoop(processor Processor) (err error) { 547 tableIt := e.MakeTableIt(true) 548 for tableIt.Valid() { 549 table := tableIt.Get().GetPayload() 550 if err = processor.OnTable(table); err != nil { 551 if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) { 552 err = nil 553 tableIt.Next() 554 continue 555 } 556 break 557 } 558 if err = table.RecurLoop(processor); err != nil { 559 return 560 } 561 if err = processor.OnPostTable(table); err != nil { 562 break 563 } 564 tableIt.Next() 565 } 566 if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) { 567 err = nil 568 } 569 return err 570 } 571 572 func (e *DBEntry) PrepareRollback() (err error) { 573 var isEmpty bool 574 if isEmpty, err = e.BaseEntryImpl.PrepareRollback(); err != nil { 575 return 576 } 577 if isEmpty { 578 if err = e.catalog.RemoveDBEntry(e); err != nil { 579 return 580 } 581 } 582 return 583 } 584 585 // IsActive is coarse API: no consistency check 586 func (e *DBEntry) IsActive() bool { 587 return !e.HasDropCommitted() 588 } 589 590 // only for test 591 func MockDBEntryWithAccInfo(accId uint64, dbId uint64) *DBEntry { 592 entry := &DBEntry{ 593 ID: dbId, 594 } 595 596 entry.DBNode = &DBNode{} 597 entry.DBNode.acInfo.TenantID = uint32(accId) 598 599 return entry 600 } 601 602 func (e *DBEntry) GetBlockEntryByID(id *common.ID) (obj *ObjectEntry, err error) { 603 e.RLock() 604 table, err := e.GetTableEntryByID(id.TableID) 605 e.RUnlock() 606 if err != nil { 607 return 608 } 609 obj, err = table.GetObjectByID(id.ObjectID()) 610 return 611 }