From 009c11254d6c19750f60a5ab2e42211d83deb75e Mon Sep 17 00:00:00 2001 From: crazycs Date: Mon, 13 Jul 2020 13:57:37 +0800 Subject: [PATCH] expression: add header for tidb_decode_plan result (#18440) --- expression/integration_test.go | 28 ++++++++++-------- infoschema/tables_test.go | 30 +++++++++++-------- util/plancodec/codec.go | 54 ++++++++++++++++++++++++++-------- 3 files changed, 73 insertions(+), 39 deletions(-) diff --git a/expression/integration_test.go b/expression/integration_test.go index 685bacfd4bb56..0614dc104e15d 100755 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -4399,24 +4399,26 @@ func (s *testIntegrationSuite) TestTiDBDecodePlanFunc(c *C) { "8xNwozCTFfMTcJMQkwCWx0HVlATlVMTCksIG5vdChpc251bGwVHAApUhcAUDIpKQo0CTEwXzE2CTEJMTAwMDAJdAHB2Dp0MSwgcmFuZ2U6Wy1p" + "bmYsK2luZl0sIGtlZXAgb3JkZXI6ZmFsc2UsIHN0YXRzOnBzZXVkbwoFtgAyAZcEMAk6tgAEMjAFtgQyMDq2AAg5LCBmtgAAMFa3AAA5FbcAO" + "T63AAAyzrcA')").Check(testkit.Rows("" + - "\tStreamAgg_13 \troot\t1 \tfuncs:count(1)\n" + - "\t└─HashJoin_14 \troot\t0 \tinner join, inner:TableReader_21, equal:[eq(Column#1, Column#9) eq(Column#2, Column#10)]\n" + - "\t ├─TableReader_18 \troot\t0 \tdata:Selection_17\n" + - "\t │ └─Selection_17 \tcop \t0 \tlt(Column#1, NULL), not(isnull(Column#1)), not(isnull(Column#2))\n" + - "\t │ └─TableScan_16\tcop \t10000\ttable:t1, range:[-inf,+inf], keep order:false, stats:pseudo\n" + - "\t └─TableReader_21 \troot\t0 \tdata:Selection_20\n" + - "\t └─Selection_20 \tcop \t0 \tlt(Column#9, NULL), not(isnull(Column#10)), not(isnull(Column#9))\n" + - "\t └─TableScan_19\tcop \t10000\ttable:t2, range:[-inf,+inf], keep order:false, stats:pseudo")) + "\tid \ttask\testRows\toperator info\n" + + "\tStreamAgg_13 \troot\t1 \tfuncs:count(1)\n" + + "\t└─HashJoin_14 \troot\t0 \tinner join, inner:TableReader_21, equal:[eq(Column#1, Column#9) eq(Column#2, Column#10)]\n" + + "\t ├─TableReader_18 \troot\t0 \tdata:Selection_17\n" + + "\t │ └─Selection_17 \tcop \t0 \tlt(Column#1, NULL), not(isnull(Column#1)), not(isnull(Column#2))\n" + + "\t │ └─TableScan_16\tcop \t10000 \ttable:t1, range:[-inf,+inf], keep order:false, stats:pseudo\n" + + "\t └─TableReader_21 \troot\t0 \tdata:Selection_20\n" + + "\t └─Selection_20 \tcop \t0 \tlt(Column#9, NULL), not(isnull(Column#10)), not(isnull(Column#9))\n" + + "\t └─TableScan_19\tcop \t10000 \ttable:t2, range:[-inf,+inf], keep order:false, stats:pseudo")) tk.MustQuery("select tidb_decode_plan('rwPwcTAJNV8xNAkwCTEJZnVuY3M6bWF4KHRlc3QudC5hKS0+Q29sdW1uIzQJMQl0aW1lOj" + "IyMy45MzXCtXMsIGxvb3BzOjIJMTI4IEJ5dGVzCU4vQQoxCTE2XzE4CTAJMQlvZmZzZXQ6MCwgY291bnQ6MQkxCQlHFDE4LjQyMjJHAAhOL0" + "EBBCAKMgkzMl8yOAkBlEBpbmRleDpMaW1pdF8yNwkxCQ0+DDYuODUdPSwxLCBycGMgbnVtOiANDAUpGDE1MC44MjQFKjhwcm9jIGtleXM6MA" + "kxOTgdsgAzAbIAMgFearIAFDU3LjM5NgVKAGwN+BGxIDQJMTNfMjYJMQGgHGFibGU6dCwgCbqwaWR4KGEpLCByYW5nZTooMCwraW5mXSwga2" + "VlcCBvcmRlcjp0cnVlLCBkZXNjAT8kaW1lOjU2LjY2MR1rJDEJTi9BCU4vQQo=')").Check(testkit.Rows("" + - "\tStreamAgg_14 \troot\t1\tfuncs:max(test.t.a)->Column#4 \t1\ttime:223.935µs, loops:2 \t128 Bytes\tN/A\n" + - "\t└─Limit_18 \troot\t1\toffset:0, count:1 \t1\ttime:218.422µs, loops:2 \tN/A \tN/A\n" + - "\t └─IndexReader_28 \troot\t1\tindex:Limit_27 \t1\ttime:216.85µs, loops:1, rpc num: 1, rpc time:150.824µs, proc keys:0\t198 Bytes\tN/A\n" + - "\t └─Limit_27 \tcop \t1\toffset:0, count:1 \t1\ttime:57.396µs, loops:2 \tN/A \tN/A\n" + - "\t └─IndexScan_26\tcop \t1\ttable:t, index:idx(a), range:(0,+inf], keep order:true, desc\t1\ttime:56.661µs, loops:1 \tN/A \tN/A")) + "\tid \ttask\testRows\toperator info \tactRows\texecution info \tmemory \tdisk\n" + + "\tStreamAgg_14 \troot\t1 \tfuncs:max(test.t.a)->Column#4 \t1 \ttime:223.935µs, loops:2 \t128 Bytes\tN/A\n" + + "\t└─Limit_18 \troot\t1 \toffset:0, count:1 \t1 \ttime:218.422µs, loops:2 \tN/A \tN/A\n" + + "\t └─IndexReader_28 \troot\t1 \tindex:Limit_27 \t1 \ttime:216.85µs, loops:1, rpc num: 1, rpc time:150.824µs, proc keys:0\t198 Bytes\tN/A\n" + + "\t └─Limit_27 \tcop \t1 \toffset:0, count:1 \t1 \ttime:57.396µs, loops:2 \tN/A \tN/A\n" + + "\t └─IndexScan_26\tcop \t1 \ttable:t, index:idx(a), range:(0,+inf], keep order:true, desc\t1 \ttime:56.661µs, loops:1 \tN/A \tN/A")) } func (s *testIntegrationSuite) TestTiDBInternalFunc(c *C) { diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index 3ae5a982ae1c0..a97cfa687e0aa 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -941,7 +941,7 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { tk.MustExec("create table p(a int primary key, b int)") for i := 1; i < 3; i++ { tk.MustQuery("select b from p where a=1") - expectedResult := fmt.Sprintf("%d \tPoint_Get_1\troot\t1\ttable:p, handle:1 %s", i, "test.p") + expectedResult := fmt.Sprintf("%d \tid \ttask\testRows\toperator info\n\tPoint_Get_1\troot\t1 \ttable:p, handle:1 %s", i, "test.p") // Also make sure that the plan digest is not empty tk.MustQuery(`select exec_count, plan, table_names from information_schema.statements_summary @@ -973,9 +973,10 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { max_prewrite_regions, avg_affected_rows, query_sample_text, plan from information_schema.statements_summary where digest_text like 'select * from t%'`, - ).Check(testkit.Rows("Select test test.t t:k 1 2 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tIndexLookUp_10\troot\t100\t\n" + - "\t├─IndexScan_8 \tcop \t100\ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableScan_9 \tcop \t100\ttable:t, keep order:false, stats:pseudo")) + ).Check(testkit.Rows("Select test test.t t:k 1 2 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask\testRows\toperator info\n" + + "\tIndexLookUp_10\troot\t100 \t\n" + + "\t├─IndexScan_8 \tcop \t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableScan_9 \tcop \t100 \ttable:t, keep order:false, stats:pseudo")) // select ... order by tk.MustQuery(`select stmt_type, schema_name, table_names, index_names, exec_count, sum_cop_task_num, avg_total_keys, @@ -993,9 +994,10 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { max_prewrite_regions, avg_affected_rows, query_sample_text, plan from information_schema.statements_summary where digest_text like 'select * from t%'`, - ).Check(testkit.Rows("Select test test.t t:k 2 4 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tIndexLookUp_10\troot\t100\t\n" + - "\t├─IndexScan_8 \tcop \t100\ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableScan_9 \tcop \t100\ttable:t, keep order:false, stats:pseudo")) + ).Check(testkit.Rows("Select test test.t t:k 2 4 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask\testRows\toperator info\n" + + "\tIndexLookUp_10\troot\t100 \t\n" + + "\t├─IndexScan_8 \tcop \t100 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableScan_9 \tcop \t100 \ttable:t, keep order:false, stats:pseudo")) // Disable it again. tk.MustExec("set global tidb_enable_stmt_summary = false") @@ -1042,9 +1044,10 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { max_prewrite_regions, avg_affected_rows, query_sample_text, plan from information_schema.statements_summary where digest_text like 'select * from t%'`, - ).Check(testkit.Rows("Select test test.t t:k 1 2 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tIndexLookUp_10\troot\t1000\t\n" + - "\t├─IndexScan_8 \tcop \t1000\ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableScan_9 \tcop \t1000\ttable:t, keep order:false, stats:pseudo")) + ).Check(testkit.Rows("Select test test.t t:k 1 2 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask\testRows\toperator info\n" + + "\tIndexLookUp_10\troot\t1000 \t\n" + + "\t├─IndexScan_8 \tcop \t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableScan_9 \tcop \t1000 \ttable:t, keep order:false, stats:pseudo")) // Disable it in global scope. tk.MustExec("set global tidb_enable_stmt_summary = false") @@ -1060,9 +1063,10 @@ func (s *testTableSuite) TestStmtSummaryTable(c *C) { max_prewrite_regions, avg_affected_rows, query_sample_text, plan from information_schema.statements_summary where digest_text like 'select * from t%'`, - ).Check(testkit.Rows("Select test test.t t:k 2 4 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tIndexLookUp_10\troot\t1000\t\n" + - "\t├─IndexScan_8 \tcop \t1000\ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + - "\t└─TableScan_9 \tcop \t1000\ttable:t, keep order:false, stats:pseudo")) + ).Check(testkit.Rows("Select test test.t t:k 2 4 0 0 0 0 0 0 0 0 0 select * from t where a=2 \tid \ttask\testRows\toperator info\n" + + "\tIndexLookUp_10\troot\t1000 \t\n" + + "\t├─IndexScan_8 \tcop \t1000 \ttable:t, index:k(a), range:[2,2], keep order:false, stats:pseudo\n" + + "\t└─TableScan_9 \tcop \t1000 \ttable:t, keep order:false, stats:pseudo")) // Unset session variable. tk.MustExec("set session tidb_enable_stmt_summary = ''") diff --git a/util/plancodec/codec.go b/util/plancodec/codec.go index 8ff46b38cb66c..d039b246dfcc5 100644 --- a/util/plancodec/codec.go +++ b/util/plancodec/codec.go @@ -52,6 +52,7 @@ func DecodePlan(planString string) (string, error) { pd := decoderPool.Get().(*planDecoder) defer decoderPool.Put(pd) pd.buf.Reset() + pd.addHeader = true return pd.decode(planString) } @@ -63,6 +64,7 @@ func DecodeNormalizedPlan(planString string) (string, error) { pd := decoderPool.Get().(*planDecoder) defer decoderPool.Put(pd) pd.buf.Reset() + pd.addHeader = false return pd.buildPlanTree(planString) } @@ -71,6 +73,7 @@ type planDecoder struct { depths []int indents [][]rune planInfos []*planInfo + addHeader bool } type planInfo struct { @@ -95,7 +98,6 @@ func (pd *planDecoder) buildPlanTree(planString string) (string, error) { } pd.depths = pd.depths[:0] pd.planInfos = pd.planInfos[:0] - planInfos := pd.planInfos for _, node := range nodes { p, err := decodePlanInfo(node) if err != nil { @@ -104,10 +106,14 @@ func (pd *planDecoder) buildPlanTree(planString string) (string, error) { if p == nil { continue } - planInfos = append(planInfos, p) + pd.planInfos = append(pd.planInfos, p) pd.depths = append(pd.depths, p.depth) } + if pd.addHeader { + pd.addPlanHeader() + } + // Calculated indentation of plans. pd.initPlanTreeIndents() for i := 1; i < len(pd.depths); i++ { @@ -115,9 +121,9 @@ func (pd *planDecoder) buildPlanTree(planString string) (string, error) { pd.fillIndent(parentIndex, i) } // Align the value of plan fields. - pd.alignFields(planInfos) + pd.alignFields() - for i, p := range planInfos { + for i, p := range pd.planInfos { if i > 0 { pd.buf.WriteByte(lineBreaker) } @@ -134,6 +140,28 @@ func (pd *planDecoder) buildPlanTree(planString string) (string, error) { return pd.buf.String(), nil } +func (pd *planDecoder) addPlanHeader() { + if len(pd.planInfos) == 0 { + return + } + header := &planInfo{ + depth: 0, + fields: []string{"id", "task", "estRows", "operator info", "actRows", "execution info", "memory", "disk"}, + } + if len(pd.planInfos[0].fields) < len(header.fields) { + // plan without runtime information. + header.fields = header.fields[:len(pd.planInfos[0].fields)] + } + planInfos := make([]*planInfo, 0, len(pd.planInfos)+1) + depths := make([]int, 0, len(pd.planInfos)+1) + planInfos = append(planInfos, header) + planInfos = append(planInfos, pd.planInfos...) + depths = append(depths, header.depth) + depths = append(depths, pd.depths...) + pd.planInfos = planInfos + pd.depths = depths +} + func (pd *planDecoder) initPlanTreeIndents() { pd.indents = pd.indents[:0] for i := 0; i < len(pd.depths); i++ { @@ -173,29 +201,29 @@ func (pd *planDecoder) fillIndent(parentIndex, childIndex int) { } } -func (pd *planDecoder) alignFields(planInfos []*planInfo) { - if len(planInfos) == 0 { +func (pd *planDecoder) alignFields() { + if len(pd.planInfos) == 0 { return } // Align fields length. Some plan may doesn't have runtime info, need append `` to align with other plan fields. maxLen := -1 - for _, p := range planInfos { + for _, p := range pd.planInfos { if len(p.fields) > maxLen { maxLen = len(p.fields) } } - for _, p := range planInfos { + for _, p := range pd.planInfos { for len(p.fields) < maxLen { p.fields = append(p.fields, "") } } - fieldsLen := len(planInfos[0].fields) + fieldsLen := len(pd.planInfos[0].fields) // Last field no need to align. fieldsLen-- for colIdx := 0; colIdx < fieldsLen; colIdx++ { - maxFieldLen := pd.getMaxFieldLength(colIdx, planInfos) - for rowIdx, p := range planInfos { + maxFieldLen := pd.getMaxFieldLength(colIdx) + for rowIdx, p := range pd.planInfos { fillLen := maxFieldLen - pd.getPlanFieldLen(rowIdx, colIdx, p) for i := 0; i < fillLen; i++ { p.fields[colIdx] += " " @@ -204,9 +232,9 @@ func (pd *planDecoder) alignFields(planInfos []*planInfo) { } } -func (pd *planDecoder) getMaxFieldLength(idx int, planInfos []*planInfo) int { +func (pd *planDecoder) getMaxFieldLength(idx int) int { maxLength := -1 - for rowIdx, p := range planInfos { + for rowIdx, p := range pd.planInfos { l := pd.getPlanFieldLen(rowIdx, idx, p) if l > maxLength { maxLength = l