github.com/matrixorigin/matrixone@v0.7.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  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    21  	"github.com/matrixorigin/matrixone/pkg/defines"
    22  	"github.com/matrixorigin/matrixone/pkg/sql/parsers/tree"
    23  	"strings"
    24  )
    25  
    26  const (
    27  	getAllAccountInfoFormat = "select " +
    28  		"account_id as `account_id`, " +
    29  		"account_name as `account_name`, " +
    30  		"created_time as `created`, " +
    31  		"status as `status`, " +
    32  		"suspended_time as `suspended_time`, " +
    33  		"comments as `comment` " +
    34  		"from " +
    35  		"mo_catalog.mo_account " +
    36  		"%s" +
    37  		";"
    38  
    39  	getAccountInfoFormat = "select " +
    40  		"account_id as `account_id`, " +
    41  		"account_name as `account_name`, " +
    42  		"created_time as `created`, " +
    43  		"status as `status`, " +
    44  		"suspended_time as `suspended_time`, " +
    45  		"comments as `comment` " +
    46  		"from " +
    47  		"mo_catalog.mo_account " +
    48  		"where account_id = %d;"
    49  
    50  	// column index in the result set generated by
    51  	// the sql getAllAccountInfoFormat, getAccountInfoFormat
    52  	idxOfAccountId     = 0
    53  	idxOfAccountName   = 1
    54  	idxOfCreated       = 2
    55  	idxOfStatus        = 3
    56  	idxOfSuspendedTime = 4
    57  	idxOfComment       = 5
    58  
    59  	getTableStatsFormat = "select " +
    60  		"( select " +
    61  		"        mu2.user_name as `admin_name` " +
    62  		"  from mo_catalog.mo_user as mu2 join " +
    63  		"      ( select " +
    64  		"              min(user_id) as `min_user_id` " +
    65  		"        from mo_catalog.mo_user " +
    66  		"      ) as mu1 on mu2.user_id = mu1.min_user_id " +
    67  		") as `admin_name`, " +
    68  		"count(distinct mt.reldatabase) as `db_count`, " +
    69  		"count(distinct mt.relname) as `table_count`, " +
    70  		"sum(mo_table_rows(mt.reldatabase,mt.relname)) as `row_count`, " +
    71  		"cast(sum(mo_table_size(mt.reldatabase,mt.relname))/1048576  as decimal(29,3)) as `size` " +
    72  		"from " +
    73  		"mo_catalog.mo_tables as mt " +
    74  		"where mt.account_id = %d;"
    75  
    76  	// column index in the result set generated by
    77  	// the sql getTableStatsFormat
    78  	idxOfAdminName  = 0
    79  	idxOfDBCount    = 1
    80  	idxOfTableCount = 2
    81  	idxOfRowCount   = 3
    82  	idxOfSize       = 4
    83  
    84  	// column index in the result set of the statement show accounts
    85  	finalIdxOfAccountName   = 0
    86  	finalIdxOfAdminName     = 1
    87  	finalIdxOfCreated       = 2
    88  	finalIdxOfStatus        = 3
    89  	finalIdxOfSuspendedTime = 4
    90  	finalIdxOfDBCount       = 5
    91  	finalIdxOfTableCount    = 6
    92  	finalIdxOfRowCount      = 7
    93  	finalIdxOfSize          = 8
    94  	finalIdxOfComment       = 9
    95  	finalColumnCount        = 10
    96  )
    97  
    98  func getSqlForAllAccountInfo(like *tree.ComparisonExpr) string {
    99  	var likePattern = ""
   100  	if like != nil {
   101  		likePattern = strings.TrimSpace(like.Right.String())
   102  	}
   103  	likeClause := ""
   104  	if len(likePattern) != 0 {
   105  		likeClause = fmt.Sprintf("where account_name like '%s'", likePattern)
   106  	}
   107  	return fmt.Sprintf(getAllAccountInfoFormat, likeClause)
   108  }
   109  
   110  func getSqlForAccountInfo(accountId uint64) string {
   111  	return fmt.Sprintf(getAccountInfoFormat, accountId)
   112  }
   113  
   114  func getSqlForTableStats(accountId uint64) string {
   115  	return fmt.Sprintf(getTableStatsFormat, accountId)
   116  }
   117  
   118  func doShowAccounts(ctx context.Context, ses *Session, sa *tree.ShowAccounts) error {
   119  	var err error
   120  	var sql string
   121  	var accountIds []uint64
   122  	var rsOfMoAccount *MysqlResultSet
   123  	var rsOfEachAccount []*MysqlResultSet
   124  	var tempRS, outputRS *MysqlResultSet
   125  	outputRS = &MysqlResultSet{}
   126  
   127  	bh := ses.GetBackgroundExec(ctx)
   128  	defer bh.Close()
   129  
   130  	account := ses.GetTenantInfo()
   131  
   132  	err = bh.Exec(ctx, "begin;")
   133  	if err != nil {
   134  		goto handleFailed
   135  	}
   136  
   137  	//step1: current account is sys or non-sys ?
   138  	//the result of the statement show accounts is different
   139  	//under the sys or non-sys.
   140  
   141  	//step2:
   142  	if account.IsSysTenant() {
   143  		//under sys account
   144  		//step2.1: get all account info from mo_account;
   145  
   146  		sql = getSqlForAllAccountInfo(sa.Like)
   147  		rsOfMoAccount, accountIds, err = getAccountInfo(ctx, bh, sql, true)
   148  		if err != nil {
   149  			goto handleFailed
   150  		}
   151  
   152  		//step2.2: for all accounts, switch into an account,
   153  		//get the admin_name, table size and table rows.
   154  		for _, id := range accountIds {
   155  			newCtx := context.WithValue(ctx, defines.TenantIDKey{}, uint32(id))
   156  			tempRS, err = getTableStats(newCtx, bh, id)
   157  			if err != nil {
   158  				goto handleFailed
   159  			}
   160  			rsOfEachAccount = append(rsOfEachAccount, tempRS)
   161  		}
   162  
   163  		//step3: merge result set from mo_account and table stats from each account
   164  		err = mergeOutputResult(ctx, outputRS, rsOfMoAccount, rsOfEachAccount)
   165  		if err != nil {
   166  			goto handleFailed
   167  		}
   168  		ses.SetMysqlResultSet(outputRS)
   169  	} else {
   170  		if sa.Like != nil {
   171  			err = moerr.NewInternalError(ctx, "only sys account can use LIKE clause")
   172  			goto handleFailed
   173  		}
   174  		//under non-sys account
   175  		//step2.1: switch into the sys account, get the account info
   176  		newCtx := context.WithValue(ctx, defines.TenantIDKey{}, uint32(sysAccountID))
   177  		sql = getSqlForAccountInfo(uint64(account.GetTenantID()))
   178  		rsOfMoAccount, _, err = getAccountInfo(newCtx, bh, sql, false)
   179  		if err != nil {
   180  			goto handleFailed
   181  		}
   182  
   183  		//step2.2: get the admin_name, table size and table rows.
   184  		tempRS, err = getTableStats(ctx, bh, uint64(account.GetTenantID()))
   185  		if err != nil {
   186  			goto handleFailed
   187  		}
   188  
   189  		err = mergeOutputResult(ctx, outputRS, rsOfMoAccount, []*MysqlResultSet{tempRS})
   190  		if err != nil {
   191  			goto handleFailed
   192  		}
   193  		ses.SetMysqlResultSet(outputRS)
   194  	}
   195  
   196  	//step3: make a response packet.
   197  	err = bh.Exec(ctx, "commit;")
   198  	if err != nil {
   199  		goto handleFailed
   200  	}
   201  
   202  	return err
   203  
   204  handleFailed:
   205  	//ROLLBACK the transaction
   206  	rbErr := bh.Exec(ctx, "rollback;")
   207  	if rbErr != nil {
   208  		return rbErr
   209  	}
   210  	return err
   211  }
   212  
   213  // getAccountInfo gets account info from mo_account under sys account
   214  func getAccountInfo(ctx context.Context,
   215  	bh BackgroundExec,
   216  	sql string,
   217  	returnAccountIds bool) (*MysqlResultSet, []uint64, error) {
   218  	var err error
   219  	var accountIds []uint64
   220  	var accountId uint64
   221  	var erArray []ExecResult
   222  	var rsOfMoAccount *MysqlResultSet
   223  	var ok bool
   224  
   225  	bh.ClearExecResultSet()
   226  	err = bh.Exec(ctx, sql)
   227  	if err != nil {
   228  		return nil, nil, err
   229  	}
   230  
   231  	erArray, err = getResultSet(ctx, bh)
   232  	if err != nil {
   233  		return nil, nil, err
   234  	}
   235  
   236  	if execResultArrayHasData(erArray) {
   237  		if returnAccountIds {
   238  			for i := uint64(0); i < erArray[0].GetRowCount(); i++ {
   239  				//account_id
   240  				accountId, err = erArray[0].GetUint64(ctx, i, 0)
   241  				if err != nil {
   242  					return nil, nil, err
   243  				}
   244  
   245  				accountIds = append(accountIds, accountId)
   246  			}
   247  		}
   248  
   249  		rsOfMoAccount, ok = erArray[0].(*MysqlResultSet)
   250  		if !ok {
   251  			return nil, nil, moerr.NewInternalError(ctx, "convert result set failed.")
   252  		}
   253  	} else {
   254  		return nil, nil, moerr.NewInternalError(ctx, "get data from mo_account failed")
   255  	}
   256  	return rsOfMoAccount, accountIds, err
   257  }
   258  
   259  // getTableStats gets the table statistics for the account
   260  func getTableStats(ctx context.Context, bh BackgroundExec, accountId uint64) (*MysqlResultSet, error) {
   261  	var sql string
   262  	var err error
   263  	var erArray []ExecResult
   264  	var rs *MysqlResultSet
   265  	var ok bool
   266  	sql = getSqlForTableStats(accountId)
   267  	bh.ClearExecResultSet()
   268  	err = bh.Exec(ctx, sql)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	erArray, err = getResultSet(ctx, bh)
   274  	if err != nil {
   275  		return nil, err
   276  	}
   277  
   278  	if execResultArrayHasData(erArray) {
   279  		rs, ok = erArray[0].(*MysqlResultSet)
   280  		if !ok {
   281  			err = moerr.NewInternalError(ctx, "convert result set failed")
   282  			return nil, err
   283  		}
   284  	} else {
   285  		err = moerr.NewInternalError(ctx, "get table stats failed")
   286  		return nil, err
   287  	}
   288  	return rs, err
   289  }
   290  
   291  // mergeOutputResult merges the result set from mo_account and the table status
   292  // into the final output format
   293  func mergeOutputResult(ctx context.Context, outputRS *MysqlResultSet, rsOfMoAccount *MysqlResultSet, rsOfEachAccount []*MysqlResultSet) error {
   294  	var err error
   295  	outputColumns := make([]Column, finalColumnCount)
   296  
   297  	outputColumns[finalIdxOfAccountName], err = rsOfMoAccount.GetColumn(ctx, idxOfAccountName)
   298  	if err != nil {
   299  		return err
   300  	}
   301  	outputColumns[finalIdxOfAdminName], err = rsOfEachAccount[0].GetColumn(ctx, idxOfAdminName)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	outputColumns[finalIdxOfCreated], err = rsOfMoAccount.GetColumn(ctx, idxOfCreated)
   306  	if err != nil {
   307  		return err
   308  	}
   309  	outputColumns[finalIdxOfStatus], err = rsOfMoAccount.GetColumn(ctx, idxOfStatus)
   310  	if err != nil {
   311  		return err
   312  	}
   313  	outputColumns[finalIdxOfSuspendedTime], err = rsOfMoAccount.GetColumn(ctx, idxOfSuspendedTime)
   314  	if err != nil {
   315  		return err
   316  	}
   317  	outputColumns[finalIdxOfDBCount], err = rsOfEachAccount[0].GetColumn(ctx, idxOfDBCount)
   318  	if err != nil {
   319  		return err
   320  	}
   321  	outputColumns[finalIdxOfTableCount], err = rsOfEachAccount[0].GetColumn(ctx, idxOfTableCount)
   322  	if err != nil {
   323  		return err
   324  	}
   325  	outputColumns[finalIdxOfRowCount], err = rsOfEachAccount[0].GetColumn(ctx, idxOfRowCount)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	outputColumns[finalIdxOfSize], err = rsOfEachAccount[0].GetColumn(ctx, idxOfSize)
   330  	if err != nil {
   331  		return err
   332  	}
   333  	outputColumns[finalIdxOfComment], err = rsOfMoAccount.GetColumn(ctx, idxOfComment)
   334  	if err != nil {
   335  		return err
   336  	}
   337  	for _, o := range outputColumns {
   338  		outputRS.AddColumn(o)
   339  	}
   340  	for i, rs := range rsOfEachAccount {
   341  		outputRow := make([]interface{}, finalColumnCount)
   342  		outputRow[finalIdxOfAccountName], err = rsOfMoAccount.GetValue(ctx, uint64(i), idxOfAccountName)
   343  		if err != nil {
   344  			return err
   345  		}
   346  		outputRow[finalIdxOfAdminName], err = rs.GetValue(ctx, 0, idxOfAdminName)
   347  		if err != nil {
   348  			return err
   349  		}
   350  		outputRow[finalIdxOfCreated], err = rsOfMoAccount.GetValue(ctx, uint64(i), idxOfCreated)
   351  		if err != nil {
   352  			return err
   353  		}
   354  		outputRow[finalIdxOfStatus], err = rsOfMoAccount.GetValue(ctx, uint64(i), idxOfStatus)
   355  		if err != nil {
   356  			return err
   357  		}
   358  		outputRow[finalIdxOfSuspendedTime], err = rsOfMoAccount.GetValue(ctx, uint64(i), idxOfSuspendedTime)
   359  		if err != nil {
   360  			return err
   361  		}
   362  		outputRow[finalIdxOfDBCount], err = rs.GetValue(ctx, 0, idxOfDBCount)
   363  		if err != nil {
   364  			return err
   365  		}
   366  		outputRow[finalIdxOfTableCount], err = rs.GetValue(ctx, 0, idxOfTableCount)
   367  		if err != nil {
   368  			return err
   369  		}
   370  		outputRow[finalIdxOfRowCount], err = rs.GetValue(ctx, 0, idxOfRowCount)
   371  		if err != nil {
   372  			return err
   373  		}
   374  		outputRow[finalIdxOfSize], err = rs.GetValue(ctx, 0, idxOfSize)
   375  		if err != nil {
   376  			return err
   377  		}
   378  		outputRow[finalIdxOfComment], err = rsOfMoAccount.GetValue(ctx, uint64(i), idxOfComment)
   379  		if err != nil {
   380  			return err
   381  		}
   382  		outputRS.AddRow(outputRow)
   383  	}
   384  	return err
   385  }