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 }