diff --git a/pkg/frontend/mysql_cmd_executor.go b/pkg/frontend/mysql_cmd_executor.go index d08d154a27e0d..70d932255cf81 100644 --- a/pkg/frontend/mysql_cmd_executor.go +++ b/pkg/frontend/mysql_cmd_executor.go @@ -430,7 +430,7 @@ func handleShowTableStatus(ses *Session, execCtx *ExecCtx, stmt *tree.ShowTableS // get table stats var rets []ExecResult - if rets, err = executeSQLInBackgroundSession(ctx, bh, sqlBuilder.String()); err != nil { + if rets, err = ExeSqlInBgSes(ctx, bh, sqlBuilder.String()); err != nil { return } @@ -466,7 +466,7 @@ func handleShowTableStatus(ses *Session, execCtx *ExecCtx, stmt *tree.ShowTableS sqlBuilder.WriteString(fmt.Sprintf("row('%s', '%s')", dbName, tblName)) } sqlBuilder.WriteString(") as tmp(db, tbl)") - rets, err := executeSQLInBackgroundSession(ctx, bh, sqlBuilder.String()) + rets, err := ExeSqlInBgSes(ctx, bh, sqlBuilder.String()) if err != nil { return nil, err } @@ -485,6 +485,54 @@ func handleShowTableStatus(ses *Session, execCtx *ExecCtx, stmt *tree.ShowTableS return sizes, nil } + // For some system tables (for example `system.statement_info`), tenant queries + // are rewritten to sys account with account-level filtering. mo_table_rows/size + // does not reflect that rewritten visibility in all cases, so fallback to + // count(*) for Rows when mo_table_rows is empty/zero to keep SHOW TABLE STATUS + // consistent with SELECT semantics without paying the extra cost when stats are + // already populated. + getSpecialTableRows := func(tblNames []string, tableRows map[string]int64) (map[string]int64, error) { + rows := make(map[string]int64) + if len(tblNames) == 0 { + return rows, nil + } + accountId, err := defines.GetAccountId(ctx) + if err != nil { + return nil, err + } + if accountId == sysAccountID { + return rows, nil + } + + escapeIdent := func(name string) string { + return strings.ReplaceAll(name, "`", "``") + } + + for _, tblName := range tblNames { + if !ShouldSwitchToSysAccount(dbName, tblName) { + continue + } + if currentRows, ok := tableRows[tblName]; ok && currentRows > 0 { + continue + } + sql := fmt.Sprintf("select count(*) from `%s`.`%s`", escapeIdent(dbName), escapeIdent(tblName)) + rets, err := ExeSqlInBgSes(ctx, bh, sql) + if err != nil { + return nil, err + } + if !execResultArrayHasData(rets) { + continue + } + cnt, err := rets[0].GetInt64(ctx, 0, 0) + if err != nil { + return nil, err + } + rows[tblName] = cnt + } + + return rows, nil + } + var tblNames []string var tblIdxes []int // baseTable -> index table names (secondary/unique only), for Index_length @@ -544,6 +592,10 @@ func handleShowTableStatus(ses *Session, execCtx *ExecCtx, stmt *tree.ShowTableS if err != nil { return err } + specialRows, err := getSpecialTableRows(tblNames, rows) + if err != nil { + return err + } indexLengths := make(map[string]int64, len(tblNames)) if len(indexTableSet) > 0 { @@ -562,7 +614,11 @@ func handleShowTableStatus(ses *Session, execCtx *ExecCtx, stmt *tree.ShowTableS for i, tblName := range tblNames { idx := tblIdxes[i] - ses.data[idx][3] = rows[tblName] + if cnt, ok := specialRows[tblName]; ok { + ses.data[idx][3] = cnt + } else { + ses.data[idx][3] = rows[tblName] + } if rows[tblName] > 0 { ses.data[idx][4] = sizes[tblName] / rows[tblName] } else { diff --git a/pkg/frontend/mysql_cmd_executor_test.go b/pkg/frontend/mysql_cmd_executor_test.go index b4193c06f0a84..e8943b6380486 100644 --- a/pkg/frontend/mysql_cmd_executor_test.go +++ b/pkg/frontend/mysql_cmd_executor_test.go @@ -19,6 +19,7 @@ import ( "encoding/binary" "fmt" "io" + "strings" "sync/atomic" "testing" "time" @@ -1775,6 +1776,61 @@ func Test_run_panic(t *testing.T) { func Test_handleShowTableStatus(t *testing.T) { ctx := defines.AttachAccountId(context.TODO(), 1) + newShowTableStatusFixture := func( + reqCtx context.Context, + ctrl *gomock.Controller, + tableType, tableName string, + roleID uint32, + tenant *TenantInfo, + dbName string, + ) (*Session, *ExecCtx, *tree.ShowTableStatus) { + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableDef(gomock.Any()).Return(&plan.TableDef{TableType: tableType}).AnyTimes() + + database := mock_frontend.NewMockDatabase(ctrl) + database.EXPECT().IsSubscription(gomock.Any()).Return(false).AnyTimes() + database.EXPECT().Relation(gomock.Any(), gomock.Any(), nil).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().New(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + eng.EXPECT().Database(gomock.Any(), gomock.Any(), nil).Return(database, nil).AnyTimes() + + txnOperator := mock_frontend.NewMockTxnOperator(ctrl) + txnOperator.EXPECT().Commit(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().Rollback(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().Status().Return(txn.TxnStatus_Active).AnyTimes() + txnOperator.EXPECT().EnterRunSqlWithTokenAndSQL(gomock.Any(), gomock.Any()).Return(uint64(0)).AnyTimes() + txnOperator.EXPECT().ExitRunSqlWithToken(gomock.Any()).Return().AnyTimes() + + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().New(gomock.Any(), gomock.Any()).Return(txnOperator, nil).AnyTimes() + + sv, err := getSystemVariables("test/system_vars_config.toml") + if err != nil { + t.Error(err) + } + pu := config.NewParameterUnit(sv, eng, txnClient, nil) + pu.SV.SkipCheckUser = true + setPu("", pu) + setSessionAlloc("", NewLeakCheckAllocator()) + ioses, err := NewIOSession(&testConn{}, pu, "") + convey.So(err, convey.ShouldBeNil) + pu.StorageEngine = eng + pu.TxnClient = txnClient + proto := NewMysqlClientProtocol("", 0, ioses, 1024, pu.SV) + + ses := NewSession(reqCtx, "", proto, nil) + ses.SetTenantInfo(tenant) + ses.mrs = &MysqlResultSet{} + ses.SetDatabaseName(dbName) + ses.data = [][]interface{}{{ + []byte(tableName), nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, roleID, nil, + }} + proto.SetSession(ses) + return ses, newTestExecCtx(reqCtx, ctrl), &tree.ShowTableStatus{DbName: dbName} + } + convey.Convey("handleShowTableStatus succ", t, func() { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -1834,6 +1890,361 @@ func Test_handleShowTableStatus(t *testing.T) { ec = newTestExecCtx(context.Background(), ctrl) convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldNotBeNil) }) + + convey.Convey("handleShowTableStatus statement_info fallback rows", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + relation := mock_frontend.NewMockRelation(ctrl) + relation.EXPECT().GetTableDef(gomock.Any()).Return(&plan.TableDef{ + TableType: catalog.SystemOrdinaryRel, + }).AnyTimes() + + database := mock_frontend.NewMockDatabase(ctrl) + database.EXPECT().IsSubscription(gomock.Any()).Return(false).AnyTimes() + database.EXPECT().Relation(gomock.Any(), gomock.Any(), nil).Return(relation, nil).AnyTimes() + + eng := mock_frontend.NewMockEngine(ctrl) + eng.EXPECT().New(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() + eng.EXPECT().Database(gomock.Any(), gomock.Any(), nil).Return(database, nil).AnyTimes() + + txnOperator := mock_frontend.NewMockTxnOperator(ctrl) + txnOperator.EXPECT().Commit(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().Rollback(gomock.Any()).Return(nil).AnyTimes() + txnOperator.EXPECT().Status().Return(txn.TxnStatus_Active).AnyTimes() + txnOperator.EXPECT().EnterRunSqlWithTokenAndSQL(gomock.Any(), gomock.Any()).Return(uint64(0)).AnyTimes() + txnOperator.EXPECT().ExitRunSqlWithToken(gomock.Any()).Return().AnyTimes() + + txnClient := mock_frontend.NewMockTxnClient(ctrl) + txnClient.EXPECT().New(gomock.Any(), gomock.Any()).Return(txnOperator, nil).AnyTimes() + + sv, err := getSystemVariables("test/system_vars_config.toml") + if err != nil { + t.Error(err) + } + pu := config.NewParameterUnit(sv, eng, txnClient, nil) + pu.SV.SkipCheckUser = true + setPu("", pu) + setSessionAlloc("", NewLeakCheckAllocator()) + ioses, err := NewIOSession(&testConn{}, pu, "") + convey.So(err, convey.ShouldBeNil) + pu.StorageEngine = eng + pu.TxnClient = txnClient + proto := NewMysqlClientProtocol("", 0, ioses, 1024, pu.SV) + + ses := NewSession(ctx, "", proto, nil) + tenant := &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + } + ses.SetTenantInfo(tenant) + ses.mrs = &MysqlResultSet{} + ses.SetDatabaseName(catalog.MO_SYSTEM) + ses.data = [][]interface{}{{ + []byte(catalog.MO_STATEMENT), nil, nil, nil, nil, nil, nil, nil, nil, + nil, nil, nil, nil, nil, nil, nil, nil, uint32(moAdminRoleID), nil, + }} + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_STATEMENT, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(0), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(128), nil).AnyTimes() + + countRet := mock_frontend.NewMockExecResult(ctrl) + countRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + countRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(0)).Return(int64(7), nil).AnyTimes() + + calls := 0 + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + calls++ + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*) from `system`.`statement_info`"): + return []ExecResult{countRet}, nil + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + proto.SetSession(ses) + ec := newTestExecCtx(ctx, ctrl) + shv := &tree.ShowTableStatus{DbName: catalog.MO_SYSTEM} + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldBeNil) + convey.So(calls, convey.ShouldEqual, 2) + convey.So(ses.data[0][3], convey.ShouldEqual, int64(7)) + convey.So(ses.data[0][4], convey.ShouldEqual, int64(0)) + convey.So(ses.data[0][5], convey.ShouldEqual, int64(128)) + }) + + convey.Convey("handleShowTableStatus statement_info with rows should skip fallback", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses, ec, shv := newShowTableStatusFixture( + ctx, ctrl, + catalog.SystemOrdinaryRel, catalog.MO_STATEMENT, + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_STATEMENT, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(3), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(90), nil).AnyTimes() + + countCalls := 0 + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*) from `system`.`statement_info`"): + countCalls++ + return nil, moerr.NewInternalErrorf(reqCtx, "count query should not be called, sql: %s", sql) + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldBeNil) + convey.So(countCalls, convey.ShouldEqual, 0) + convey.So(ses.data[0][3], convey.ShouldEqual, int64(3)) + convey.So(ses.data[0][4], convey.ShouldEqual, int64(30)) + convey.So(ses.data[0][5], convey.ShouldEqual, int64(90)) + }) + + convey.Convey("handleShowTableStatus non-special table should not fallback", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses, ec, shv := newShowTableStatusFixture( + ctx, ctrl, + catalog.SystemOrdinaryRel, "not_special", + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return("not_special", nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(5), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(100), nil).AnyTimes() + + countCalls := 0 + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*)"): + countCalls++ + return nil, moerr.NewInternalErrorf(reqCtx, "count query should not be called, sql: %s", sql) + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldBeNil) + convey.So(countCalls, convey.ShouldEqual, 0) + convey.So(ses.data[0][3], convey.ShouldEqual, int64(5)) + convey.So(ses.data[0][4], convey.ShouldEqual, int64(20)) + convey.So(ses.data[0][5], convey.ShouldEqual, int64(100)) + }) + + convey.Convey("handleShowTableStatus statement_info fallback empty result should keep mo_table_rows", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses, ec, shv := newShowTableStatusFixture( + ctx, ctrl, + catalog.SystemOrdinaryRel, catalog.MO_STATEMENT, + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_STATEMENT, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(0), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(90), nil).AnyTimes() + + countRet := mock_frontend.NewMockExecResult(ctrl) + countRet.EXPECT().GetRowCount().Return(uint64(0)).AnyTimes() + + calls := 0 + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + calls++ + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*) from `system`.`statement_info`"): + return []ExecResult{countRet}, nil + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldBeNil) + convey.So(calls, convey.ShouldEqual, 2) + convey.So(ses.data[0][3], convey.ShouldEqual, int64(0)) + convey.So(ses.data[0][4], convey.ShouldEqual, int64(0)) + convey.So(ses.data[0][5], convey.ShouldEqual, int64(90)) + }) + + convey.Convey("handleShowTableStatus statement_info fallback count query error", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses, ec, shv := newShowTableStatusFixture( + ctx, ctrl, + catalog.SystemOrdinaryRel, catalog.MO_STATEMENT, + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_STATEMENT, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(0), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(90), nil).AnyTimes() + + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*) from `system`.`statement_info`"): + return nil, moerr.NewInternalErrorf(reqCtx, "mock count query error") + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldNotBeNil) + }) + + convey.Convey("handleShowTableStatus statement_info fallback count decode error", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ses, ec, shv := newShowTableStatusFixture( + ctx, ctrl, + catalog.SystemOrdinaryRel, catalog.MO_STATEMENT, + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "acc_fallback", + TenantID: 1, + User: "admin", + DefaultRole: accountAdminRoleName, + DefaultRoleID: moAdminRoleID, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_STATEMENT, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(0), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(90), nil).AnyTimes() + + countRet := mock_frontend.NewMockExecResult(ctrl) + countRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + countRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(0)).Return(int64(0), moerr.NewInternalErrorNoCtx("mock decode error")).AnyTimes() + + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*) from `system`.`statement_info`"): + return []ExecResult{countRet}, nil + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldNotBeNil) + }) + + convey.Convey("handleShowTableStatus sys account should skip special rows fallback", t, func() { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + sysCtx := defines.AttachAccountId(context.TODO(), sysAccountID) + ses, ec, shv := newShowTableStatusFixture( + sysCtx, ctrl, + catalog.SystemOrdinaryRel, catalog.MO_DATABASE, + uint32(moAdminRoleID), + &TenantInfo{ + Tenant: "sys", + TenantID: 0, + User: DefaultTenantMoAdmin, + }, + catalog.MO_SYSTEM, + ) + + statsRet := mock_frontend.NewMockExecResult(ctrl) + statsRet.EXPECT().GetRowCount().Return(uint64(1)).AnyTimes() + statsRet.EXPECT().GetString(gomock.Any(), uint64(0), uint64(0)).Return(catalog.MO_DATABASE, nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(1)).Return(int64(11), nil).AnyTimes() + statsRet.EXPECT().GetInt64(gomock.Any(), uint64(0), uint64(2)).Return(int64(121), nil).AnyTimes() + + countCalls := 0 + execStub := gostub.Stub(&ExeSqlInBgSes, func(reqCtx context.Context, bh BackgroundExec, sql string) ([]ExecResult, error) { + switch { + case strings.Contains(sql, "mo_table_rows(db, tbl)"): + return []ExecResult{statsRet}, nil + case strings.Contains(sql, "select count(*)"): + countCalls++ + return nil, moerr.NewInternalErrorf(reqCtx, "count query should not be called, sql: %s", sql) + default: + return nil, moerr.NewInternalErrorf(reqCtx, "unexpected sql in test: %s", sql) + } + }) + defer execStub.Reset() + + convey.So(handleShowTableStatus(ses, ec, shv), convey.ShouldBeNil) + convey.So(countCalls, convey.ShouldEqual, 0) + convey.So(ses.data[0][3], convey.ShouldEqual, int64(11)) + convey.So(ses.data[0][4], convey.ShouldEqual, int64(11)) + convey.So(ses.data[0][5], convey.ShouldEqual, int64(121)) + }) } func Test_checkModify(t *testing.T) { diff --git a/test/distributed/cases/dml/show/show7.result b/test/distributed/cases/dml/show/show7.result new file mode 100644 index 0000000000000..551ad760c843b --- /dev/null +++ b/test/distributed/cases/dml/show/show7.result @@ -0,0 +1,22 @@ +drop account if exists show_table_status_acc_10163; +create account show_table_status_acc_10163 admin_name = 'admin' identified by '111'; +select 1; +1 +1 +select 2; +2 +2 +select 3; +3 +3 +select case when count(*) > 0 then 0 else sleep(15) end as wait_for_statement_info_ready from system.statement_info; +wait_for_statement_info_ready +0 +select count(*) > 0 as has_data from system.statement_info; +has_data +1 +show table status from system like 'statement_info'; +-- @regex("(?m)^statement_info.*Tae.*Dynamic.*[1-9][0-9]*.*$",true) +➤ Name[12,-1,0] ¦ Engine[12,-1,0] ¦ Row_format[12,-1,0] ¦ Rows[-5,64,0] ¦ Avg_row_length[-5,64,0] ¦ Data_length[-5,64,0] ¦ Max_data_length[-5,64,0] ¦ Index_length[-5,64,0] ¦ Data_free[12,-1,0] ¦ Auto_increment[-5,64,0] ¦ Create_time[93,64,0] ¦ Update_time[12,-1,0] ¦ Check_time[12,-1,0] ¦ Collation[12,-1,0] ¦ Checksum[12,-1,0] ¦ Create_options[1,0,0] ¦ Comment[12,-1,0] ¦ Role_id[4,32,0] ¦ Role_name[12,-1,0] 𝄀 +statement_info ¦ Tae ¦ Dynamic ¦ 1 ¦ 0 ¦ 0 ¦ 0 ¦ 0 ¦ NULL ¦ 1 ¦ 2026-02-28 16:08:26 ¦ NULL ¦ NULL ¦ utf8mb4_bin ¦ NULL ¦ ¦ record each statement and stats info[mo_no_del_hint] ¦ 2 ¦ accountadmin +drop account show_table_status_acc_10163; diff --git a/test/distributed/cases/dml/show/show7.sql b/test/distributed/cases/dml/show/show7.sql new file mode 100644 index 0000000000000..a19876912917a --- /dev/null +++ b/test/distributed/cases/dml/show/show7.sql @@ -0,0 +1,17 @@ +drop account if exists show_table_status_acc_10163; +create account show_table_status_acc_10163 admin_name = 'admin' identified by '111'; + +-- @session:id=1&user=show_table_status_acc_10163:admin&password=111 +select 1; +select 2; +select 3; +select case when count(*) > 0 then 0 else sleep(15) end as wait_for_statement_info_ready from system.statement_info; + +select count(*) > 0 as has_data from system.statement_info; +-- @ignore:0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18 +-- @metacmp(false) +-- @regex("(?m)^statement_info.*Tae.*Dynamic.*[1-9][0-9]*.*$",true) +show table status from system like 'statement_info'; + +-- @session +drop account show_table_status_acc_10163;