vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/vexplain.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "context" 21 "encoding/json" 22 "fmt" 23 24 "vitess.io/vitess/go/sqltypes" 25 querypb "vitess.io/vitess/go/vt/proto/query" 26 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 27 "vitess.io/vitess/go/vt/sqlparser" 28 "vitess.io/vitess/go/vt/srvtopo" 29 "vitess.io/vitess/go/vt/vterrors" 30 ) 31 32 type ( 33 ExecuteEntry struct { 34 ID int 35 Target *querypb.Target 36 Gateway srvtopo.Gateway 37 Query string 38 FiredFrom Primitive 39 } 40 41 VExplain struct { 42 Input Primitive 43 Type sqlparser.VExplainType 44 } 45 ) 46 47 var _ Primitive = (*VExplain)(nil) 48 49 // RouteType implements the Primitive interface 50 func (v *VExplain) RouteType() string { 51 return v.Input.RouteType() 52 } 53 54 // GetKeyspaceName implements the Primitive interface 55 func (v *VExplain) GetKeyspaceName() string { 56 return v.Input.GetKeyspaceName() 57 } 58 59 // GetTableName implements the Primitive interface 60 func (v *VExplain) GetTableName() string { 61 return v.Input.GetTableName() 62 } 63 64 // GetFields implements the Primitive interface 65 func (v *VExplain) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 66 return v.Input.GetFields(ctx, vcursor, bindVars) 67 } 68 69 // NeedsTransaction implements the Primitive interface 70 func (v *VExplain) NeedsTransaction() bool { 71 return v.Input.NeedsTransaction() 72 } 73 74 // TryExecute implements the Primitive interface 75 func (v *VExplain) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 76 vcursor.Session().VExplainLogging() 77 _, err := vcursor.ExecutePrimitive(ctx, v.Input, bindVars, wantfields) 78 if err != nil { 79 return nil, err 80 } 81 return v.convertToResult(ctx, vcursor) 82 } 83 84 // TryStreamExecute implements the Primitive interface 85 func (v *VExplain) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 86 vcursor.Session().VExplainLogging() 87 err := vcursor.StreamExecutePrimitive(ctx, v.Input, bindVars, wantfields, func(result *sqltypes.Result) error { 88 return nil 89 }) 90 if err != nil { 91 return err 92 } 93 result, err := v.convertToResult(ctx, vcursor) 94 if err != nil { 95 return err 96 } 97 return callback(result) 98 } 99 100 func (v *VExplain) convertToResult(ctx context.Context, vcursor VCursor) (*sqltypes.Result, error) { 101 switch v.Type { 102 case sqlparser.QueriesVExplainType: 103 result := convertToVExplainQueriesResult(vcursor.Session().GetVExplainLogs()) 104 return result, nil 105 case sqlparser.AllVExplainType: 106 return v.convertToVExplainAllResult(ctx, vcursor) 107 default: 108 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "Unknown type of VExplain plan") 109 } 110 } 111 112 func (v *VExplain) convertToVExplainAllResult(ctx context.Context, vcursor VCursor) (*sqltypes.Result, error) { 113 logEntries := vcursor.Session().GetVExplainLogs() 114 explainResults := make(map[Primitive]string) 115 for _, entry := range logEntries { 116 if entry.Target == nil || entry.Gateway == nil || entry.FiredFrom == nil { 117 continue 118 } 119 if explainResults[entry.FiredFrom] != "" { 120 continue 121 } 122 explainQuery := fmt.Sprintf("explain format = json %v", entry.Query) 123 // We rely on the parser to see if the query we have is explainable or not 124 // If we get an error in parsing then we can't execute explain on the given query, and we skip it 125 _, err := sqlparser.Parse(explainQuery) 126 if err != nil { 127 continue 128 } 129 // Explain statement should now succeed 130 res, err := vcursor.ExecuteStandalone(ctx, nil, explainQuery, nil, &srvtopo.ResolvedShard{ 131 Target: entry.Target, 132 Gateway: entry.Gateway, 133 }) 134 if err != nil { 135 return nil, err 136 } 137 explainResults[entry.FiredFrom] = res.Rows[0][0].ToString() 138 } 139 140 planDescription := primitiveToPlanDescriptionWithSQLResults(v.Input, explainResults) 141 resultBytes, err := json.MarshalIndent(planDescription, "", "\t") 142 if err != nil { 143 return nil, err 144 } 145 146 result := string(resultBytes) 147 fields := []*querypb.Field{ 148 { 149 Name: "VExplain", Type: sqltypes.VarChar, 150 }, 151 } 152 rows := []sqltypes.Row{ 153 { 154 sqltypes.NewVarChar(result), 155 }, 156 } 157 qr := &sqltypes.Result{ 158 Fields: fields, 159 Rows: rows, 160 } 161 return qr, nil 162 } 163 164 // primitiveToPlanDescriptionWithSQLResults transforms a primitive tree into a corresponding PlanDescription tree 165 // and adds the given res ... 166 func primitiveToPlanDescriptionWithSQLResults(in Primitive, res map[Primitive]string) PrimitiveDescription { 167 this := in.description() 168 169 if v, found := res[in]; found { 170 this.Other["mysql_explain_json"] = json.RawMessage(v) 171 } 172 173 for _, input := range in.Inputs() { 174 this.Inputs = append(this.Inputs, primitiveToPlanDescriptionWithSQLResults(input, res)) 175 } 176 177 if len(in.Inputs()) == 0 { 178 this.Inputs = []PrimitiveDescription{} 179 } 180 181 return this 182 } 183 184 func convertToVExplainQueriesResult(logs []ExecuteEntry) *sqltypes.Result { 185 fields := []*querypb.Field{{ 186 Name: "#", Type: sqltypes.Int32, 187 }, { 188 Name: "keyspace", Type: sqltypes.VarChar, 189 }, { 190 Name: "shard", Type: sqltypes.VarChar, 191 }, { 192 Name: "query", Type: sqltypes.VarChar, 193 }} 194 qr := &sqltypes.Result{ 195 Fields: fields, 196 } 197 for _, line := range logs { 198 qr.Rows = append(qr.Rows, sqltypes.Row{ 199 sqltypes.NewInt32(int32(line.ID)), 200 sqltypes.NewVarChar(line.Target.Keyspace), 201 sqltypes.NewVarChar(line.Target.Shard), 202 sqltypes.NewVarChar(line.Query), 203 }) 204 } 205 return qr 206 } 207 208 // Inputs implements the Primitive interface 209 func (v *VExplain) Inputs() []Primitive { 210 return []Primitive{v.Input} 211 } 212 213 func (v *VExplain) description() PrimitiveDescription { 214 return PrimitiveDescription{ 215 OperatorType: "VEXPLAIN", 216 Other: map[string]any{"Type": v.Type.ToString()}, 217 } 218 }