Skip to content

Commit

Permalink
*: support JSON format output in explain statement (#39253)
Browse files Browse the repository at this point in the history
ref #39261
  • Loading branch information
fzzf678 authored Dec 1, 2022
1 parent 04b676a commit 8e0e49c
Show file tree
Hide file tree
Showing 12 changed files with 9,764 additions and 9,260 deletions.
94 changes: 94 additions & 0 deletions executor/explain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package executor_test

import (
"bytes"
"encoding/json"
"fmt"
"regexp"
"strconv"
Expand Down Expand Up @@ -515,3 +516,96 @@ func TestIssue35105(t *testing.T) {
require.Error(t, tk.ExecToErr("explain analyze insert into t values (1), (2), (3)"))
tk.MustQuery("select * from t").Check(testkit.Rows("2"))
}

func flatJSONPlan(j *plannercore.ExplainInfoForEncode) (res []*plannercore.ExplainInfoForEncode) {
if j == nil {
return
}
res = append(res, j)
for _, child := range j.SubOperators {
res = append(res, flatJSONPlan(child)...)
}
return
}

func TestExplainJSON(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
tk.MustExec("use test")
tk.MustExec("drop table if exists t1, t2")
tk.MustExec("create table t1(id int, key(id))")
tk.MustExec("create table t2(id int, key(id))")
cases := []string{
"select * from t1",
"select count(*) from t2",
"select * from t1, t2 where t1.id = t2.id",
"select /*+ merge_join(t1, t2)*/ * from t1, t2 where t1.id = t2.id",
"with top10 as ( select * from t1 order by id desc limit 10 ) select * from top10 where id in (1,2)",
"insert into t1 values(1)",
"delete from t2 where t2.id > 10",
"update t2 set id = 1 where id =2",
"select * from t1 where t1.id < (select sum(t2.id) from t2 where t2.id = t1.id)",
}
// test syntax
tk.MustExec("explain format = 'tidb_json' select * from t1")
tk.MustExec("explain format = tidb_json select * from t1")
tk.MustExec("explain format = 'TIDB_JSON' select * from t1")
tk.MustExec("explain format = TIDB_JSON select * from t1")
tk.MustExec("explain analyze format = 'tidb_json' select * from t1")
tk.MustExec("explain analyze format = tidb_json select * from t1")
tk.MustExec("explain analyze format = 'TIDB_JSON' select * from t1")
tk.MustExec("explain analyze format = TIDB_JSON select * from t1")

// explain
for _, sql := range cases {
jsonForamt := "explain format = tidb_json " + sql
rowForamt := "explain format = row " + sql
resJSON := tk.MustQuery(jsonForamt).Rows()
resRow := tk.MustQuery(rowForamt).Rows()

j := new([]*plannercore.ExplainInfoForEncode)
require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), j))
var flatJSONRows []*plannercore.ExplainInfoForEncode
for _, row := range *j {
flatJSONRows = append(flatJSONRows, flatJSONPlan(row)...)
}
require.Equal(t, len(flatJSONRows), len(resRow))

for i, row := range resRow {
require.Contains(t, row[0], flatJSONRows[i].ID)
require.Equal(t, flatJSONRows[i].EstRows, row[1])
require.Equal(t, flatJSONRows[i].TaskType, row[2])
require.Equal(t, flatJSONRows[i].AccessObject, row[3])
require.Equal(t, flatJSONRows[i].OperatorInfo, row[4])
}
}

// explain analyze
for _, sql := range cases {
jsonForamt := "explain analyze format = tidb_json " + sql
rowForamt := "explain analyze format = row " + sql
resJSON := tk.MustQuery(jsonForamt).Rows()
resRow := tk.MustQuery(rowForamt).Rows()

j := new([]*plannercore.ExplainInfoForEncode)
require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), j))
var flatJSONRows []*plannercore.ExplainInfoForEncode
for _, row := range *j {
flatJSONRows = append(flatJSONRows, flatJSONPlan(row)...)
}
require.Equal(t, len(flatJSONRows), len(resRow))

for i, row := range resRow {
require.Contains(t, row[0], flatJSONRows[i].ID)
require.Equal(t, flatJSONRows[i].EstRows, row[1])
require.Equal(t, flatJSONRows[i].ActRows, row[2])
require.Equal(t, flatJSONRows[i].TaskType, row[3])
require.Equal(t, flatJSONRows[i].AccessObject, row[4])
require.Equal(t, flatJSONRows[i].OperatorInfo, row[6])
// executeInfo, memory, disk maybe vary in multi execution
require.NotEqual(t, flatJSONRows[i].ExecuteInfo, "")
require.NotEqual(t, flatJSONRows[i].MemoryInfo, "")
require.NotEqual(t, flatJSONRows[i].DiskInfo, "")
}
}
}
72 changes: 72 additions & 0 deletions executor/explainfor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package executor_test

import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"testing"
Expand Down Expand Up @@ -1395,3 +1396,74 @@ func TestIssue28792(t *testing.T) {
r2 := tk.MustQuery("EXPLAIN SELECT t12.a, t12.b FROM t12 LEFT JOIN t97 use index () on t12.b = t97.b;").Rows()
require.Equal(t, r2, r1)
}

func TestExplainForJSON(t *testing.T) {
store := testkit.CreateMockStore(t)
tk1 := testkit.NewTestKit(t, store)
tk2 := testkit.NewTestKit(t, store)

tk1.MustExec("use test")
tk1.MustExec("set @@tidb_enable_collect_execution_info=0;")
tk1.MustExec("drop table if exists t1")
tk1.MustExec("create table t1(id int);")
tk1.MustQuery("select * from t1;")
tk1RootProcess := tk1.Session().ShowProcess()
ps := []*util.ProcessInfo{tk1RootProcess}
tk1.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
tk2.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
resRow := tk2.MustQuery(fmt.Sprintf("explain format = 'row' for connection %d", tk1RootProcess.ID)).Rows()
resJSON := tk2.MustQuery(fmt.Sprintf("explain format = 'tidb_json' for connection %d", tk1RootProcess.ID)).Rows()

j := new([]*core.ExplainInfoForEncode)
require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), j))
flatJSONRows := make([]*core.ExplainInfoForEncode, 0)
for _, row := range *j {
flatJSONRows = append(flatJSONRows, flatJSONPlan(row)...)
}
require.Equal(t, len(flatJSONRows), len(resRow))

for i, row := range resRow {
require.Contains(t, row[0], flatJSONRows[i].ID)
require.Equal(t, flatJSONRows[i].EstRows, row[1])
require.Equal(t, flatJSONRows[i].TaskType, row[2])
require.Equal(t, flatJSONRows[i].AccessObject, row[3])
require.Equal(t, flatJSONRows[i].OperatorInfo, row[4])
}

tk1.MustExec("set @@tidb_enable_collect_execution_info=1;")
tk1.MustExec("drop table if exists t2")
tk1.MustExec("create table t2(id int);")
tk1.MustQuery("select * from t2;")
tk1RootProcess = tk1.Session().ShowProcess()
ps = []*util.ProcessInfo{tk1RootProcess}
tk1.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
tk2.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps})
resRow = tk2.MustQuery(fmt.Sprintf("explain format = 'row' for connection %d", tk1RootProcess.ID)).Rows()
resJSON = tk2.MustQuery(fmt.Sprintf("explain format = 'tidb_json' for connection %d", tk1RootProcess.ID)).Rows()

j = new([]*core.ExplainInfoForEncode)
require.NoError(t, json.Unmarshal([]byte(resJSON[0][0].(string)), j))
flatJSONRows = []*core.ExplainInfoForEncode{}
for _, row := range *j {
flatJSONRows = append(flatJSONRows, flatJSONPlan(row)...)
}
require.Equal(t, len(flatJSONRows), len(resRow))

for i, row := range resRow {
require.Contains(t, row[0], flatJSONRows[i].ID)
require.Equal(t, flatJSONRows[i].EstRows, row[1])
require.Equal(t, flatJSONRows[i].ActRows, row[2])
require.Equal(t, flatJSONRows[i].TaskType, row[3])
require.Equal(t, flatJSONRows[i].AccessObject, row[4])
require.Equal(t, flatJSONRows[i].OperatorInfo, row[6])
// executeInfo, memory, disk maybe vary in multi execution
require.NotEqual(t, flatJSONRows[i].ExecuteInfo, "")
require.NotEqual(t, flatJSONRows[i].MemoryInfo, "")
require.NotEqual(t, flatJSONRows[i].DiskInfo, "")
}
// test syntax
tk2.MustExec(fmt.Sprintf("explain format = 'tidb_json' for connection %d", tk1RootProcess.ID))
tk2.MustExec(fmt.Sprintf("explain format = tidb_json for connection %d", tk1RootProcess.ID))
tk2.MustExec(fmt.Sprintf("explain format = 'TIDB_JSON' for connection %d", tk1RootProcess.ID))
tk2.MustExec(fmt.Sprintf("explain format = TIDB_JSON for connection %d", tk1RootProcess.ID))
}
1 change: 1 addition & 0 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ var tokenMap = map[string]int{
"THEN": then,
"TIDB": tidb,
"TIDB_CURRENT_TSO": tidbCurrentTSO,
"TIDB_JSON": tidbJson,
"TIFLASH": tiFlash,
"TIKV_IMPORTER": tikvImporter,
"TIME": timeType,
Expand Down
Loading

0 comments on commit 8e0e49c

Please sign in to comment.