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 }