github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/show_account.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 frontend
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"math"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/catalog"
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/common/mpool"
    27  	"github.com/matrixorigin/matrixone/pkg/container/batch"
    28  	"github.com/matrixorigin/matrixone/pkg/container/vector"
    29  	"github.com/matrixorigin/matrixone/pkg/defines"
    30  	"github.com/matrixorigin/matrixone/pkg/fileservice"
    31  	"github.com/matrixorigin/matrixone/pkg/logutil"
    32  	"github.com/matrixorigin/matrixone/pkg/pb/api"
    33  	"github.com/matrixorigin/matrixone/pkg/pb/plan"
    34  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    35  	"github.com/matrixorigin/matrixone/pkg/sql/plan/function/ctl"
    36  	v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2"
    37  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/db"
    38  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logtail"
    39  	"github.com/matrixorigin/matrixone/pkg/vm/process"
    40  )
    41  
    42  const (
    43  	getAllAccountInfoFormat = "select " +
    44  		"account_id as `account_id`, " +
    45  		"account_name as `account_name`, " +
    46  		"created_time as `created`, " +
    47  		"status as `status`, " +
    48  		"suspended_time as `suspended_time`, " +
    49  		"comments as `comment` " +
    50  		"from " +
    51  		"mo_catalog.mo_account " +
    52  		"%s" +
    53  		";"
    54  
    55  	getAccountInfoFormat = "select " +
    56  		"account_id as `account_id`, " +
    57  		"account_name as `account_name`, " +
    58  		"created_time as `created`, " +
    59  		"status as `status`, " +
    60  		"suspended_time as `suspended_time`, " +
    61  		"comments as `comment` " +
    62  		"from " +
    63  		"mo_catalog.mo_account " +
    64  		"where account_id = %d;"
    65  
    66  	// column index in the result set generated by
    67  	// the sql getAllAccountInfoFormat, getAccountInfoFormat
    68  	idxOfAccountId     = 0
    69  	idxOfAccountName   = 1
    70  	idxOfCreated       = 2
    71  	idxOfStatus        = 3
    72  	idxOfSuspendedTime = 4
    73  	idxOfComment       = 5
    74  
    75  	// left the `size` as a placeholder, the value will be embedding later
    76  	// index table(__index_xxx): do not counting it into the `table_count`, but need its size.
    77  	// mo_increment_columns: do not counting it into the `table_count`, but need its size.
    78  	// subscription db: do not counting it into the `db_count`, and its size
    79  	// sys table(mo_database, mo_tables, mo_column): counting them into the `table_count`, and need their sizes
    80  	getTableStatsFormatV2 = "select " +
    81  		"( select " +
    82  		"        mu2.user_name as `admin_name` " +
    83  		"  from mo_catalog.mo_user as mu2 join " +
    84  		"      ( select " +
    85  		"              min(user_id) as `min_user_id` " +
    86  		"        from mo_catalog.mo_user " +
    87  		"      ) as mu1 on mu2.user_id = mu1.min_user_id " +
    88  		") as `admin_name`, " +
    89  		"count(distinct md.datname) as `db_count`, " +
    90  		"count(distinct mt.relname) as `table_count`, " +
    91  		"cast(0 as double) as `size` " +
    92  		"from " +
    93  		"mo_catalog.mo_tables as mt, mo_catalog.mo_database as md " +
    94  		"where md.dat_type != 'subscription' " +
    95  		"and mt.relkind in ('v','r','e','cluster') " +
    96  		"and mt.account_id in (%d, %d);"
    97  
    98  	// column index in the result set generated by
    99  	// the sql getTableStatsFormatV2
   100  	idxOfAdminName  = 0
   101  	idxOfDBCount    = 1
   102  	idxOfTableCount = 2
   103  	idxOfSize       = 3
   104  
   105  	// column index in the result set of the statement show accounts
   106  	finalIdxOfAccountName   = 0
   107  	finalIdxOfAdminName     = 1
   108  	finalIdxOfCreated       = 2
   109  	finalIdxOfStatus        = 3
   110  	finalIdxOfSuspendedTime = 4
   111  	finalIdxOfDBCount       = 5
   112  	finalIdxOfTableCount    = 6
   113  	finalIdxOfSize          = 7
   114  	finalIdxOfComment       = 8
   115  	finalColumnCount        = 9
   116  )
   117  
   118  var cnUsageCache = logtail.NewStorageUsageCache(
   119  	logtail.WithLazyThreshold(5))
   120  
   121  func getSqlForAllAccountInfo(like *tree.ComparisonExpr) string {
   122  	var likePattern = ""
   123  	if like != nil {
   124  		likePattern = strings.TrimSpace(like.Right.String())
   125  	}
   126  	likeClause := ""
   127  	if len(likePattern) != 0 {
   128  		likeClause = fmt.Sprintf("where account_name like '%s'", likePattern)
   129  	}
   130  	return fmt.Sprintf(getAllAccountInfoFormat, likeClause)
   131  }
   132  
   133  func getSqlForAccountInfo(accountId uint64) string {
   134  	return fmt.Sprintf(getAccountInfoFormat, accountId)
   135  }
   136  
   137  func getSqlForTableStats(accountId int32) string {
   138  	//return fmt.Sprintf(getTableStatsFormatV2, catalog.SystemPartitionRel, accountId)
   139  	return fmt.Sprintf(getTableStatsFormatV2, accountId, sysAccountID)
   140  }
   141  
   142  func requestStorageUsage(ctx context.Context, ses *Session, accIds [][]int32) (resp any, tried bool, err error) {
   143  	whichTN := func(string) ([]uint64, error) { return nil, nil }
   144  	payload := func(tnShardID uint64, parameter string, proc *process.Process) ([]byte, error) {
   145  		req := db.StorageUsageReq{}
   146  		for x := range accIds {
   147  			req.AccIds = append(req.AccIds, accIds[x]...)
   148  		}
   149  
   150  		return req.Marshal()
   151  	}
   152  
   153  	responseUnmarshaler := func(payload []byte) (any, error) {
   154  		usage := &db.StorageUsageResp{}
   155  		if err := usage.Unmarshal(payload); err != nil {
   156  			return nil, err
   157  		}
   158  		return usage, nil
   159  	}
   160  
   161  	txnOperator := ses.txnHandler.GetTxn()
   162  
   163  	// create a new proc for `handler`
   164  	proc := process.New(ctx, ses.proc.GetMPool(),
   165  		ses.proc.TxnClient, txnOperator,
   166  		ses.proc.FileService, ses.proc.LockService,
   167  		ses.proc.QueryClient, ses.proc.Hakeeper,
   168  		ses.proc.UdfService, ses.proc.Aicm,
   169  	)
   170  
   171  	handler := ctl.GetTNHandlerFunc(api.OpCode_OpStorageUsage, whichTN, payload, responseUnmarshaler)
   172  	result, err := handler(proc, "DN", "", ctl.MoCtlTNCmdSender)
   173  	if moerr.IsMoErrCode(err, moerr.ErrNotSupported) {
   174  		// try the previous RPC method
   175  		payload_V0 := func(tnShardID uint64, parameter string, proc *process.Process) ([]byte, error) { return nil, nil }
   176  		responseUnmarshaler_V0 := func(payload []byte) (interface{}, error) {
   177  			usage := &db.StorageUsageResp_V0{}
   178  			if err := usage.Unmarshal(payload); err != nil {
   179  				return nil, err
   180  			}
   181  			return usage, nil
   182  		}
   183  
   184  		tried = true
   185  		CmdMethod_StorageUsage := api.OpCode(14)
   186  		handler = ctl.GetTNHandlerFunc(CmdMethod_StorageUsage, whichTN, payload_V0, responseUnmarshaler_V0)
   187  		result, err = handler(proc, "DN", "", ctl.MoCtlTNCmdSender)
   188  
   189  		if moerr.IsMoErrCode(err, moerr.ErrNotSupported) {
   190  			return nil, tried, moerr.NewNotSupportedNoCtx("current tn version not supported `show accounts`")
   191  		}
   192  	}
   193  
   194  	if err != nil {
   195  		return nil, tried, err
   196  	}
   197  
   198  	return result.Data.([]any)[0], tried, nil
   199  }
   200  
   201  func handleStorageUsageResponse_V0(ctx context.Context, fs fileservice.FileService,
   202  	usage *db.StorageUsageResp_V0) (map[int32]uint64, error) {
   203  	result := make(map[int32]uint64, 0)
   204  	for idx := range usage.CkpEntries {
   205  		version := usage.CkpEntries[idx].Version
   206  		location := usage.CkpEntries[idx].Location
   207  
   208  		// storage usage was introduced after `CheckpointVersion9`
   209  		if version < logtail.CheckpointVersion9 {
   210  			// exist old version checkpoint which hasn't storage usage data in it,
   211  			// to avoid inaccurate info leading misunderstand, we chose to return empty result
   212  			logutil.Info("[storage usage]: found older ckp when handle storage usage response")
   213  			return map[int32]uint64{}, nil
   214  		}
   215  
   216  		ckpData, err := logtail.LoadSpecifiedCkpBatch(ctx, location, version, logtail.StorageUsageInsIDX, fs)
   217  		if err != nil {
   218  			return nil, err
   219  		}
   220  
   221  		storageUsageBat := ckpData.GetBatches()[logtail.StorageUsageInsIDX]
   222  		accIDVec := vector.MustFixedCol[uint64](
   223  			storageUsageBat.GetVectorByName(catalog.SystemColAttr_AccID).GetDownstreamVector(),
   224  		)
   225  		sizeVec := vector.MustFixedCol[uint64](
   226  			storageUsageBat.GetVectorByName(logtail.CheckpointMetaAttr_ObjectSize).GetDownstreamVector(),
   227  		)
   228  
   229  		size := uint64(0)
   230  		length := len(accIDVec)
   231  		for i := 0; i < length; i++ {
   232  			result[int32(accIDVec[i])] += sizeVec[i]
   233  			size += sizeVec[i]
   234  		}
   235  
   236  		ckpData.Close()
   237  	}
   238  
   239  	// [account_id, db_id, table_id, obj_id, table_total_size]
   240  	for _, info := range usage.BlockEntries {
   241  		result[int32(info.Info[0])] += info.Info[3]
   242  	}
   243  
   244  	return result, nil
   245  }
   246  
   247  func handleStorageUsageResponse(
   248  	ctx context.Context,
   249  	usage *db.StorageUsageResp,
   250  ) (map[int32]uint64, error) {
   251  	result := make(map[int32]uint64, 0)
   252  
   253  	for x := range usage.AccIds {
   254  		result[usage.AccIds[x]] += usage.Sizes[x]
   255  	}
   256  
   257  	return result, nil
   258  }
   259  
   260  func checkStorageUsageCache(accIds [][]int32) (result map[int32]uint64, succeed bool) {
   261  	cnUsageCache.Lock()
   262  	defer cnUsageCache.Unlock()
   263  
   264  	if cnUsageCache.IsExpired() {
   265  		return nil, false
   266  	}
   267  
   268  	result = make(map[int32]uint64)
   269  	for x := range accIds {
   270  		for y := range accIds[x] {
   271  			size, exist := cnUsageCache.GatherAccountSize(uint64(accIds[x][y]))
   272  			if !exist {
   273  				// one missed, update all
   274  				return nil, false
   275  			}
   276  
   277  			result[accIds[x][y]] = size
   278  		}
   279  	}
   280  
   281  	return result, true
   282  }
   283  
   284  func updateStorageUsageCache(accIds []int32, sizes []uint64) {
   285  
   286  	if len(accIds) == 0 {
   287  		return
   288  	}
   289  
   290  	cnUsageCache.Lock()
   291  	defer cnUsageCache.Unlock()
   292  
   293  	// step 1: delete stale accounts
   294  	cnUsageCache.ClearForUpdate()
   295  
   296  	// step 2: update
   297  	for x := range accIds {
   298  		usage := logtail.UsageData{AccId: uint64(accIds[x]), Size: sizes[x]}
   299  		if old, exist := cnUsageCache.Get(usage); exist {
   300  			usage.Size += old.Size
   301  		}
   302  
   303  		cnUsageCache.SetOrReplace(usage)
   304  	}
   305  }
   306  
   307  // getAccountStorageUsage calculates the storage usage of all accounts
   308  // by handling checkpoint
   309  func getAccountsStorageUsage(ctx context.Context, ses *Session, accIds [][]int32) (map[int32]uint64, error) {
   310  	if len(accIds) == 0 {
   311  		return nil, nil
   312  	}
   313  
   314  	// step 1: check cache
   315  	if usage, succeed := checkStorageUsageCache(accIds); succeed {
   316  		return usage, nil
   317  	}
   318  
   319  	// step 2: query to tn
   320  	response, tried, err := requestStorageUsage(ctx, ses, accIds)
   321  	if err != nil {
   322  		return nil, err
   323  	}
   324  
   325  	if tried {
   326  		usage, ok := response.(*db.StorageUsageResp_V0)
   327  		if !ok {
   328  			return nil, moerr.NewInternalErrorNoCtx("storage usage response decode failed, retry later")
   329  		}
   330  
   331  		fs, err := fileservice.Get[fileservice.FileService](getGlobalPu().FileService, defines.SharedFileServiceName)
   332  		if err != nil {
   333  			return nil, err
   334  		}
   335  
   336  		// step 3: handling these pulled data
   337  		return handleStorageUsageResponse_V0(ctx, fs, usage)
   338  
   339  	} else {
   340  		usage, ok := response.(*db.StorageUsageResp)
   341  		if !ok || usage.Magic != logtail.StorageUsageMagic {
   342  			return nil, moerr.NewInternalErrorNoCtx("storage usage response decode failed, retry later")
   343  		}
   344  
   345  		updateStorageUsageCache(usage.AccIds, usage.Sizes)
   346  
   347  		// step 3: handling these pulled data
   348  		return handleStorageUsageResponse(ctx, usage)
   349  	}
   350  }
   351  
   352  func embeddingSizeToBatch(ori *batch.Batch, size uint64, mp *mpool.MPool) {
   353  	vector.SetFixedAt(ori.Vecs[idxOfSize], 0, math.Round(float64(size)/1048576.0*1e6)/1e6)
   354  }
   355  
   356  func doShowAccounts(ctx context.Context, ses *Session, sa *tree.ShowAccounts) (err error) {
   357  	var sql string
   358  	var accountIds [][]int32
   359  	var allAccountInfo []*batch.Batch
   360  	var eachAccountInfo []*batch.Batch
   361  	var tempBatch *batch.Batch
   362  	var MoAccountColumns, EachAccountColumns *plan.ResultColDef
   363  	var outputBatches []*batch.Batch
   364  
   365  	start := time.Now()
   366  	getTableStatsDur := time.Duration(0)
   367  	defer func() {
   368  		v2.TaskShowAccountsTotalDurationHistogram.Observe(time.Since(start).Seconds())
   369  		v2.TaskShowAccountsGetTableStatsDurationHistogram.Observe(getTableStatsDur.Seconds())
   370  
   371  	}()
   372  
   373  	mp := ses.GetMemPool()
   374  
   375  	defer func() {
   376  		for _, b := range allAccountInfo {
   377  			if b == nil {
   378  				continue
   379  			}
   380  			b.Clean(mp)
   381  		}
   382  		for _, b := range outputBatches {
   383  			if b == nil {
   384  				continue
   385  			}
   386  			b.Clean(mp)
   387  		}
   388  		for _, b := range eachAccountInfo {
   389  			if b == nil {
   390  				continue
   391  			}
   392  			b.Clean(mp)
   393  		}
   394  		if tempBatch != nil {
   395  			tempBatch.Clean(mp)
   396  		}
   397  	}()
   398  
   399  	bh := ses.GetRawBatchBackgroundExec(ctx)
   400  	defer bh.Close()
   401  
   402  	account := ses.GetTenantInfo()
   403  
   404  	err = bh.Exec(ctx, "begin;")
   405  	defer func() {
   406  		err = finishTxn(ctx, bh, err)
   407  	}()
   408  
   409  	if err != nil {
   410  		return err
   411  	}
   412  
   413  	var rsOfMoAccount *MysqlResultSet
   414  
   415  	// step 1:
   416  	// 	get all accounts info according the type of the requested tenant
   417  
   418  	// system account
   419  	if account.IsSysTenant() {
   420  		sql = getSqlForAllAccountInfo(sa.Like)
   421  		if allAccountInfo, accountIds, err = getAccountInfo(ctx, bh, sql, true); err != nil {
   422  			return err
   423  		}
   424  
   425  		// normal account
   426  	} else {
   427  		if sa.Like != nil {
   428  			return moerr.NewInternalError(ctx, "only sys account can use LIKE clause")
   429  		}
   430  		// switch to the sys account to get account info
   431  		newCtx := defines.AttachAccountId(ctx, uint32(sysAccountID))
   432  		sql = getSqlForAccountInfo(uint64(account.GetTenantID()))
   433  		if allAccountInfo, accountIds, err = getAccountInfo(newCtx, bh, sql, true); err != nil {
   434  			return err
   435  		}
   436  
   437  		if len(allAccountInfo) != 1 {
   438  			return moerr.NewInternalError(ctx, "no such account %v", account.GetTenantID())
   439  		}
   440  	}
   441  
   442  	backSes := bh.(*backExec)
   443  	rsOfMoAccount = backSes.backSes.allResultSet[0]
   444  	MoAccountColumns = backSes.backSes.rs
   445  	bh.ClearExecResultSet()
   446  
   447  	// step 2
   448  	// calculating the storage usage size of accounts
   449  	// the returned value is a map: account_id -> size (in bytes)
   450  	tt := time.Now()
   451  	usage, err := getAccountsStorageUsage(ctx, ses, accountIds)
   452  	if err != nil {
   453  		return err
   454  	}
   455  	v2.TaskShowAccountsGetUsageDurationHistogram.Observe(time.Since(tt).Seconds())
   456  
   457  	// step 3
   458  	outputBatches = make([]*batch.Batch, len(allAccountInfo))
   459  	for i, ids := range accountIds {
   460  		for _, id := range ids {
   461  			//step 3.1: get the admin_name, db_count, table_count for each account
   462  			newCtx := defines.AttachAccountId(ctx, uint32(id))
   463  
   464  			tt = time.Now()
   465  			if tempBatch, err = getTableStats(newCtx, bh, id); err != nil {
   466  				return err
   467  			}
   468  			getTableStatsDur += time.Since(tt)
   469  
   470  			// step 3.2: put size value into batch
   471  			embeddingSizeToBatch(tempBatch, usage[id], mp)
   472  
   473  			eachAccountInfo = append(eachAccountInfo, tempBatch)
   474  		}
   475  
   476  		// merge result set from mo_account and table stats from each account
   477  		outputBatches[i] = batch.NewWithSize(finalColumnCount)
   478  		if err = mergeOutputResult(ses, outputBatches[i], allAccountInfo[i], eachAccountInfo); err != nil {
   479  			return err
   480  		}
   481  
   482  		for _, b := range eachAccountInfo {
   483  			b.Clean(mp)
   484  		}
   485  		eachAccountInfo = nil
   486  	}
   487  
   488  	rsOfEachAccount := backSes.backSes.allResultSet[0]
   489  	EachAccountColumns = backSes.backSes.rs
   490  	bh.ClearExecResultSet()
   491  
   492  	//step4: generate mysql result set
   493  	outputRS := &MysqlResultSet{}
   494  	if err = initOutputRs(outputRS, rsOfMoAccount, rsOfEachAccount, ctx); err != nil {
   495  		return err
   496  	}
   497  
   498  	oq := newFakeOutputQueue(outputRS)
   499  	for _, b := range outputBatches {
   500  		if err = fillResultSet(ctx, oq, b, ses); err != nil {
   501  			return err
   502  		}
   503  	}
   504  
   505  	ses.SetMysqlResultSet(outputRS)
   506  
   507  	ses.rs = mergeRsColumns(MoAccountColumns, EachAccountColumns)
   508  	if openSaveQueryResult(ctx, ses) {
   509  		err = saveResult(ctx, ses, outputBatches)
   510  	}
   511  
   512  	return err
   513  }
   514  
   515  func mergeRsColumns(rsOfMoAccountColumns *plan.ResultColDef, rsOfEachAccountColumns *plan.ResultColDef) *plan.ResultColDef {
   516  	def := &plan.ResultColDef{
   517  		ResultCols: make([]*plan.ColDef, finalColumnCount),
   518  	}
   519  	def.ResultCols[finalIdxOfAccountName] = rsOfMoAccountColumns.ResultCols[idxOfAccountName]
   520  	def.ResultCols[finalIdxOfAdminName] = rsOfEachAccountColumns.ResultCols[idxOfAdminName]
   521  	def.ResultCols[finalIdxOfCreated] = rsOfMoAccountColumns.ResultCols[idxOfCreated]
   522  	def.ResultCols[finalIdxOfStatus] = rsOfMoAccountColumns.ResultCols[idxOfStatus]
   523  	def.ResultCols[finalIdxOfSuspendedTime] = rsOfMoAccountColumns.ResultCols[idxOfSuspendedTime]
   524  	def.ResultCols[finalIdxOfDBCount] = rsOfEachAccountColumns.ResultCols[idxOfDBCount]
   525  	def.ResultCols[finalIdxOfTableCount] = rsOfEachAccountColumns.ResultCols[idxOfTableCount]
   526  	def.ResultCols[finalIdxOfSize] = rsOfEachAccountColumns.ResultCols[idxOfSize]
   527  	def.ResultCols[finalIdxOfComment] = rsOfMoAccountColumns.ResultCols[idxOfComment]
   528  	return def
   529  }
   530  
   531  func saveResult(ctx context.Context, ses *Session, outputBatch []*batch.Batch) error {
   532  	for _, b := range outputBatch {
   533  		if err := saveQueryResult(ctx, ses, b); err != nil {
   534  			return err
   535  		}
   536  	}
   537  	if err := saveQueryResultMeta(ctx, ses); err != nil {
   538  		return err
   539  	}
   540  	return nil
   541  }
   542  
   543  func initOutputRs(rs *MysqlResultSet, rsOfMoAccount *MysqlResultSet, rsOfEachAccount *MysqlResultSet, ctx context.Context) error {
   544  	outputColumns := make([]Column, finalColumnCount)
   545  	var err error
   546  	outputColumns[finalIdxOfAccountName], err = rsOfMoAccount.GetColumn(ctx, idxOfAccountName)
   547  	if err != nil {
   548  		return err
   549  	}
   550  	outputColumns[finalIdxOfAdminName], err = rsOfEachAccount.GetColumn(ctx, idxOfAdminName)
   551  	if err != nil {
   552  		return err
   553  	}
   554  	outputColumns[finalIdxOfCreated], err = rsOfMoAccount.GetColumn(ctx, idxOfCreated)
   555  	if err != nil {
   556  		return err
   557  	}
   558  	outputColumns[finalIdxOfStatus], err = rsOfMoAccount.GetColumn(ctx, idxOfStatus)
   559  	if err != nil {
   560  		return err
   561  	}
   562  	outputColumns[finalIdxOfSuspendedTime], err = rsOfMoAccount.GetColumn(ctx, idxOfSuspendedTime)
   563  	if err != nil {
   564  		return err
   565  	}
   566  	outputColumns[finalIdxOfDBCount], err = rsOfEachAccount.GetColumn(ctx, idxOfDBCount)
   567  	if err != nil {
   568  		return err
   569  	}
   570  	outputColumns[finalIdxOfTableCount], err = rsOfEachAccount.GetColumn(ctx, idxOfTableCount)
   571  	if err != nil {
   572  		return err
   573  	}
   574  	outputColumns[finalIdxOfSize], err = rsOfEachAccount.GetColumn(ctx, idxOfSize)
   575  	if err != nil {
   576  		return err
   577  	}
   578  	outputColumns[finalIdxOfComment], err = rsOfMoAccount.GetColumn(ctx, idxOfComment)
   579  	if err != nil {
   580  		return err
   581  	}
   582  	for _, o := range outputColumns {
   583  		rs.AddColumn(o)
   584  	}
   585  	return nil
   586  }
   587  
   588  // getAccountInfo gets account info from mo_account under sys account
   589  func getAccountInfo(ctx context.Context,
   590  	bh BackgroundExec,
   591  	sql string,
   592  	returnAccountIds bool) ([]*batch.Batch, [][]int32, error) {
   593  	var err error
   594  	var batchIndex2AccounsIds [][]int32
   595  	var rsOfMoAccount []*batch.Batch
   596  
   597  	bh.ClearExecResultBatches()
   598  	err = bh.Exec(ctx, sql)
   599  	if err != nil {
   600  		return nil, nil, err
   601  	}
   602  
   603  	rsOfMoAccount = bh.GetExecResultBatches()
   604  	if len(rsOfMoAccount) == 0 {
   605  		return nil, nil, moerr.NewInternalError(ctx, "no account info")
   606  	}
   607  	if returnAccountIds {
   608  		batchCount := len(rsOfMoAccount)
   609  		batchIndex2AccounsIds = make([][]int32, batchCount)
   610  		for i := 0; i < batchCount; i++ {
   611  			vecLen := rsOfMoAccount[i].Vecs[0].Length()
   612  			for row := 0; row < vecLen; row++ {
   613  				batchIndex2AccounsIds[i] = append(batchIndex2AccounsIds[i], vector.GetFixedAt[int32](rsOfMoAccount[i].Vecs[0], row))
   614  			}
   615  		}
   616  	}
   617  	return rsOfMoAccount, batchIndex2AccounsIds, err
   618  }
   619  
   620  // getTableStats gets the table statistics for the account
   621  func getTableStats(ctx context.Context, bh BackgroundExec, accountId int32) (*batch.Batch, error) {
   622  	var sql string
   623  	var err error
   624  	var rs []*batch.Batch
   625  	sql = getSqlForTableStats(accountId)
   626  	bh.ClearExecResultBatches()
   627  	err = bh.Exec(ctx, sql)
   628  	if err != nil {
   629  		return nil, err
   630  	}
   631  	rs = bh.GetExecResultBatches()
   632  	if len(rs) != 1 {
   633  		return nil, moerr.NewInternalError(ctx, "get table stats failed")
   634  	}
   635  	return rs[0], err
   636  }
   637  
   638  // mergeOutputResult merges the result set from mo_account and the table status
   639  // into the final output format
   640  func mergeOutputResult(ses *Session, outputBatch *batch.Batch, rsOfMoAccount *batch.Batch, rsOfEachAccount []*batch.Batch) error {
   641  	var err error
   642  	mp := ses.GetMemPool()
   643  	outputBatch.Vecs[finalIdxOfAccountName], err = rsOfMoAccount.Vecs[idxOfAccountName].Dup(mp)
   644  	if err != nil {
   645  		return err
   646  	}
   647  	outputBatch.Vecs[finalIdxOfAdminName] = vector.NewVec(*rsOfEachAccount[0].Vecs[idxOfAdminName].GetType())
   648  	outputBatch.Vecs[finalIdxOfCreated], err = rsOfMoAccount.Vecs[idxOfCreated].Dup(mp)
   649  	if err != nil {
   650  		return err
   651  	}
   652  	outputBatch.Vecs[finalIdxOfStatus], err = rsOfMoAccount.Vecs[idxOfStatus].Dup(mp)
   653  	if err != nil {
   654  		return err
   655  	}
   656  	outputBatch.Vecs[finalIdxOfSuspendedTime], err = rsOfMoAccount.Vecs[idxOfSuspendedTime].Dup(mp)
   657  	if err != nil {
   658  		return err
   659  	}
   660  	outputBatch.Vecs[finalIdxOfDBCount] = vector.NewVec(*rsOfEachAccount[0].Vecs[idxOfDBCount].GetType())
   661  	outputBatch.Vecs[finalIdxOfTableCount] = vector.NewVec(*rsOfEachAccount[0].Vecs[idxOfTableCount].GetType())
   662  	outputBatch.Vecs[finalIdxOfSize] = vector.NewVec(*rsOfEachAccount[0].Vecs[idxOfSize].GetType())
   663  	outputBatch.Vecs[finalIdxOfComment], err = rsOfMoAccount.Vecs[idxOfComment].Dup(mp)
   664  	if err != nil {
   665  		return err
   666  	}
   667  
   668  	for _, bat := range rsOfEachAccount {
   669  		err = outputBatch.Vecs[finalIdxOfAdminName].UnionOne(bat.Vecs[idxOfAdminName], 0, mp)
   670  		if err != nil {
   671  			return err
   672  		}
   673  		err = outputBatch.Vecs[finalIdxOfDBCount].UnionOne(bat.Vecs[idxOfDBCount], 0, mp)
   674  		if err != nil {
   675  			return err
   676  		}
   677  		err = outputBatch.Vecs[finalIdxOfTableCount].UnionOne(bat.Vecs[idxOfTableCount], 0, mp)
   678  		if err != nil {
   679  			return err
   680  		}
   681  		if err != nil {
   682  			return err
   683  		}
   684  		err = outputBatch.Vecs[finalIdxOfSize].UnionOne(bat.Vecs[idxOfSize], 0, mp)
   685  		if err != nil {
   686  			return err
   687  		}
   688  	}
   689  	outputBatch.SetRowCount(rsOfMoAccount.RowCount())
   690  	return nil
   691  }