github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/catalog/table.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  	"math"
    22  	"strings"
    23  	"sync/atomic"
    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/objectio"
    30  	apipb "github.com/matrixorigin/matrixone/pkg/pb/api"
    31  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    32  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/data"
    33  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    34  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/txn/txnbase"
    35  	"github.com/tidwall/btree"
    36  )
    37  
    38  type TableDataFactory = func(meta *TableEntry) data.Table
    39  
    40  func tableVisibilityFn[T *TableEntry](n *common.GenericDLNode[*TableEntry], txn txnif.TxnReader) (visible, dropped bool, name string) {
    41  	table := n.GetPayload()
    42  	visible, dropped, name = table.GetVisibilityAndName(txn)
    43  	return
    44  }
    45  
    46  type TableEntry struct {
    47  	*BaseEntryImpl[*TableMVCCNode]
    48  	*TableNode
    49  	Stats   *common.TableCompactStat
    50  	ID      uint64
    51  	db      *DBEntry
    52  	entries map[types.Objectid]*common.GenericDLNode[*ObjectEntry]
    53  	//link.head and link.tail is nil when create tableEntry object.
    54  	link      *common.GenericSortedDList[*ObjectEntry]
    55  	tableData data.Table
    56  	rows      atomic.Uint64
    57  	// used for the next flush table tail.
    58  	DeletedDirties []*ObjectEntry
    59  	// fullname is format as 'tenantID-tableName', the tenantID prefix is only used 'mo_catalog' database
    60  	fullName string
    61  
    62  	deleteList *btree.BTreeG[DeleteEntry]
    63  }
    64  
    65  type DeleteEntry struct {
    66  	ObjectID objectio.ObjectId
    67  	data.Tombstone
    68  }
    69  
    70  func (d DeleteEntry) Less(o DeleteEntry) bool {
    71  	return bytes.Compare(d.ObjectID[:], o.ObjectID[:]) < 0
    72  }
    73  
    74  func genTblFullName(tenantID uint32, name string) string {
    75  	if name == pkgcatalog.MO_DATABASE || name == pkgcatalog.MO_TABLES || name == pkgcatalog.MO_COLUMNS {
    76  		tenantID = 0
    77  	}
    78  	return fmt.Sprintf("%d-%s", tenantID, name)
    79  }
    80  
    81  func NewTableEntry(db *DBEntry, schema *Schema, txnCtx txnif.AsyncTxn, dataFactory TableDataFactory) *TableEntry {
    82  	id := db.catalog.NextTable()
    83  	return NewTableEntryWithTableId(db, schema, txnCtx, dataFactory, id)
    84  }
    85  
    86  func NewTableEntryWithTableId(db *DBEntry, schema *Schema, txnCtx txnif.AsyncTxn, dataFactory TableDataFactory, tableId uint64) *TableEntry {
    87  	if txnCtx != nil {
    88  		// Only in unit test, txnCtx can be nil
    89  		schema.AcInfo.TenantID = txnCtx.GetTenantID()
    90  		schema.AcInfo.UserID, schema.AcInfo.RoleID = txnCtx.GetUserAndRoleID()
    91  	}
    92  	schema.AcInfo.CreateAt = types.CurrentTimestamp()
    93  	opts := btree.Options{
    94  		Degree: 4,
    95  	}
    96  	e := &TableEntry{
    97  		ID: tableId,
    98  		BaseEntryImpl: NewBaseEntry(
    99  			func() *TableMVCCNode { return &TableMVCCNode{} }),
   100  		db:         db,
   101  		TableNode:  &TableNode{},
   102  		link:       common.NewGenericSortedDList((*ObjectEntry).Less),
   103  		entries:    make(map[types.Objectid]*common.GenericDLNode[*ObjectEntry]),
   104  		deleteList: btree.NewBTreeGOptions(DeleteEntry.Less, opts),
   105  		Stats:      common.NewTableCompactStat(),
   106  	}
   107  	e.TableNode.schema.Store(schema)
   108  	if dataFactory != nil {
   109  		e.tableData = dataFactory(e)
   110  	}
   111  	e.CreateWithTxnAndSchema(txnCtx, schema)
   112  	return e
   113  }
   114  
   115  func NewSystemTableEntry(db *DBEntry, id uint64, schema *Schema) *TableEntry {
   116  	opts := btree.Options{
   117  		Degree: 4,
   118  	}
   119  	e := &TableEntry{
   120  		ID: id,
   121  		BaseEntryImpl: NewBaseEntry(
   122  			func() *TableMVCCNode { return &TableMVCCNode{} }),
   123  		db:         db,
   124  		TableNode:  &TableNode{},
   125  		link:       common.NewGenericSortedDList((*ObjectEntry).Less),
   126  		entries:    make(map[types.Objectid]*common.GenericDLNode[*ObjectEntry]),
   127  		deleteList: btree.NewBTreeGOptions(DeleteEntry.Less, opts),
   128  		Stats:      common.NewTableCompactStat(),
   129  	}
   130  	e.TableNode.schema.Store(schema)
   131  	e.CreateWithTS(types.SystemDBTS, &TableMVCCNode{Schema: schema})
   132  	var sid types.Uuid
   133  	if schema.Name == SystemTableSchema.Name {
   134  		sid = SystemObject_Table_ID
   135  	} else if schema.Name == SystemDBSchema.Name {
   136  		sid = SystemObject_DB_ID
   137  	} else if schema.Name == SystemColumnSchema.Name {
   138  		sid = SystemObject_Columns_ID
   139  	} else {
   140  		panic("not supported")
   141  	}
   142  	objectEntry := NewSysObjectEntry(e, sid)
   143  	e.AddEntryLocked(objectEntry)
   144  	return e
   145  }
   146  
   147  func NewReplayTableEntry() *TableEntry {
   148  	opts := btree.Options{
   149  		Degree: 4,
   150  	}
   151  	e := &TableEntry{
   152  		BaseEntryImpl: NewReplayBaseEntry(
   153  			func() *TableMVCCNode { return &TableMVCCNode{} }),
   154  		link:       common.NewGenericSortedDList((*ObjectEntry).Less),
   155  		entries:    make(map[types.Objectid]*common.GenericDLNode[*ObjectEntry]),
   156  		deleteList: btree.NewBTreeGOptions(DeleteEntry.Less, opts),
   157  		Stats:      common.NewTableCompactStat(),
   158  	}
   159  	return e
   160  }
   161  
   162  func MockStaloneTableEntry(id uint64, schema *Schema) *TableEntry {
   163  	node := &TableNode{}
   164  	node.schema.Store(schema)
   165  	opts := btree.Options{
   166  		Degree: 4,
   167  	}
   168  	return &TableEntry{
   169  		ID: id,
   170  		BaseEntryImpl: NewBaseEntry(
   171  			func() *TableMVCCNode { return &TableMVCCNode{} }),
   172  		TableNode:  node,
   173  		link:       common.NewGenericSortedDList((*ObjectEntry).Less),
   174  		entries:    make(map[types.Objectid]*common.GenericDLNode[*ObjectEntry]),
   175  		deleteList: btree.NewBTreeGOptions(DeleteEntry.Less, opts),
   176  		Stats:      common.NewTableCompactStat(),
   177  	}
   178  }
   179  func (entry *TableEntry) GCTombstone(id objectio.ObjectId) {
   180  	pivot := DeleteEntry{
   181  		ObjectID: id,
   182  	}
   183  	entry.deleteList.Delete(pivot)
   184  }
   185  func (entry *TableEntry) GetDeleteList() *btree.BTreeG[DeleteEntry] {
   186  	return entry.deleteList.Copy()
   187  }
   188  func (entry *TableEntry) TryGetTombstone(oid objectio.ObjectId) data.Tombstone {
   189  	pivot := DeleteEntry{ObjectID: oid}
   190  	tombstone, ok := entry.deleteList.Get(pivot)
   191  	if !ok {
   192  		return nil
   193  	}
   194  	return tombstone.Tombstone
   195  }
   196  
   197  func (entry *TableEntry) GetOrCreateTombstone(obj *ObjectEntry, factory TombstoneFactory) data.Tombstone {
   198  	pivot := DeleteEntry{ObjectID: obj.ID}
   199  	delete, ok := entry.deleteList.Get(pivot)
   200  	if ok {
   201  		return delete.Tombstone
   202  	}
   203  	pivot.Tombstone = factory(obj)
   204  	entry.deleteList.Set(pivot)
   205  	return pivot.Tombstone
   206  }
   207  
   208  func (entry *TableEntry) GetID() uint64 { return entry.ID }
   209  func (entry *TableEntry) IsVirtual() bool {
   210  	if !entry.db.IsSystemDB() {
   211  		return false
   212  	}
   213  	name := entry.GetLastestSchemaLocked().Name
   214  	return name == pkgcatalog.MO_DATABASE ||
   215  		name == pkgcatalog.MO_TABLES ||
   216  		name == pkgcatalog.MO_COLUMNS
   217  }
   218  
   219  func (entry *TableEntry) GetRows() uint64 {
   220  	return entry.rows.Load()
   221  }
   222  
   223  func (entry *TableEntry) AddRows(delta uint64) uint64 {
   224  	return entry.rows.Add(delta)
   225  }
   226  
   227  func (entry *TableEntry) RemoveRows(delta uint64) uint64 {
   228  	return entry.rows.Add(^(delta - 1))
   229  }
   230  
   231  func (entry *TableEntry) GetObjectByID(id *types.Objectid) (obj *ObjectEntry, err error) {
   232  	entry.RLock()
   233  	defer entry.RUnlock()
   234  	node := entry.entries[*id]
   235  	if node == nil {
   236  		return nil, moerr.GetOkExpectedEOB()
   237  	}
   238  	return node.GetPayload(), nil
   239  }
   240  func (entry *TableEntry) GetObjectsByID(id *types.Segmentid) (obj []*ObjectEntry, err error) {
   241  	entry.RLock()
   242  	defer entry.RUnlock()
   243  	for nodeID, node := range entry.entries {
   244  		if nodeID.Segment().Eq(*id) {
   245  			if obj == nil {
   246  				obj = make([]*ObjectEntry, 0)
   247  			}
   248  			obj = append(obj, node.GetPayload())
   249  		}
   250  	}
   251  	if obj == nil {
   252  		return nil, moerr.GetOkExpectedEOB()
   253  	}
   254  	return obj, nil
   255  }
   256  
   257  func (entry *TableEntry) MakeObjectIt(reverse bool) *common.GenericSortedDListIt[*ObjectEntry] {
   258  	entry.RLock()
   259  	defer entry.RUnlock()
   260  	return common.NewGenericSortedDListIt(entry.RWMutex, entry.link, reverse)
   261  }
   262  
   263  func (entry *TableEntry) CreateObject(
   264  	txn txnif.AsyncTxn,
   265  	state EntryState,
   266  	opts *objectio.CreateObjOpt,
   267  	dataFactory ObjectDataFactory,
   268  ) (created *ObjectEntry, err error) {
   269  	entry.Lock()
   270  	defer entry.Unlock()
   271  	var id *objectio.ObjectId
   272  	if opts != nil && opts.Id != nil {
   273  		id = opts.Id
   274  	} else {
   275  		id = objectio.NewObjectid()
   276  	}
   277  	created = NewObjectEntry(entry, id, txn, state, dataFactory)
   278  	entry.AddEntryLocked(created)
   279  	return
   280  }
   281  
   282  func (entry *TableEntry) MakeCommand(id uint32) (cmd txnif.TxnCmd, err error) {
   283  	cmdType := IOET_WALTxnCommand_Table
   284  	entry.RLock()
   285  	defer entry.RUnlock()
   286  	return newTableCmd(id, cmdType, entry), nil
   287  }
   288  
   289  func (entry *TableEntry) Set1PC() {
   290  	entry.GetLatestNodeLocked().Set1PC()
   291  }
   292  func (entry *TableEntry) Is1PC() bool {
   293  	return entry.GetLatestNodeLocked().Is1PC()
   294  }
   295  func (entry *TableEntry) AddEntryLocked(objectEntry *ObjectEntry) {
   296  	n := entry.link.Insert(objectEntry)
   297  	entry.entries[objectEntry.ID] = n
   298  }
   299  
   300  func (entry *TableEntry) deleteEntryLocked(objectEntry *ObjectEntry) error {
   301  	if n, ok := entry.entries[objectEntry.ID]; !ok {
   302  		return moerr.GetOkExpectedEOB()
   303  	} else {
   304  		entry.link.Delete(n)
   305  		delete(entry.entries, objectEntry.ID)
   306  	}
   307  	return nil
   308  }
   309  
   310  // GetLastestSchemaLocked returns the latest committed schema with entry Not locked
   311  func (entry *TableEntry) GetLastestSchemaLocked() *Schema {
   312  	return entry.schema.Load()
   313  }
   314  
   315  // GetLastestSchema returns the latest committed schema with entry locked
   316  func (entry *TableEntry) GetLastestSchema() *Schema {
   317  	entry.Lock()
   318  	defer entry.Unlock()
   319  
   320  	return entry.schema.Load()
   321  }
   322  
   323  // GetVisibleSchema returns committed schema visible at the given txn
   324  func (entry *TableEntry) GetVisibleSchema(txn txnif.TxnReader) *Schema {
   325  	entry.RLock()
   326  	defer entry.RUnlock()
   327  	node := entry.GetVisibleNodeLocked(txn)
   328  	if node != nil {
   329  		return node.BaseNode.Schema
   330  	}
   331  	return nil
   332  }
   333  
   334  func (entry *TableEntry) GetVersionSchema(ver uint32) *Schema {
   335  	entry.RLock()
   336  	defer entry.RUnlock()
   337  	var ret *Schema
   338  	entry.LoopChainLocked(func(m *MVCCNode[*TableMVCCNode]) bool {
   339  		if cur := m.BaseNode.Schema.Version; cur > ver {
   340  			return true
   341  		} else if cur == ver {
   342  			ret = m.BaseNode.Schema
   343  		}
   344  		return false
   345  	})
   346  	return ret
   347  }
   348  
   349  func (entry *TableEntry) GetColDefs() []*ColDef {
   350  	return entry.GetLastestSchemaLocked().ColDefs
   351  }
   352  
   353  func (entry *TableEntry) GetFullName() string {
   354  	if len(entry.fullName) == 0 {
   355  		schema := entry.GetLastestSchemaLocked()
   356  		entry.fullName = genTblFullName(schema.AcInfo.TenantID, schema.Name)
   357  	}
   358  	return entry.fullName
   359  }
   360  
   361  func (entry *TableEntry) GetDB() *DBEntry {
   362  	return entry.db
   363  }
   364  
   365  func (entry *TableEntry) PPString(level common.PPLevel, depth int, prefix string) string {
   366  	var w bytes.Buffer
   367  	_, _ = w.WriteString(fmt.Sprintf("%s%s%s", common.RepeatStr("\t", depth), prefix, entry.StringWithLevel(level)))
   368  	if level == common.PPL0 {
   369  		return w.String()
   370  	}
   371  	it := entry.MakeObjectIt(true)
   372  	for it.Valid() {
   373  		objectEntry := it.Get().GetPayload()
   374  		_ = w.WriteByte('\n')
   375  		_, _ = w.WriteString(objectEntry.PPString(level, depth+1, prefix))
   376  		it.Next()
   377  	}
   378  	if level > common.PPL2 {
   379  		_ = w.WriteByte('\n')
   380  		it2 := entry.deleteList.Copy().Iter()
   381  		for it2.Next() {
   382  			w.WriteString(common.RepeatStr("\t", depth+1))
   383  			w.WriteString(prefix)
   384  			objID := it2.Item().ObjectID
   385  			w.WriteString(fmt.Sprintf("Tombstone[%s]\n", objID.String()))
   386  			it2.Item().GetObject().(*ObjectEntry).RLock()
   387  			w.WriteString(it2.Item().StringLocked(level, depth+1, prefix))
   388  			it2.Item().GetObject().(*ObjectEntry).RUnlock()
   389  		}
   390  	}
   391  	return w.String()
   392  }
   393  
   394  type TableStat struct {
   395  	ObjectCnt int
   396  	Loaded    int
   397  	Rows      int
   398  	OSize     int
   399  	Csize     int
   400  }
   401  
   402  func (entry *TableEntry) ObjectStats(level common.PPLevel, start, end int) (stat TableStat, w bytes.Buffer) {
   403  
   404  	it := entry.MakeObjectIt(true)
   405  	zonemapKind := common.ZonemapPrintKindNormal
   406  	if schema := entry.GetLastestSchemaLocked(); schema.HasSortKey() && strings.HasPrefix(schema.GetSingleSortKey().Name, "__") {
   407  		zonemapKind = common.ZonemapPrintKindCompose
   408  	}
   409  
   410  	if level == common.PPL3 { // some magic, do not ask why
   411  		zonemapKind = common.ZonemapPrintKindHex
   412  	}
   413  
   414  	scanCount := 0
   415  	needCount := end - start
   416  	if needCount < 0 {
   417  		needCount = math.MaxInt
   418  	}
   419  
   420  	for ; it.Valid(); it.Next() {
   421  		objectEntry := it.Get().GetPayload()
   422  		if !objectEntry.IsActive() {
   423  			continue
   424  		}
   425  		scanCount++
   426  		if scanCount <= start {
   427  			continue
   428  		}
   429  		if needCount <= 0 {
   430  			break
   431  		}
   432  		needCount--
   433  		stat.ObjectCnt += 1
   434  		if objectEntry.GetLoaded() {
   435  			stat.Loaded += 1
   436  			stat.Rows += int(objectEntry.GetRows())
   437  			stat.OSize += int(objectEntry.GetOriginSize())
   438  			stat.Csize += int(objectEntry.GetCompSize())
   439  		}
   440  		if level > common.PPL0 {
   441  			_ = w.WriteByte('\n')
   442  			_, _ = w.WriteString(objectEntry.ID.String())
   443  			_ = w.WriteByte('\n')
   444  			_, _ = w.WriteString("    ")
   445  			_, _ = w.WriteString(objectEntry.StatsString(zonemapKind))
   446  		}
   447  		if w.Len() > 8*common.Const1MBytes {
   448  			w.WriteString("\n...(truncated for too long, more than 8 MB)")
   449  			break
   450  		}
   451  	}
   452  	if level > common.PPL0 && stat.ObjectCnt > 0 {
   453  		w.WriteByte('\n')
   454  	}
   455  	return
   456  }
   457  
   458  func (entry *TableEntry) ObjectStatsString(level common.PPLevel, start, end int) string {
   459  	stat, detail := entry.ObjectStats(level, start, end)
   460  
   461  	var avgCsize, avgRow, avgOsize int
   462  	if stat.Loaded > 0 {
   463  		avgRow = stat.Rows / stat.Loaded
   464  		avgOsize = stat.OSize / stat.Loaded
   465  		avgCsize = stat.Csize / stat.Loaded
   466  	}
   467  
   468  	summary := fmt.Sprintf(
   469  		"summary: %d total, %d unknown, avgRow %d, avgOsize %s, avgCsize %v\n"+
   470  			"Update History:\n  rows %v\n  dels %v ",
   471  		stat.ObjectCnt, stat.ObjectCnt-stat.Loaded, avgRow, common.HumanReadableBytes(avgOsize), common.HumanReadableBytes(avgCsize),
   472  		entry.Stats.RowCnt.String(), entry.Stats.RowDel.String(),
   473  	)
   474  	detail.WriteString(summary)
   475  	return detail.String()
   476  }
   477  
   478  func (entry *TableEntry) String() string {
   479  	entry.RLock()
   480  	defer entry.RUnlock()
   481  	return entry.StringLocked()
   482  }
   483  
   484  func (entry *TableEntry) StringWithLevel(level common.PPLevel) string {
   485  	entry.RLock()
   486  	defer entry.RUnlock()
   487  	return entry.StringLockedWithLevel(level)
   488  }
   489  func (entry *TableEntry) StringLockedWithLevel(level common.PPLevel) string {
   490  	name := entry.GetLastestSchemaLocked().Name
   491  	if level <= common.PPL1 {
   492  		return fmt.Sprintf("TBL[%d][name=%s][C@%s,D@%s]",
   493  			entry.ID, name, entry.GetCreatedAtLocked().ToString(), entry.GetDeleteAtLocked().ToString())
   494  	}
   495  	return fmt.Sprintf("TBL%s[name=%s, id=%d]", entry.BaseEntryImpl.StringLocked(), name, entry.ID)
   496  }
   497  
   498  func (entry *TableEntry) StringLocked() string {
   499  	return entry.StringLockedWithLevel(common.PPL1)
   500  }
   501  
   502  func (entry *TableEntry) GetCatalog() *Catalog { return entry.db.catalog }
   503  
   504  func (entry *TableEntry) GetTableData() data.Table { return entry.tableData }
   505  
   506  func (entry *TableEntry) LastAppendableObject() (obj *ObjectEntry) {
   507  	it := entry.MakeObjectIt(false)
   508  	for it.Valid() {
   509  		itObj := it.Get().GetPayload()
   510  		dropped := itObj.HasDropCommitted()
   511  		if itObj.IsAppendable() && !dropped {
   512  			obj = itObj
   513  			break
   514  		}
   515  		it.Next()
   516  	}
   517  	return obj
   518  }
   519  
   520  func (entry *TableEntry) AsCommonID() *common.ID {
   521  	return &common.ID{
   522  		DbID:    entry.GetDB().ID,
   523  		TableID: entry.ID,
   524  	}
   525  }
   526  
   527  func (entry *TableEntry) RecurLoop(processor Processor) (err error) {
   528  	defer func() {
   529  		if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) {
   530  			err = nil
   531  		}
   532  	}()
   533  	objIt := entry.MakeObjectIt(true)
   534  	for objIt.Valid() {
   535  		objectEntry := objIt.Get().GetPayload()
   536  		if err := processor.OnObject(objectEntry); err != nil {
   537  			if moerr.IsMoErrCode(err, moerr.OkStopCurrRecur) {
   538  				objIt.Next()
   539  				continue
   540  			}
   541  			return err
   542  		}
   543  		if err := processor.OnPostObject(objectEntry); err != nil {
   544  			return err
   545  		}
   546  		objIt.Next()
   547  	}
   548  	tombstones := entry.deleteList.Copy().Items()
   549  	for _, deletes := range tombstones {
   550  		err = processor.OnTombstone(deletes)
   551  		if err != nil {
   552  			return
   553  		}
   554  	}
   555  	return
   556  }
   557  
   558  func (entry *TableEntry) DropObjectEntry(id *types.Objectid, txn txnif.AsyncTxn) (deleted *ObjectEntry, err error) {
   559  	obj, err := entry.GetObjectByID(id)
   560  	if err != nil {
   561  		return
   562  	}
   563  	obj.Lock()
   564  	defer obj.Unlock()
   565  	needWait, waitTxn := obj.NeedWaitCommittingLocked(txn.GetStartTS())
   566  	if needWait {
   567  		obj.Unlock()
   568  		waitTxn.GetTxnState(true)
   569  		obj.Lock()
   570  	}
   571  	var isNewNode bool
   572  	isNewNode, err = obj.DropEntryLocked(txn)
   573  	if err == nil && isNewNode {
   574  		deleted = obj
   575  	}
   576  	return
   577  }
   578  
   579  func (entry *TableEntry) RemoveEntry(objectEntry *ObjectEntry) (err error) {
   580  	logutil.Debug("[Catalog]", common.OperationField("remove"),
   581  		common.OperandField(objectEntry.String()))
   582  	// objectEntry.Close()
   583  	entry.Lock()
   584  	defer entry.Unlock()
   585  	return entry.deleteEntryLocked(objectEntry)
   586  }
   587  
   588  func (entry *TableEntry) PrepareRollback() (err error) {
   589  	// Safety: in commit queue, that's ok without lock
   590  	t := entry.GetLatestNodeLocked()
   591  	if schema := t.BaseNode.Schema; schema.Extra.OldName != "" {
   592  		fullname := genTblFullName(schema.AcInfo.TenantID, schema.Name)
   593  		entry.GetDB().RollbackRenameTable(fullname, entry.ID)
   594  	}
   595  	var isEmpty bool
   596  	isEmpty, err = entry.BaseEntryImpl.PrepareRollback()
   597  	if err != nil {
   598  		return
   599  	}
   600  	if isEmpty {
   601  		err = entry.GetDB().RemoveEntry(entry)
   602  		if err != nil {
   603  			return
   604  		}
   605  	}
   606  	return
   607  }
   608  
   609  /*
   610  s: start
   611  p: prepare
   612  c: commit
   613  
   614  	         	    old schema  <- | -> new schema
   615  	        					   |
   616  		  s------------------p-----c         AlterColumn Txn
   617  
   618  Append Txn:
   619  
   620  	          s------------p----c               Yes
   621  	              s-------------p--------c      Yes
   622  	s-----------------------p---------c         Yes
   623  	           s----------------------p         No, schema at s is not same with schema at p
   624  */
   625  func (entry *TableEntry) ApplyCommit() (err error) {
   626  	err = entry.BaseEntryImpl.ApplyCommit()
   627  	if err != nil {
   628  		return
   629  	}
   630  	// It is not wanted that a block spans across different schemas
   631  	if entry.isColumnChangedInSchema() {
   632  		entry.FreezeAppend()
   633  	}
   634  	entry.RLock()
   635  	schema := entry.GetLatestNodeLocked().BaseNode.Schema
   636  	entry.RUnlock()
   637  	// update the shortcut to the lastest schema
   638  	entry.TableNode.schema.Store(schema)
   639  	return
   640  }
   641  
   642  // hasColumnChangedSchema checks if add or drop columns on previous schema
   643  func (entry *TableEntry) isColumnChangedInSchema() bool {
   644  	entry.RLock()
   645  	defer entry.RUnlock()
   646  	node := entry.GetLatestNodeLocked()
   647  	toCommitted := node.BaseNode.Schema
   648  	ver := toCommitted.Version
   649  	// skip create table
   650  	if ver == 0 {
   651  		return false
   652  	}
   653  	return toCommitted.Extra.ColumnChanged
   654  }
   655  
   656  func (entry *TableEntry) FreezeAppend() {
   657  	obj := entry.LastAppendableObject()
   658  	if obj == nil {
   659  		// nothing to freeze
   660  		return
   661  	}
   662  	obj.GetObjectData().FreezeAppend()
   663  }
   664  
   665  // IsActive is coarse API: no consistency check
   666  func (entry *TableEntry) IsActive() bool {
   667  	db := entry.GetDB()
   668  	if !db.IsActive() {
   669  		return false
   670  	}
   671  	return !entry.HasDropCommitted()
   672  }
   673  
   674  // GetTerminationTS is coarse API: no consistency check
   675  func (entry *TableEntry) GetTerminationTS() (ts types.TS, terminated bool) {
   676  	dbEntry := entry.GetDB()
   677  
   678  	terminated, ts = dbEntry.TryGetTerminatedTS(true)
   679  
   680  	return
   681  }
   682  
   683  func (entry *TableEntry) AlterTable(ctx context.Context, txn txnif.TxnReader, req *apipb.AlterTableReq) (isNewNode bool, newSchema *Schema, err error) {
   684  	entry.Lock()
   685  	defer entry.Unlock()
   686  	needWait, txnToWait := entry.NeedWaitCommittingLocked(txn.GetStartTS())
   687  	if needWait {
   688  		entry.Unlock()
   689  		txnToWait.GetTxnState(true)
   690  		entry.Lock()
   691  	}
   692  	err = entry.CheckConflictLocked(txn)
   693  	if err != nil {
   694  		return
   695  	}
   696  	var node *MVCCNode[*TableMVCCNode]
   697  	isNewNode, node = entry.getOrSetUpdateNodeLocked(txn)
   698  
   699  	newSchema = node.BaseNode.Schema
   700  	if isNewNode {
   701  		// Extra info(except seqnnum etc.) is meaningful to the previous version schema
   702  		// reset in new Schema
   703  		var hints []apipb.MergeHint
   704  		copy(hints, newSchema.Extra.Hints)
   705  		newSchema.Extra = &apipb.SchemaExtra{
   706  			NextColSeqnum:     newSchema.Extra.NextColSeqnum,
   707  			MinOsizeQuailifed: newSchema.Extra.MinOsizeQuailifed,
   708  			MaxObjOnerun:      newSchema.Extra.MaxObjOnerun,
   709  			MaxOsizeMergedObj: newSchema.Extra.MaxOsizeMergedObj,
   710  			MinCnMergeSize:    newSchema.Extra.MinCnMergeSize,
   711  			Hints:             hints,
   712  		}
   713  
   714  	}
   715  	if err = newSchema.ApplyAlterTable(req); err != nil {
   716  		return
   717  	}
   718  	if isNewNode {
   719  		node.BaseNode.Schema.Version += 1
   720  	}
   721  	return
   722  }
   723  
   724  func (entry *TableEntry) CreateWithTxnAndSchema(txn txnif.AsyncTxn, schema *Schema) {
   725  	node := &MVCCNode[*TableMVCCNode]{
   726  		EntryMVCCNode: &EntryMVCCNode{
   727  			CreatedAt: txnif.UncommitTS,
   728  		},
   729  		TxnMVCCNode: txnbase.NewTxnMVCCNodeWithTxn(txn),
   730  		BaseNode: &TableMVCCNode{
   731  			Schema: schema,
   732  		},
   733  	}
   734  	entry.Insert(node)
   735  }
   736  
   737  func (entry *TableEntry) GetVisibilityAndName(txn txnif.TxnReader) (visible, dropped bool, name string) {
   738  	entry.RLock()
   739  	defer entry.RUnlock()
   740  	needWait, txnToWait := entry.NeedWaitCommittingLocked(txn.GetStartTS())
   741  	if needWait {
   742  		entry.RUnlock()
   743  		txnToWait.GetTxnState(true)
   744  		entry.RLock()
   745  	}
   746  	un := entry.GetVisibleNodeLocked(txn)
   747  	if un == nil {
   748  		return
   749  	}
   750  	visible = true
   751  	if un.IsSameTxn(txn) {
   752  		dropped = un.HasDropIntent()
   753  	} else {
   754  		dropped = un.HasDropCommitted()
   755  	}
   756  	name = un.BaseNode.Schema.Name
   757  	return
   758  }
   759  
   760  // only for test
   761  func MockTableEntryWithDB(dbEntry *DBEntry, tblId uint64) *TableEntry {
   762  	entry := NewReplayTableEntry()
   763  	entry.TableNode = &TableNode{}
   764  	entry.TableNode.schema.Store(NewEmptySchema("test"))
   765  	entry.ID = tblId
   766  	entry.db = dbEntry
   767  	return entry
   768  }