github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/explain.go (about)

     1  // Copyright 2016 PingCAP, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package executor
    15  
    16  import (
    17  	"strconv"
    18  	"strings"
    19  
    20  	"github.com/insionng/yougam/libraries/pingcap/tidb/ast"
    21  	"github.com/insionng/yougam/libraries/pingcap/tidb/optimizer/plan"
    22  	"github.com/insionng/yougam/libraries/pingcap/tidb/parser/opcode"
    23  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    24  )
    25  
    26  type explainEntry struct {
    27  	ID           int64
    28  	selectType   string
    29  	table        string
    30  	joinType     string
    31  	possibleKeys string
    32  	key          string
    33  	keyLen       string
    34  	ref          string
    35  	rows         int64
    36  	extra        []string
    37  }
    38  
    39  func (e *explainEntry) setJoinTypeForTableScan(p *plan.TableScan) {
    40  	if len(p.AccessConditions) == 0 {
    41  		e.joinType = "ALL"
    42  		return
    43  	}
    44  	if p.RefAccess {
    45  		e.joinType = "eq_ref"
    46  		return
    47  	}
    48  	for _, con := range p.AccessConditions {
    49  		if x, ok := con.(*ast.BinaryOperationExpr); ok {
    50  			if x.Op == opcode.EQ {
    51  				e.joinType = "const"
    52  				return
    53  			}
    54  		}
    55  	}
    56  	e.joinType = "range"
    57  }
    58  
    59  func (e *explainEntry) setJoinTypeForIndexScan(p *plan.IndexScan) {
    60  	if len(p.AccessConditions) == 0 {
    61  		e.joinType = "index"
    62  		return
    63  	}
    64  	if len(p.AccessConditions) == p.AccessEqualCount {
    65  		if p.RefAccess {
    66  			if p.Index.Unique {
    67  				e.joinType = "eq_ref"
    68  			} else {
    69  				e.joinType = "ref"
    70  			}
    71  		} else {
    72  			if p.Index.Unique {
    73  				e.joinType = "const"
    74  			} else {
    75  				e.joinType = "range"
    76  			}
    77  		}
    78  		return
    79  	}
    80  	e.joinType = "range"
    81  }
    82  
    83  // ExplainExec represents an explain executor.
    84  // See: https://dev.mysql.com/doc/refman/5.7/en/explain-output.html
    85  type ExplainExec struct {
    86  	StmtPlan plan.Plan
    87  	fields   []*ast.ResultField
    88  	rows     []*Row
    89  	cursor   int
    90  }
    91  
    92  // Fields implements Executor Fields interface.
    93  func (e *ExplainExec) Fields() []*ast.ResultField {
    94  	return e.fields
    95  }
    96  
    97  // Next implements Execution Next interface.
    98  func (e *ExplainExec) Next() (*Row, error) {
    99  	if e.rows == nil {
   100  		e.fetchRows()
   101  	}
   102  	if e.cursor >= len(e.rows) {
   103  		return nil, nil
   104  	}
   105  	row := e.rows[e.cursor]
   106  	e.cursor++
   107  	return row, nil
   108  }
   109  
   110  func (e *ExplainExec) fetchRows() {
   111  	visitor := &explainVisitor{id: 1}
   112  	visitor.explain(e.StmtPlan)
   113  	for _, entry := range visitor.entries {
   114  		row := &Row{}
   115  		row.Data = types.MakeDatums(
   116  			entry.ID,
   117  			entry.selectType,
   118  			entry.table,
   119  			entry.joinType,
   120  			entry.key,
   121  			entry.key,
   122  			entry.keyLen,
   123  			entry.ref,
   124  			entry.rows,
   125  			strings.Join(entry.extra, "; "),
   126  		)
   127  		for i := range row.Data {
   128  			if row.Data[i].Kind() == types.KindString && row.Data[i].GetString() == "" {
   129  				row.Data[i].SetNull()
   130  			}
   131  		}
   132  		e.rows = append(e.rows, row)
   133  	}
   134  }
   135  
   136  // Close implements Executor Close interface.
   137  func (e *ExplainExec) Close() error {
   138  	return nil
   139  }
   140  
   141  type explainVisitor struct {
   142  	id int64
   143  
   144  	// Sort extra should be appended in the first table in a join.
   145  	sort    bool
   146  	entries []*explainEntry
   147  }
   148  
   149  func (v *explainVisitor) explain(p plan.Plan) {
   150  	switch x := p.(type) {
   151  	case *plan.TableScan:
   152  		v.entries = append(v.entries, v.newEntryForTableScan(x))
   153  	case *plan.IndexScan:
   154  		v.entries = append(v.entries, v.newEntryForIndexScan(x))
   155  	case *plan.Sort:
   156  		v.sort = true
   157  	}
   158  
   159  	for _, c := range p.GetChildren() {
   160  		v.explain(c)
   161  	}
   162  }
   163  
   164  func (v *explainVisitor) newEntryForTableScan(p *plan.TableScan) *explainEntry {
   165  	entry := &explainEntry{
   166  		ID:         v.id,
   167  		selectType: "SIMPLE",
   168  		table:      p.Table.Name.O,
   169  	}
   170  	entry.setJoinTypeForTableScan(p)
   171  	if entry.joinType != "ALL" {
   172  		entry.key = "PRIMARY"
   173  		entry.keyLen = "8"
   174  	}
   175  	if len(p.AccessConditions)+len(p.FilterConditions) > 0 {
   176  		entry.extra = append(entry.extra, "Using where")
   177  	}
   178  
   179  	v.setSortExtra(entry)
   180  	return entry
   181  }
   182  
   183  func (v *explainVisitor) newEntryForIndexScan(p *plan.IndexScan) *explainEntry {
   184  	entry := &explainEntry{
   185  		ID:         v.id,
   186  		selectType: "SIMPLE",
   187  		table:      p.Table.Name.O,
   188  		key:        p.Index.Name.O,
   189  	}
   190  	if len(p.AccessConditions) != 0 {
   191  		keyLen := 0
   192  		for i := 0; i < len(p.Index.Columns); i++ {
   193  			if i < p.AccessEqualCount {
   194  				keyLen += p.Index.Columns[i].Length
   195  			} else if i < len(p.AccessConditions) {
   196  				keyLen += p.Index.Columns[i].Length
   197  				break
   198  			}
   199  		}
   200  		entry.keyLen = strconv.Itoa(keyLen)
   201  	}
   202  	entry.setJoinTypeForIndexScan(p)
   203  	if len(p.AccessConditions)+len(p.FilterConditions) > 0 {
   204  		entry.extra = append(entry.extra, "Using where")
   205  	}
   206  
   207  	v.setSortExtra(entry)
   208  	return entry
   209  }
   210  
   211  func (v *explainVisitor) setSortExtra(entry *explainEntry) {
   212  	if v.sort {
   213  		entry.extra = append(entry.extra, "Using filesort")
   214  		v.sort = false
   215  	}
   216  }