Skip to content

Commit

Permalink
support explain (pingcap#1632)
Browse files Browse the repository at this point in the history
  • Loading branch information
hanfei1991 authored Aug 26, 2016
1 parent 558ba06 commit e33587e
Show file tree
Hide file tree
Showing 18 changed files with 605 additions and 395 deletions.
46 changes: 23 additions & 23 deletions ast/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,29 +32,29 @@ var (

// List scalar function names.
const (
AndAnd = "&&"
LeftShift = "<<"
RightShift = ">>"
OrOr = "||"
GE = ">="
LE = "<="
EQ = "="
NE = "!="
LT = "<"
GT = ">"
Plus = "+"
Minus = "-"
And = "&"
Or = "|"
Mod = "%"
Xor = "^"
Div = "/"
Mul = "*"
UnaryNot = "!" // Avoid name conflict with Not in github/pingcap/check.
BitNeg = "~"
IntDiv = "DIV"
LogicXor = "XOR"
NullEQ = "<=>"
AndAnd = "and"
LeftShift = "leftshift"
RightShift = "rightshift"
OrOr = "or"
GE = "ge"
LE = "le"
EQ = "eq"
NE = "ne"
LT = "lt"
GT = "gt"
Plus = "plus"
Minus = "minus"
And = "bitand"
Or = "bitor"
Mod = "mod"
Xor = "bitxor"
Div = "div"
Mul = "mul"
UnaryNot = "not" // Avoid name conflict with Not in github/pingcap/check.
BitNeg = "bitneg"
IntDiv = "intdiv"
LogicXor = "xor"
NullEQ = "nulleq"
UnaryPlus = "unaryplus"
UnaryMinus = "unaryminus"
In = "in"
Expand Down
2 changes: 1 addition & 1 deletion executor/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ func (b *executorBuilder) buildDDL(v *plan.DDL) Executor {
func (b *executorBuilder) buildExplain(v *plan.Explain) Executor {
return &ExplainExec{
StmtPlan: v.StmtPlan,
fields: v.Fields(),
schema: v.GetSchema(),
}
}

Expand Down
131 changes: 16 additions & 115 deletions executor/explain.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,149 +14,50 @@
package executor

import (
"strings"
"encoding/json"

"github.com/juju/errors"
"github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/expression"
"github.com/pingcap/tidb/plan"
"github.com/pingcap/tidb/util/types"
)

type explainEntry struct {
ID int64
selectType string
table string
joinType string
possibleKeys string
key string
keyLen string
ref string
rows int64
extra []string
}

// ExplainExec represents an explain executor.
// See https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
type ExplainExec struct {
StmtPlan plan.Plan
fields []*ast.ResultField
rows []*Row
cursor int
StmtPlan plan.Plan
schema expression.Schema
evaluated bool
}

// Schema implements Executor Schema interface.
func (e *ExplainExec) Schema() expression.Schema {
return nil
return e.schema
}

// Fields implements Executor Fields interface.
func (e *ExplainExec) Fields() []*ast.ResultField {
return e.fields
return nil
}

// Next implements Execution Next interface.
func (e *ExplainExec) Next() (*Row, error) {
if e.rows == nil {
e.fetchRows()
}
if e.cursor >= len(e.rows) {
if e.evaluated {
return nil, nil
}
row := e.rows[e.cursor]
e.cursor++
return row, nil
}

func (e *ExplainExec) fetchRows() {
visitor := &explainVisitor{id: 1}
visitor.explain(e.StmtPlan)
for _, entry := range visitor.entries {
row := &Row{}
row.Data = types.MakeDatums(
entry.ID,
entry.selectType,
entry.table,
entry.joinType,
entry.key,
entry.key,
entry.keyLen,
entry.ref,
entry.rows,
strings.Join(entry.extra, "; "),
)
for i := range row.Data {
if row.Data[i].Kind() == types.KindString && row.Data[i].GetString() == "" {
row.Data[i].SetNull()
}
}
e.rows = append(e.rows, row)
e.evaluated = true
explain, err := json.MarshalIndent(e.StmtPlan, "", " ")
if err != nil {
return nil, errors.Trace(err)
}
row := &Row{
Data: types.MakeDatums("EXPLAIN", string(explain)),
}
return row, nil
}

// Close implements Executor Close interface.
func (e *ExplainExec) Close() error {
return nil
}

type explainVisitor struct {
id int64

// Sort extra should be appended in the first table in a join.
sort bool
entries []*explainEntry
}

func (v *explainVisitor) explain(p plan.Plan) {
switch x := p.(type) {
case *plan.PhysicalTableScan:
v.entries = append(v.entries, v.newEntryForTableScan(x))
case *plan.PhysicalIndexScan:
v.entries = append(v.entries, v.newEntryForIndexScan(x))
case *plan.NewSort:
v.sort = true
}

for _, c := range p.GetChildren() {
v.explain(c)
}
}

func (v *explainVisitor) newEntryForTableScan(p *plan.PhysicalTableScan) *explainEntry {
entry := &explainEntry{
ID: v.id,
selectType: "SIMPLE",
table: p.Table.Name.O,
}
if entry.joinType != "ALL" {
entry.key = "PRIMARY"
entry.keyLen = "8"
}
if len(p.AccessCondition) > 0 {
entry.extra = append(entry.extra, "Using where")
}

v.setSortExtra(entry)
return entry
}

func (v *explainVisitor) newEntryForIndexScan(p *plan.PhysicalIndexScan) *explainEntry {
entry := &explainEntry{
ID: v.id,
selectType: "SIMPLE",
table: p.Table.Name.O,
key: p.Index.Name.O,
}
if len(p.AccessCondition) > 0 {
entry.extra = append(entry.extra, "Using where")
}

v.setSortExtra(entry)
return entry
}

func (v *explainVisitor) setSortExtra(entry *explainEntry) {
if v.sort {
entry.extra = append(entry.extra, "Using filesort")
v.sort = false
}
}
Loading

0 comments on commit e33587e

Please sign in to comment.