github.com/XiaoMi/Gaea@v1.2.5/proxy/plan/plan_explain.go (about)

     1  // Copyright 2019 The Gaea Authors. All Rights Reserved.
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package plan
    16  
    17  import (
    18  	"fmt"
    19  	"github.com/XiaoMi/Gaea/mysql"
    20  	"github.com/XiaoMi/Gaea/parser/ast"
    21  	"github.com/XiaoMi/Gaea/proxy/router"
    22  	"github.com/XiaoMi/Gaea/proxy/sequence"
    23  	"github.com/XiaoMi/Gaea/util"
    24  )
    25  
    26  // constants of ShardType
    27  const (
    28  	ShardTypeUnshard = "unshard"
    29  	ShardTypeShard   = "shard"
    30  )
    31  
    32  // ExplainPlan is the plan for explain statement
    33  type ExplainPlan struct {
    34  	shardType string
    35  	sqls      map[string]map[string][]string
    36  }
    37  
    38  func buildExplainPlan(stmt *ast.ExplainStmt, phyDBs map[string]string, db, sql string, r *router.Router, seq *sequence.SequenceManager) (*ExplainPlan, error) {
    39  	stmtToExplain := stmt.Stmt
    40  	if _, ok := stmtToExplain.(*ast.ExplainStmt); ok {
    41  		return nil, fmt.Errorf("nested explain")
    42  	}
    43  
    44  	p, err := BuildPlan(stmtToExplain, phyDBs, db, sql, r, seq)
    45  	if err != nil {
    46  		return nil, fmt.Errorf("build plan to explain error: %v", err)
    47  	}
    48  
    49  	ep := &ExplainPlan{}
    50  
    51  	switch pl := p.(type) {
    52  	case *SelectPlan:
    53  		ep.shardType = ShardTypeShard
    54  		ep.sqls = pl.sqls
    55  		return ep, nil
    56  	case *DeletePlan:
    57  		ep.shardType = ShardTypeShard
    58  		ep.sqls = pl.sqls
    59  		return ep, nil
    60  	case *UpdatePlan:
    61  		ep.shardType = ShardTypeShard
    62  		ep.sqls = pl.sqls
    63  		return ep, nil
    64  	case *InsertPlan:
    65  		ep.shardType = ShardTypeShard
    66  		ep.sqls = pl.sqls
    67  		return ep, nil
    68  	case *UnshardPlan:
    69  		ep.shardType = ShardTypeUnshard
    70  		ep.sqls = make(map[string]map[string][]string)
    71  		dbSQLs := make(map[string][]string)
    72  		if phyDB, ok := phyDBs[pl.db]; ok {
    73  			pl.db = phyDB
    74  		}
    75  		dbSQLs[pl.db] = []string{pl.sql}
    76  		ep.sqls[r.GetDefaultRule().GetSlice(0)] = dbSQLs
    77  		return ep, nil
    78  	default:
    79  		return nil, fmt.Errorf("unsupport plan to explain, type: %T", p)
    80  	}
    81  }
    82  
    83  // ExecuteIn implement Plan
    84  func (p *ExplainPlan) ExecuteIn(*util.RequestContext, Executor) (*mysql.Result, error) {
    85  	return createExplainResult(p.shardType, p.sqls), nil
    86  }
    87  
    88  // Size implement Plan
    89  func (p *ExplainPlan) Size() int {
    90  	return 1
    91  }
    92  
    93  func createExplainResult(shardType string, sqls map[string]map[string][]string) *mysql.Result {
    94  	var rows [][]interface{}
    95  	var names = []string{"type", "slice", "db", "sql"}
    96  
    97  	for slice, dbSQLs := range sqls {
    98  		for db, tableSQLs := range dbSQLs {
    99  			for _, sql := range tableSQLs {
   100  				row := []interface{}{shardType, slice, db, sql}
   101  				rows = append(rows, row)
   102  			}
   103  		}
   104  	}
   105  
   106  	r, _ := mysql.BuildResultset(nil, names, rows)
   107  	ret := &mysql.Result{
   108  		Resultset: r,
   109  	}
   110  
   111  	return ret
   112  }