github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/catalog/catalog.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  	"context"
    20  	"fmt"
    21  	"sync"
    22  	"sync/atomic"
    23  
    24  	// "time"
    25  
    26  	pkgcatalog "github.com/matrixorigin/matrixone/pkg/catalog"
    27  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    28  	"github.com/matrixorigin/matrixone/pkg/container/types"
    29  	"github.com/matrixorigin/matrixone/pkg/logutil"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    31  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/containers"
    32  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    33  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks"
    34  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase"
    35  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/wal"
    36  )
    37  
    38  // +--------+---------+----------+----------+------------+
    39  // |   ID   |  Name   | CreateAt | DeleteAt | CommitInfo |
    40  // +--------+---------+----------+----------+------------+
    41  // |(uint64)|(varchar)| (uint64) | (uint64) |  (varchar) |
    42  // +--------+---------+----------+----------+------------+
    43  const (
    44  	SnapshotAttr_SegID           = "segment_id"
    45  	SnapshotAttr_TID             = "table_id"
    46  	SnapshotAttr_DBID            = "db_id"
    47  	SegmentAttr_ID               = "id"
    48  	SegmentAttr_CreateAt         = "create_at"
    49  	SegmentAttr_State            = "state"
    50  	SegmentAttr_Sorted           = "sorted"
    51  	SnapshotAttr_BlockMaxRow     = "block_max_row"
    52  	SnapshotAttr_SegmentMaxBlock = "segment_max_block"
    53  )
    54  
    55  type DataFactory interface {
    56  	MakeTableFactory() TableDataFactory
    57  	MakeSegmentFactory() SegmentDataFactory
    58  	MakeBlockFactory() BlockDataFactory
    59  }
    60  
    61  func rowIDToU64(rowID types.Rowid) uint64 {
    62  	return types.DecodeUint64(rowID[:8])
    63  }
    64  
    65  type Catalog struct {
    66  	*IDAlloctor
    67  	*sync.RWMutex
    68  
    69  	scheduler tasks.TaskScheduler
    70  
    71  	entries   map[uint64]*common.GenericDLNode[*DBEntry]
    72  	nameNodes map[string]*nodeList[*DBEntry]
    73  	link      *common.GenericSortedDList[*DBEntry]
    74  
    75  	nodesMu sync.RWMutex
    76  
    77  	tableCnt  atomic.Int32
    78  	columnCnt atomic.Int32
    79  }
    80  
    81  func genDBFullName(tenantID uint32, name string) string {
    82  	if name == pkgcatalog.MO_CATALOG {
    83  		tenantID = 0
    84  	}
    85  	return fmt.Sprintf("%d-%s", tenantID, name)
    86  }
    87  
    88  func compareDBFn(a, b *DBEntry) int {
    89  	return a.DBBaseEntry.DoCompre(b.DBBaseEntry)
    90  }
    91  
    92  func MockCatalog(scheduler tasks.TaskScheduler) *Catalog {
    93  	catalog := &Catalog{
    94  		RWMutex:    new(sync.RWMutex),
    95  		IDAlloctor: NewIDAllocator(),
    96  		entries:    make(map[uint64]*common.GenericDLNode[*DBEntry]),
    97  		nameNodes:  make(map[string]*nodeList[*DBEntry]),
    98  		link:       common.NewGenericSortedDList(compareDBFn),
    99  		scheduler:  scheduler,
   100  	}
   101  	catalog.InitSystemDB()
   102  	return catalog
   103  }
   104  
   105  func NewEmptyCatalog() *Catalog {
   106  	return &Catalog{
   107  		RWMutex:    new(sync.RWMutex),
   108  		IDAlloctor: NewIDAllocator(),
   109  		entries:    make(map[uint64]*common.GenericDLNode[*DBEntry]),
   110  		nameNodes:  make(map[string]*nodeList[*DBEntry]),
   111  		link:       common.NewGenericSortedDList(compareDBFn),
   112  		scheduler:  nil,
   113  	}
   114  }
   115  
   116  func OpenCatalog(scheduler tasks.TaskScheduler, dataFactory DataFactory) (*Catalog, error) {
   117  	catalog := &Catalog{
   118  		RWMutex:    new(sync.RWMutex),
   119  		IDAlloctor: NewIDAllocator(),
   120  		entries:    make(map[uint64]*common.GenericDLNode[*DBEntry]),
   121  		nameNodes:  make(map[string]*nodeList[*DBEntry]),
   122  		link:       common.NewGenericSortedDList(compareDBFn),
   123  		scheduler:  scheduler,
   124  	}
   125  	catalog.InitSystemDB()
   126  	return catalog, nil
   127  }
   128  
   129  func (catalog *Catalog) InitSystemDB() {
   130  	sysDB := NewSystemDBEntry(catalog)
   131  	dbTables := NewSystemTableEntry(sysDB, pkgcatalog.MO_DATABASE_ID, SystemDBSchema)
   132  	tableTables := NewSystemTableEntry(sysDB, pkgcatalog.MO_TABLES_ID, SystemTableSchema)
   133  	columnTables := NewSystemTableEntry(sysDB, pkgcatalog.MO_COLUMNS_ID, SystemColumnSchema)
   134  	err := sysDB.AddEntryLocked(dbTables, nil, false)
   135  	if err != nil {
   136  		panic(err)
   137  	}
   138  	if err = sysDB.AddEntryLocked(tableTables, nil, false); err != nil {
   139  		panic(err)
   140  	}
   141  	if err = sysDB.AddEntryLocked(columnTables, nil, false); err != nil {
   142  		panic(err)
   143  	}
   144  	if err = catalog.AddEntryLocked(sysDB, nil, false); err != nil {
   145  		panic(err)
   146  	}
   147  }
   148  func (catalog *Catalog) GCByTS(ctx context.Context, ts types.TS) {
   149  	logutil.Infof("GC Catalog %v", ts.ToString())
   150  	processor := LoopProcessor{}
   151  	processor.DatabaseFn = func(d *DBEntry) error {
   152  		d.RLock()
   153  		needGC := d.DeleteBefore(ts)
   154  		d.RUnlock()
   155  		if needGC {
   156  			catalog.RemoveEntry(d)
   157  		}
   158  		return nil
   159  	}
   160  	processor.TableFn = func(te *TableEntry) error {
   161  		te.RLock()
   162  		needGC := te.DeleteBefore(ts)
   163  		te.RUnlock()
   164  		if needGC {
   165  			db := te.db
   166  			db.RemoveEntry(te)
   167  		}
   168  		return nil
   169  	}
   170  	processor.SegmentFn = func(se *SegmentEntry) error {
   171  		se.RLock()
   172  		needGC := se.DeleteBefore(ts)
   173  		se.RUnlock()
   174  		if needGC {
   175  			tbl := se.table
   176  			tbl.RemoveEntry(se)
   177  		}
   178  		return nil
   179  	}
   180  	processor.BlockFn = func(be *BlockEntry) error {
   181  		be.RLock()
   182  		needGC := be.DeleteBefore(ts)
   183  		be.RUnlock()
   184  		if needGC {
   185  			seg := be.segment
   186  			seg.RemoveEntry(be)
   187  		}
   188  		return nil
   189  	}
   190  	err := catalog.RecurLoop(&processor)
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  }
   195  func (catalog *Catalog) ReplayCmd(
   196  	txncmd txnif.TxnCmd,
   197  	dataFactory DataFactory,
   198  	idxCtx *wal.Index,
   199  	observer wal.ReplayObserver) {
   200  	switch txncmd.GetType() {
   201  	case txnbase.CmdComposed:
   202  		cmds := txncmd.(*txnbase.ComposedCmd)
   203  		idxCtx.Size = cmds.CmdSize
   204  		for i, cmds := range cmds.Cmds {
   205  			idx := idxCtx.Clone()
   206  			idx.CSN = uint32(i)
   207  			catalog.ReplayCmd(cmds, dataFactory, idx, observer)
   208  		}
   209  	case CmdUpdateDatabase:
   210  		cmd := txncmd.(*EntryCommand)
   211  		catalog.onReplayUpdateDatabase(cmd, idxCtx, observer)
   212  	case CmdUpdateTable:
   213  		cmd := txncmd.(*EntryCommand)
   214  		catalog.onReplayUpdateTable(cmd, dataFactory, idxCtx, observer)
   215  	case CmdUpdateSegment:
   216  		cmd := txncmd.(*EntryCommand)
   217  		catalog.onReplayUpdateSegment(cmd, dataFactory, idxCtx, observer)
   218  	case CmdUpdateBlock:
   219  		cmd := txncmd.(*EntryCommand)
   220  		catalog.onReplayUpdateBlock(cmd, dataFactory, idxCtx, observer)
   221  	default:
   222  		panic("unsupport")
   223  	}
   224  }
   225  
   226  func (catalog *Catalog) onReplayUpdateDatabase(cmd *EntryCommand, idx *wal.Index, observer wal.ReplayObserver) {
   227  	catalog.OnReplayDBID(cmd.DB.ID)
   228  	var err error
   229  	un := cmd.entry.GetLatestNodeLocked().(*DBMVCCNode)
   230  	un.SetLogIndex(idx)
   231  	if un.Is1PC() {
   232  		if err := un.ApplyCommit(nil); err != nil {
   233  			panic(err)
   234  		}
   235  	}
   236  
   237  	db, err := catalog.GetDatabaseByID(cmd.entry.GetID())
   238  	if err != nil {
   239  		cmd.DB.RWMutex = new(sync.RWMutex)
   240  		cmd.DB.catalog = catalog
   241  		cmd.entry.GetLatestNodeLocked().SetLogIndex(idx)
   242  		err = catalog.AddEntryLocked(cmd.DB, un.GetTxn(), false)
   243  		if err != nil {
   244  			panic(err)
   245  		}
   246  		return
   247  	}
   248  
   249  	dbun := db.SearchNode(un)
   250  	if dbun == nil {
   251  		db.Insert(un)
   252  	} else {
   253  		return
   254  		// panic(fmt.Sprintf("logic err: duplicate node %v and %v", dbun.String(), un.String()))
   255  	}
   256  }
   257  
   258  func (catalog *Catalog) OnReplayDatabaseBatch(ins, insTxn, del, delTxn *containers.Batch) {
   259  	for i := 0; i < ins.Length(); i++ {
   260  		dbid := ins.GetVectorByName(pkgcatalog.SystemDBAttr_ID).Get(i).(uint64)
   261  		name := string(ins.GetVectorByName(pkgcatalog.SystemDBAttr_Name).Get(i).([]byte))
   262  		txnNode := txnbase.ReadTuple(insTxn, i)
   263  		tenantID := ins.GetVectorByName(pkgcatalog.SystemDBAttr_AccID).Get(i).(uint32)
   264  		userID := ins.GetVectorByName(pkgcatalog.SystemDBAttr_Creator).Get(i).(uint32)
   265  		roleID := ins.GetVectorByName(pkgcatalog.SystemDBAttr_Owner).Get(i).(uint32)
   266  		createAt := ins.GetVectorByName(pkgcatalog.SystemDBAttr_CreateAt).Get(i).(types.Timestamp)
   267  		catalog.onReplayCreateDB(dbid, name, txnNode, tenantID, userID, roleID, createAt)
   268  	}
   269  	for i := 0; i < del.Length(); i++ {
   270  		dbid := delTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   271  		txnNode := txnbase.ReadTuple(delTxn, i)
   272  		catalog.onReplayDeleteDB(dbid, txnNode)
   273  	}
   274  }
   275  
   276  func (catalog *Catalog) onReplayCreateDB(dbid uint64, name string, txnNode *txnbase.TxnMVCCNode, tenantID, userID, roleID uint32, createAt types.Timestamp) {
   277  	catalog.OnReplayDBID(dbid)
   278  	db, _ := catalog.GetDatabaseByID(dbid)
   279  	if db != nil {
   280  		dbCreatedAt := db.GetCreatedAt()
   281  		if !dbCreatedAt.Equal(txnNode.End) {
   282  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), dbCreatedAt.ToString()))
   283  		}
   284  		return
   285  	}
   286  	db = NewReplayDBEntry()
   287  	db.catalog = catalog
   288  	db.ID = dbid
   289  	db.name = name
   290  	db.acInfo = accessInfo{
   291  		TenantID: tenantID,
   292  		UserID:   userID,
   293  		RoleID:   roleID,
   294  		CreateAt: createAt,
   295  	}
   296  	_ = catalog.AddEntryLocked(db, nil, true)
   297  	un := &DBMVCCNode{
   298  		EntryMVCCNode: &EntryMVCCNode{
   299  			CreatedAt: txnNode.End,
   300  		},
   301  		TxnMVCCNode: txnNode,
   302  	}
   303  	db.Insert(un)
   304  }
   305  func (catalog *Catalog) onReplayDeleteDB(dbid uint64, txnNode *txnbase.TxnMVCCNode) {
   306  	catalog.OnReplayDBID(dbid)
   307  	db, err := catalog.GetDatabaseByID(dbid)
   308  	if err != nil {
   309  		logutil.Infof("delete %d", dbid)
   310  		logutil.Info(catalog.SimplePPString(common.PPL3))
   311  		panic(err)
   312  	}
   313  	dbDeleteAt := db.GetDeleteAt()
   314  	if !dbDeleteAt.IsEmpty() {
   315  		if !dbDeleteAt.Equal(txnNode.End) {
   316  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), dbDeleteAt.ToString()))
   317  		}
   318  		return
   319  	}
   320  	un := &DBMVCCNode{
   321  		EntryMVCCNode: &EntryMVCCNode{
   322  			CreatedAt: db.GetCreatedAt(),
   323  			DeletedAt: txnNode.End,
   324  		},
   325  		TxnMVCCNode: txnNode,
   326  	}
   327  	db.Insert(un)
   328  }
   329  func (catalog *Catalog) onReplayUpdateTable(cmd *EntryCommand, dataFactory DataFactory, idx *wal.Index, observer wal.ReplayObserver) {
   330  	catalog.OnReplayTableID(cmd.Table.ID)
   331  	// prepareTS := cmd.GetTs()
   332  	// if prepareTS.LessEq(catalog.GetCheckpointed().MaxTS) {
   333  	// 	if observer != nil {
   334  	// 		observer.OnStaleIndex(idx)
   335  	// 	}
   336  	// 	return
   337  	// }
   338  	db, err := catalog.GetDatabaseByID(cmd.DBID)
   339  	if err != nil {
   340  		panic(err)
   341  	}
   342  	tbl, err := db.GetTableEntryByID(cmd.Table.ID)
   343  
   344  	un := cmd.entry.GetLatestNodeLocked().(*TableMVCCNode)
   345  	un.SetLogIndex(idx)
   346  	if un.Is1PC() {
   347  		if err := un.ApplyCommit(nil); err != nil {
   348  			panic(err)
   349  		}
   350  	}
   351  
   352  	if err != nil {
   353  		cmd.Table.db = db
   354  		cmd.Table.tableData = dataFactory.MakeTableFactory()(cmd.Table)
   355  		err = db.AddEntryLocked(cmd.Table, un.GetTxn(), false)
   356  		if err != nil {
   357  			logutil.Warn(catalog.SimplePPString(common.PPL3))
   358  			panic(err)
   359  		}
   360  		return
   361  	}
   362  	tblun := tbl.SearchNode(un)
   363  	if tblun == nil {
   364  		tbl.Insert(un) //TODO isvalid
   365  	}
   366  
   367  }
   368  
   369  func (catalog *Catalog) OnReplayTableBatch(ins, insTxn, insCol, del, delTxn *containers.Batch, dataFactory DataFactory) {
   370  	schemaOffset := 0
   371  	for i := 0; i < ins.Length(); i++ {
   372  		tid := ins.GetVectorByName(pkgcatalog.SystemRelAttr_ID).Get(i).(uint64)
   373  		dbid := ins.GetVectorByName(pkgcatalog.SystemRelAttr_DBID).Get(i).(uint64)
   374  		name := string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_Name).Get(i).([]byte))
   375  		schema := NewEmptySchema(name)
   376  		schemaOffset = schema.ReadFromBatch(insCol, schemaOffset, tid)
   377  		schema.Comment = string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_Comment).Get(i).([]byte))
   378  		schema.Partition = string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_Partition).Get(i).([]byte))
   379  		schema.Relkind = string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_Kind).Get(i).([]byte))
   380  		schema.Createsql = string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_CreateSQL).Get(i).([]byte))
   381  		schema.View = string(ins.GetVectorByName(pkgcatalog.SystemRelAttr_ViewDef).Get(i).([]byte))
   382  		schema.Constraint = ins.GetVectorByName(pkgcatalog.SystemRelAttr_Constraint).Get(i).([]byte)
   383  		schema.AcInfo = accessInfo{}
   384  		schema.AcInfo.RoleID = ins.GetVectorByName(pkgcatalog.SystemRelAttr_Owner).Get(i).(uint32)
   385  		schema.AcInfo.UserID = ins.GetVectorByName(pkgcatalog.SystemRelAttr_Creator).Get(i).(uint32)
   386  		schema.AcInfo.CreateAt = ins.GetVectorByName(pkgcatalog.SystemRelAttr_CreateAt).Get(i).(types.Timestamp)
   387  		schema.AcInfo.TenantID = ins.GetVectorByName(pkgcatalog.SystemRelAttr_AccID).Get(i).(uint32)
   388  		schema.BlockMaxRows = insTxn.GetVectorByName(SnapshotAttr_BlockMaxRow).Get(i).(uint32)
   389  		schema.SegmentMaxBlocks = insTxn.GetVectorByName(SnapshotAttr_SegmentMaxBlock).Get(i).(uint16)
   390  		txnNode := txnbase.ReadTuple(insTxn, i)
   391  		catalog.onReplayCreateTable(dbid, tid, schema, txnNode, dataFactory)
   392  	}
   393  	for i := 0; i < del.Length(); i++ {
   394  		dbid := delTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   395  		tid := delTxn.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64)
   396  		txnNode := txnbase.ReadTuple(delTxn, i)
   397  		catalog.onReplayDeleteTable(dbid, tid, txnNode)
   398  	}
   399  }
   400  
   401  func (catalog *Catalog) onReplayCreateTable(dbid, tid uint64, schema *Schema, txnNode *txnbase.TxnMVCCNode, dataFactory DataFactory) {
   402  	catalog.OnReplayTableID(tid)
   403  	db, err := catalog.GetDatabaseByID(dbid)
   404  	if err != nil {
   405  		logutil.Info(catalog.SimplePPString(common.PPL3))
   406  		panic(err)
   407  	}
   408  	tbl, _ := db.GetTableEntryByID(tid)
   409  	if tbl != nil {
   410  		tblCreatedAt := tbl.GetCreatedAt()
   411  		if tblCreatedAt.Greater(txnNode.End) {
   412  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), tblCreatedAt.ToString()))
   413  		}
   414  		// update constraint
   415  		un := &TableMVCCNode{
   416  			EntryMVCCNode: &EntryMVCCNode{
   417  				CreatedAt: tblCreatedAt,
   418  			},
   419  			TxnMVCCNode:       txnNode,
   420  			SchemaConstraints: string(schema.Constraint),
   421  		}
   422  		tbl.Insert(un)
   423  
   424  		return
   425  	}
   426  	tbl = NewReplayTableEntry()
   427  	tbl.schema = schema
   428  	tbl.db = db
   429  	tbl.ID = tid
   430  	tbl.tableData = dataFactory.MakeTableFactory()(tbl)
   431  	_ = db.AddEntryLocked(tbl, nil, true)
   432  	un := &TableMVCCNode{
   433  		EntryMVCCNode: &EntryMVCCNode{
   434  			CreatedAt: txnNode.End,
   435  		},
   436  		TxnMVCCNode:       txnNode,
   437  		SchemaConstraints: string(schema.Constraint),
   438  	}
   439  	tbl.Insert(un)
   440  }
   441  func (catalog *Catalog) onReplayDeleteTable(dbid, tid uint64, txnNode *txnbase.TxnMVCCNode) {
   442  	catalog.OnReplayTableID(tid)
   443  	db, err := catalog.GetDatabaseByID(dbid)
   444  	if err != nil {
   445  		logutil.Info(catalog.SimplePPString(common.PPL3))
   446  		panic(err)
   447  	}
   448  	tbl, err := db.GetTableEntryByID(tid)
   449  	if err != nil {
   450  		logutil.Info(catalog.SimplePPString(common.PPL3))
   451  		panic(err)
   452  	}
   453  	tableDeleteAt := tbl.GetDeleteAt()
   454  	if !tableDeleteAt.IsEmpty() {
   455  		if !tableDeleteAt.Equal(txnNode.End) {
   456  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), tableDeleteAt.ToString()))
   457  		}
   458  		return
   459  	}
   460  	prev := tbl.MVCCChain.GetLatestCommittedNode().(*TableMVCCNode)
   461  	un := &TableMVCCNode{
   462  		EntryMVCCNode: &EntryMVCCNode{
   463  			CreatedAt: prev.CreatedAt,
   464  			DeletedAt: txnNode.End,
   465  		},
   466  		TxnMVCCNode: txnNode,
   467  	}
   468  	tbl.Insert(un)
   469  
   470  }
   471  func (catalog *Catalog) onReplayUpdateSegment(
   472  	cmd *EntryCommand,
   473  	dataFactory DataFactory,
   474  	idx *wal.Index,
   475  	observer wal.ReplayObserver) {
   476  	catalog.OnReplaySegmentID(cmd.Segment.ID)
   477  
   478  	un := cmd.entry.GetLatestNodeLocked().(*MetadataMVCCNode)
   479  	un.SetLogIndex(idx)
   480  	if un.Is1PC() {
   481  		if err := un.ApplyCommit(nil); err != nil {
   482  			panic(err)
   483  		}
   484  	}
   485  	db, err := catalog.GetDatabaseByID(cmd.DBID)
   486  	if err != nil {
   487  		panic(err)
   488  	}
   489  	tbl, err := db.GetTableEntryByID(cmd.TableID)
   490  	if err != nil {
   491  		logutil.Infof("tbl %d-%d", cmd.DBID, cmd.TableID)
   492  		logutil.Info(catalog.SimplePPString(3))
   493  		panic(err)
   494  	}
   495  	seg, err := tbl.GetSegmentByID(cmd.Segment.ID)
   496  	if err != nil {
   497  		cmd.Segment.table = tbl
   498  		cmd.Segment.RWMutex = new(sync.RWMutex)
   499  		cmd.Segment.segData = dataFactory.MakeSegmentFactory()(cmd.Segment)
   500  		tbl.AddEntryLocked(cmd.Segment)
   501  	} else {
   502  		node := seg.SearchNode(un)
   503  		if node == nil {
   504  			seg.Insert(un)
   505  		} else {
   506  			node.Update(un)
   507  		}
   508  	}
   509  }
   510  
   511  func (catalog *Catalog) OnReplaySegmentBatch(ins, insTxn, del, delTxn *containers.Batch, dataFactory DataFactory) {
   512  	idVec := ins.GetVectorByName(SegmentAttr_ID)
   513  	for i := 0; i < idVec.Length(); i++ {
   514  		dbid := insTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   515  		tid := insTxn.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64)
   516  		appendable := ins.GetVectorByName(SegmentAttr_State).Get(i).(bool)
   517  		state := ES_NotAppendable
   518  		if appendable {
   519  			state = ES_Appendable
   520  		}
   521  		sorted := ins.GetVectorByName(SegmentAttr_Sorted).Get(i).(bool)
   522  		sid := ins.GetVectorByName(SegmentAttr_ID).Get(i).(uint64)
   523  		txnNode := txnbase.ReadTuple(insTxn, i)
   524  		catalog.onReplayCreateSegment(dbid, tid, sid, state, sorted, txnNode, dataFactory)
   525  	}
   526  	idVec = delTxn.GetVectorByName(SnapshotAttr_DBID)
   527  	for i := 0; i < idVec.Length(); i++ {
   528  		dbid := delTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   529  		tid := delTxn.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64)
   530  		sid := del.GetVectorByName(AttrRowID).Get(i).(types.Rowid)
   531  		txnNode := txnbase.ReadTuple(delTxn, i)
   532  		catalog.onReplayDeleteSegment(dbid, tid, rowIDToU64(sid), txnNode)
   533  	}
   534  }
   535  func (catalog *Catalog) onReplayCreateSegment(dbid, tbid, segid uint64, state EntryState, sorted bool, txnNode *txnbase.TxnMVCCNode, dataFactory DataFactory) {
   536  	catalog.OnReplaySegmentID(segid)
   537  	db, err := catalog.GetDatabaseByID(dbid)
   538  	if err != nil {
   539  		logutil.Info(catalog.SimplePPString(common.PPL3))
   540  		panic(err)
   541  	}
   542  	rel, err := db.GetTableEntryByID(tbid)
   543  	if err != nil {
   544  		logutil.Info(catalog.SimplePPString(common.PPL3))
   545  		panic(err)
   546  	}
   547  	seg, _ := rel.GetSegmentByID(segid)
   548  	if seg != nil {
   549  		segCreatedAt := seg.GetCreatedAt()
   550  		if !segCreatedAt.Equal(txnNode.End) {
   551  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), segCreatedAt.ToString()))
   552  		}
   553  		return
   554  	}
   555  	seg = NewReplaySegmentEntry()
   556  	seg.table = rel
   557  	seg.ID = segid
   558  	seg.state = state
   559  	seg.sorted = sorted
   560  	seg.segData = dataFactory.MakeSegmentFactory()(seg)
   561  	rel.AddEntryLocked(seg)
   562  	un := &MetadataMVCCNode{
   563  		EntryMVCCNode: &EntryMVCCNode{
   564  			CreatedAt: txnNode.End,
   565  		},
   566  		TxnMVCCNode: txnNode,
   567  	}
   568  	seg.Insert(un)
   569  }
   570  func (catalog *Catalog) onReplayDeleteSegment(dbid, tbid, segid uint64, txnNode *txnbase.TxnMVCCNode) {
   571  	catalog.OnReplaySegmentID(segid)
   572  	db, err := catalog.GetDatabaseByID(dbid)
   573  	if err != nil {
   574  		logutil.Info(catalog.SimplePPString(common.PPL3))
   575  		panic(err)
   576  	}
   577  	rel, err := db.GetTableEntryByID(tbid)
   578  	if err != nil {
   579  		logutil.Info(catalog.SimplePPString(common.PPL3))
   580  		panic(err)
   581  	}
   582  	seg, err := rel.GetSegmentByID(segid)
   583  	if err != nil {
   584  		logutil.Info(catalog.SimplePPString(common.PPL3))
   585  		panic(err)
   586  	}
   587  	segDeleteAt := seg.GetDeleteAt()
   588  	if !segDeleteAt.IsEmpty() {
   589  		if !segDeleteAt.Equal(txnNode.End) {
   590  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), segDeleteAt.ToString()))
   591  		}
   592  		return
   593  	}
   594  	prevUn := seg.MVCCChain.GetLatestNodeLocked().(*MetadataMVCCNode)
   595  	un := &MetadataMVCCNode{
   596  		EntryMVCCNode: &EntryMVCCNode{
   597  			CreatedAt: prevUn.CreatedAt,
   598  			DeletedAt: txnNode.End,
   599  		},
   600  		TxnMVCCNode: txnNode,
   601  	}
   602  	seg.Insert(un)
   603  }
   604  func (catalog *Catalog) onReplayUpdateBlock(cmd *EntryCommand,
   605  	dataFactory DataFactory,
   606  	idx *wal.Index,
   607  	observer wal.ReplayObserver) {
   608  	catalog.OnReplayBlockID(cmd.Block.ID)
   609  	prepareTS := cmd.GetTs()
   610  	db, err := catalog.GetDatabaseByID(cmd.DBID)
   611  	if err != nil {
   612  		panic(err)
   613  	}
   614  	tbl, err := db.GetTableEntryByID(cmd.TableID)
   615  	if err != nil {
   616  		panic(err)
   617  	}
   618  	seg, err := tbl.GetSegmentByID(cmd.SegmentID)
   619  	if err != nil {
   620  		panic(err)
   621  	}
   622  	blk, err := seg.GetBlockEntryByID(cmd.Block.ID)
   623  	un := cmd.entry.GetLatestNodeLocked().(*MetadataMVCCNode)
   624  	un.SetLogIndex(idx)
   625  	if un.Is1PC() {
   626  		if err := un.ApplyCommit(nil); err != nil {
   627  			panic(err)
   628  		}
   629  	}
   630  	if err == nil {
   631  		blkun := blk.SearchNode(un)
   632  		if blkun != nil {
   633  			blkun.Update(un)
   634  		} else {
   635  			blk.Insert(un)
   636  		}
   637  		return
   638  	}
   639  	cmd.Block.RWMutex = new(sync.RWMutex)
   640  	cmd.Block.segment = seg
   641  	cmd.Block.blkData = dataFactory.MakeBlockFactory()(cmd.Block)
   642  	if observer != nil {
   643  		observer.OnTimeStamp(prepareTS)
   644  	}
   645  	seg.AddEntryLocked(cmd.Block)
   646  }
   647  
   648  func (catalog *Catalog) OnReplayBlockBatch(ins, insTxn, del, delTxn *containers.Batch, dataFactory DataFactory) {
   649  	for i := 0; i < ins.Length(); i++ {
   650  		dbid := insTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   651  		tid := insTxn.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64)
   652  		sid := insTxn.GetVectorByName(SnapshotAttr_SegID).Get(i).(uint64)
   653  		appendable := ins.GetVectorByName(pkgcatalog.BlockMeta_EntryState).Get(i).(bool)
   654  		state := ES_NotAppendable
   655  		if appendable {
   656  			state = ES_Appendable
   657  		}
   658  		blkID := ins.GetVectorByName(pkgcatalog.BlockMeta_ID).Get(i).(uint64)
   659  		metaLoc := string(ins.GetVectorByName(pkgcatalog.BlockMeta_MetaLoc).Get(i).([]byte))
   660  		deltaLoc := string(ins.GetVectorByName(pkgcatalog.BlockMeta_DeltaLoc).Get(i).([]byte))
   661  		txnNode := txnbase.ReadTuple(insTxn, i)
   662  		catalog.onReplayCreateBlock(dbid, tid, sid, blkID, state, metaLoc, deltaLoc, txnNode, dataFactory)
   663  	}
   664  	for i := 0; i < del.Length(); i++ {
   665  		dbid := delTxn.GetVectorByName(SnapshotAttr_DBID).Get(i).(uint64)
   666  		tid := delTxn.GetVectorByName(SnapshotAttr_TID).Get(i).(uint64)
   667  		sid := delTxn.GetVectorByName(SnapshotAttr_SegID).Get(i).(uint64)
   668  		blkID := del.GetVectorByName(AttrRowID).Get(i).(types.Rowid)
   669  		un := txnbase.ReadTuple(delTxn, i)
   670  		metaLoc := string(delTxn.GetVectorByName(pkgcatalog.BlockMeta_MetaLoc).Get(i).([]byte))
   671  		deltaLoc := string(delTxn.GetVectorByName(pkgcatalog.BlockMeta_DeltaLoc).Get(i).([]byte))
   672  		catalog.onReplayDeleteBlock(dbid, tid, sid, rowIDToU64(blkID), metaLoc, deltaLoc, un)
   673  	}
   674  }
   675  func (catalog *Catalog) onReplayCreateBlock(
   676  	dbid, tid, segid, blkid uint64,
   677  	state EntryState,
   678  	metaloc, deltaloc string,
   679  	txnNode *txnbase.TxnMVCCNode,
   680  	dataFactory DataFactory) {
   681  	catalog.OnReplayBlockID(blkid)
   682  	db, err := catalog.GetDatabaseByID(dbid)
   683  	if err != nil {
   684  		logutil.Info(catalog.SimplePPString(common.PPL3))
   685  		panic(err)
   686  	}
   687  	rel, err := db.GetTableEntryByID(tid)
   688  	if err != nil {
   689  		logutil.Info(catalog.SimplePPString(common.PPL3))
   690  		panic(err)
   691  	}
   692  	seg, err := rel.GetSegmentByID(segid)
   693  	if err != nil {
   694  		logutil.Info(catalog.SimplePPString(common.PPL3))
   695  		panic(err)
   696  	}
   697  	blk, _ := seg.GetBlockEntryByID(blkid)
   698  	var un *MetadataMVCCNode
   699  	if blk == nil {
   700  		blk = NewReplayBlockEntry()
   701  		blk.segment = seg
   702  		blk.ID = blkid
   703  		blk.state = state
   704  		blk.blkData = dataFactory.MakeBlockFactory()(blk)
   705  		seg.AddEntryLocked(blk)
   706  		un = &MetadataMVCCNode{
   707  			EntryMVCCNode: &EntryMVCCNode{
   708  				CreatedAt: txnNode.End,
   709  			},
   710  			TxnMVCCNode: txnNode,
   711  			MetaLoc:     metaloc,
   712  			DeltaLoc:    deltaloc,
   713  		}
   714  	} else {
   715  		prevUn := blk.MVCCChain.GetLatestNodeLocked().(*MetadataMVCCNode)
   716  		un = &MetadataMVCCNode{
   717  			EntryMVCCNode: &EntryMVCCNode{
   718  				CreatedAt: prevUn.CreatedAt,
   719  			},
   720  			TxnMVCCNode: txnNode,
   721  			MetaLoc:     metaloc,
   722  			DeltaLoc:    deltaloc,
   723  		}
   724  		node := blk.MVCCChain.SearchNode(un).(*MetadataMVCCNode)
   725  		if node != nil {
   726  			if !node.CreatedAt.Equal(un.CreatedAt) {
   727  				panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", node.CreatedAt.ToString(), un.CreatedAt.ToString()))
   728  			}
   729  			if node.MetaLoc != un.MetaLoc {
   730  				panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", node.MetaLoc, un.MetaLoc))
   731  			}
   732  			if node.DeltaLoc != un.DeltaLoc {
   733  				panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", node.DeltaLoc, un.DeltaLoc))
   734  			}
   735  			return
   736  		}
   737  	}
   738  	blk.Insert(un)
   739  }
   740  func (catalog *Catalog) onReplayDeleteBlock(dbid, tid, segid, blkid uint64, metaloc, deltaloc string, txnNode *txnbase.TxnMVCCNode) {
   741  	catalog.OnReplayBlockID(blkid)
   742  	db, err := catalog.GetDatabaseByID(dbid)
   743  	if err != nil {
   744  		logutil.Info(catalog.SimplePPString(common.PPL3))
   745  		panic(err)
   746  	}
   747  	rel, err := db.GetTableEntryByID(tid)
   748  	if err != nil {
   749  		logutil.Info(catalog.SimplePPString(common.PPL3))
   750  		panic(err)
   751  	}
   752  	seg, err := rel.GetSegmentByID(segid)
   753  	if err != nil {
   754  		logutil.Info(catalog.SimplePPString(common.PPL3))
   755  		panic(err)
   756  	}
   757  	blk, err := seg.GetBlockEntryByID(blkid)
   758  	if err != nil {
   759  		logutil.Info(catalog.SimplePPString(common.PPL3))
   760  		panic(err)
   761  	}
   762  	blkDeleteAt := blk.GetDeleteAt()
   763  	if !blkDeleteAt.IsEmpty() {
   764  		if !blkDeleteAt.Equal(txnNode.End) {
   765  			panic(moerr.NewInternalErrorNoCtx("logic err expect %s, get %s", txnNode.End.ToString(), blkDeleteAt.ToString()))
   766  		}
   767  		return
   768  	}
   769  	prevUn := blk.MVCCChain.GetLatestNodeLocked().(*MetadataMVCCNode)
   770  	un := &MetadataMVCCNode{
   771  		EntryMVCCNode: &EntryMVCCNode{
   772  			CreatedAt: prevUn.CreatedAt,
   773  			DeletedAt: txnNode.End,
   774  		},
   775  		TxnMVCCNode: txnNode,
   776  		MetaLoc:     metaloc,
   777  		DeltaLoc:    deltaloc,
   778  	}
   779  	blk.Insert(un)
   780  }
   781  func (catalog *Catalog) ReplayTableRows() {
   782  	rows := uint64(0)
   783  	tableProcessor := new(LoopProcessor)
   784  	tableProcessor.BlockFn = func(be *BlockEntry) error {
   785  		if !be.IsActive() {
   786  			return nil
   787  		}
   788  		rows += be.GetBlockData().GetRowsOnReplay()
   789  		return nil
   790  	}
   791  	processor := new(LoopProcessor)
   792  	processor.TableFn = func(tbl *TableEntry) error {
   793  		if tbl.db.name == pkgcatalog.MO_CATALOG {
   794  			return nil
   795  		}
   796  		rows = 0
   797  		err := tbl.RecurLoop(tableProcessor)
   798  		if err != nil {
   799  			panic(err)
   800  		}
   801  		tbl.rows.Store(rows)
   802  		return nil
   803  	}
   804  	err := catalog.RecurLoop(processor)
   805  	if err != nil {
   806  		panic(err)
   807  	}
   808  }
   809  func (catalog *Catalog) Close() error {
   810  	return nil
   811  }
   812  
   813  func (catalog *Catalog) CoarseDBCnt() int {
   814  	catalog.RLock()
   815  	defer catalog.RUnlock()
   816  	return len(catalog.entries)
   817  }
   818  
   819  func (catalog *Catalog) CoarseTableCnt() int {
   820  	return int(catalog.tableCnt.Load())
   821  }
   822  
   823  func (catalog *Catalog) CoarseColumnCnt() int {
   824  	return int(catalog.columnCnt.Load())
   825  }
   826  
   827  func (catalog *Catalog) AddTableCnt(cnt int) {
   828  	if catalog.tableCnt.Add(int32(cnt)) < 0 {
   829  		panic("logic error")
   830  	}
   831  }
   832  
   833  func (catalog *Catalog) AddColumnCnt(cnt int) {
   834  	if catalog.columnCnt.Add(int32(cnt)) < 0 {
   835  		panic("logic error")
   836  	}
   837  }
   838  
   839  func (catalog *Catalog) GetItemNodeByIDLocked(id uint64) *common.GenericDLNode[*DBEntry] {
   840  	return catalog.entries[id]
   841  }
   842  
   843  func (catalog *Catalog) GetScheduler() tasks.TaskScheduler { return catalog.scheduler }
   844  func (catalog *Catalog) GetDatabaseByID(id uint64) (db *DBEntry, err error) {
   845  	catalog.RLock()
   846  	defer catalog.RUnlock()
   847  	node := catalog.entries[id]
   848  	if node == nil {
   849  		err = moerr.GetOkExpectedEOB()
   850  		return
   851  	}
   852  	db = node.GetPayload()
   853  	return
   854  }
   855  
   856  func (catalog *Catalog) AddEntryLocked(database *DBEntry, txn txnif.TxnReader, skipDedup bool) error {
   857  	nn := catalog.nameNodes[database.GetFullName()]
   858  	if nn == nil {
   859  		n := catalog.link.Insert(database)
   860  		catalog.entries[database.GetID()] = n
   861  
   862  		nn := newNodeList(catalog.GetItemNodeByIDLocked,
   863  			dbVisibilityFn[*DBEntry],
   864  			&catalog.nodesMu,
   865  			database.name)
   866  		catalog.nameNodes[database.GetFullName()] = nn
   867  
   868  		nn.CreateNode(database.GetID())
   869  	} else {
   870  		node := nn.GetNode()
   871  		if !skipDedup {
   872  			record := node.GetPayload()
   873  			err := record.PrepareAdd(txn)
   874  			if err != nil {
   875  				return err
   876  			}
   877  		}
   878  		n := catalog.link.Insert(database)
   879  		catalog.entries[database.GetID()] = n
   880  		nn.CreateNode(database.GetID())
   881  	}
   882  	return nil
   883  }
   884  
   885  func (catalog *Catalog) MakeDBIt(reverse bool) *common.GenericSortedDListIt[*DBEntry] {
   886  	catalog.RLock()
   887  	defer catalog.RUnlock()
   888  	return common.NewGenericSortedDListIt(catalog.RWMutex, catalog.link, reverse)
   889  }
   890  
   891  func (catalog *Catalog) SimplePPString(level common.PPLevel) string {
   892  	return catalog.PPString(level, 0, "")
   893  }
   894  
   895  func (catalog *Catalog) PPString(level common.PPLevel, depth int, prefix string) string {
   896  	var w bytes.Buffer
   897  	cnt := 0
   898  	it := catalog.MakeDBIt(true)
   899  	for it.Valid() {
   900  		cnt++
   901  		entry := it.Get().GetPayload()
   902  		_ = w.WriteByte('\n')
   903  		_, _ = w.WriteString(entry.PPString(level, depth+1, ""))
   904  		it.Next()
   905  	}
   906  
   907  	var w2 bytes.Buffer
   908  	_, _ = w2.WriteString(fmt.Sprintf("CATALOG[CNT=%d]", cnt))
   909  	_, _ = w2.WriteString(w.String())
   910  	return w2.String()
   911  }
   912  
   913  func (catalog *Catalog) RemoveEntry(database *DBEntry) error {
   914  	if database.IsSystemDB() {
   915  		logutil.Warnf("system db cannot be removed")
   916  		return moerr.NewTAEErrorNoCtx("not permitted")
   917  	}
   918  	logutil.Info("[Catalog]", common.OperationField("remove"),
   919  		common.OperandField(database.String()))
   920  	// database.Close()
   921  	catalog.Lock()
   922  	defer catalog.Unlock()
   923  	if n, ok := catalog.entries[database.GetID()]; !ok {
   924  		return moerr.NewBadDBNoCtx(database.GetName())
   925  	} else {
   926  		nn := catalog.nameNodes[database.GetFullName()]
   927  		nn.DeleteNode(database.GetID())
   928  		catalog.link.Delete(n)
   929  		if nn.Length() == 0 {
   930  			delete(catalog.nameNodes, database.GetFullName())
   931  		}
   932  		delete(catalog.entries, database.GetID())
   933  	}
   934  	return nil
   935  }
   936  
   937  func (catalog *Catalog) txnGetNodeByName(
   938  	tenantID uint32,
   939  	name string,
   940  	ts types.TS) (*common.GenericDLNode[*DBEntry], error) {
   941  	catalog.RLock()
   942  	defer catalog.RUnlock()
   943  	fullName := genDBFullName(tenantID, name)
   944  	node := catalog.nameNodes[fullName]
   945  	if node == nil {
   946  		return nil, moerr.NewBadDBNoCtx(name)
   947  	}
   948  	return node.TxnGetNodeLocked(ts)
   949  }
   950  
   951  func (catalog *Catalog) GetDBEntryByName(
   952  	tenantID uint32,
   953  	name string,
   954  	ts types.TS) (db *DBEntry, err error) {
   955  	n, err := catalog.txnGetNodeByName(tenantID, name, ts)
   956  	if err != nil {
   957  		return
   958  	}
   959  	db = n.GetPayload()
   960  	return
   961  }
   962  
   963  func (catalog *Catalog) TxnGetDBEntryByName(name string, txn txnif.AsyncTxn) (*DBEntry, error) {
   964  	n, err := catalog.txnGetNodeByName(txn.GetTenantID(), name, txn.GetStartTS())
   965  	if err != nil {
   966  		return nil, err
   967  	}
   968  	return n.GetPayload(), nil
   969  }
   970  
   971  func (catalog *Catalog) TxnGetDBEntryByID(id uint64, txn txnif.AsyncTxn) (*DBEntry, error) {
   972  	dbEntry, err := catalog.GetDatabaseByID(id)
   973  	if err != nil {
   974  		return nil, err
   975  	}
   976  	visiable, dropped := dbEntry.GetVisibility(txn.GetStartTS())
   977  	if !visiable || dropped {
   978  		return nil, moerr.GetOkExpectedEOB()
   979  	}
   980  	return dbEntry, nil
   981  }
   982  
   983  func (catalog *Catalog) DropDBEntry(
   984  	name string,
   985  	txn txnif.AsyncTxn) (newEntry bool, deleted *DBEntry, err error) {
   986  	if name == pkgcatalog.MO_CATALOG {
   987  		err = moerr.NewTAEErrorNoCtx("not permitted")
   988  		return
   989  	}
   990  	dn, err := catalog.txnGetNodeByName(txn.GetTenantID(), name, txn.GetStartTS())
   991  	if err != nil {
   992  		return
   993  	}
   994  	entry := dn.GetPayload()
   995  	entry.Lock()
   996  	defer entry.Unlock()
   997  	if newEntry, err = entry.DropEntryLocked(txn); err == nil {
   998  		deleted = entry
   999  	}
  1000  	return
  1001  }
  1002  
  1003  func (catalog *Catalog) DropDBEntryByID(id uint64, txn txnif.AsyncTxn) (newEntry bool, deleted *DBEntry, err error) {
  1004  	if id == pkgcatalog.MO_CATALOG_ID {
  1005  		err = moerr.NewTAEErrorNoCtx("not permitted")
  1006  		return
  1007  	}
  1008  	entry, err := catalog.GetDatabaseByID(id)
  1009  	if err != nil {
  1010  		return
  1011  	}
  1012  	entry.Lock()
  1013  	defer entry.Unlock()
  1014  	if newEntry, err = entry.DropEntryLocked(txn); err == nil {
  1015  		deleted = entry
  1016  	}
  1017  	return
  1018  }
  1019  
  1020  func (catalog *Catalog) CreateDBEntry(name, createSql string, txn txnif.AsyncTxn) (*DBEntry, error) {
  1021  	var err error
  1022  	catalog.Lock()
  1023  	defer catalog.Unlock()
  1024  	entry := NewDBEntry(catalog, name, createSql, txn)
  1025  	err = catalog.AddEntryLocked(entry, txn, false)
  1026  
  1027  	return entry, err
  1028  }
  1029  
  1030  func (catalog *Catalog) CreateDBEntryWithID(name, createSql string, id uint64, txn txnif.AsyncTxn) (*DBEntry, error) {
  1031  	var err error
  1032  	catalog.Lock()
  1033  	defer catalog.Unlock()
  1034  	if _, exist := catalog.entries[id]; exist {
  1035  		return nil, moerr.GetOkExpectedDup()
  1036  	}
  1037  	entry := NewDBEntryWithID(catalog, name, createSql, id, txn)
  1038  	err = catalog.AddEntryLocked(entry, txn, false)
  1039  
  1040  	return entry, err
  1041  }
  1042  
  1043  func (catalog *Catalog) CreateDBEntryByTS(name string, ts types.TS) (*DBEntry, error) {
  1044  	entry := NewDBEntryByTS(catalog, name, ts)
  1045  	err := catalog.AddEntryLocked(entry, nil, false)
  1046  	return entry, err
  1047  }
  1048  
  1049  func (catalog *Catalog) RecurLoop(processor Processor) (err error) {
  1050  	dbIt := catalog.MakeDBIt(true)
  1051  	for dbIt.Valid() {
  1052  		dbEntry := dbIt.Get().GetPayload()
  1053  		if err = processor.OnDatabase(dbEntry); err != nil {
  1054  			// XXX: Performance problem.   Error should not be used
  1055  			// to handle normal return code.
  1056  			if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) {
  1057  				err = nil
  1058  				dbIt.Next()
  1059  				continue
  1060  			}
  1061  			break
  1062  		}
  1063  		if err = dbEntry.RecurLoop(processor); err != nil {
  1064  			return
  1065  		}
  1066  		dbIt.Next()
  1067  	}
  1068  	if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) {
  1069  		err = nil
  1070  	}
  1071  	return err
  1072  }