Skip to content

Commit

Permalink
expression: fix panic of explain for connection when plan ca… (#16285)
Browse files Browse the repository at this point in the history
  • Loading branch information
sre-bot authored Apr 11, 2020
1 parent 56bf9e5 commit 03a8b4f
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 4 deletions.
3 changes: 3 additions & 0 deletions executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,9 @@ func ResetContextOfStmt(ctx sessionctx.Context, s ast.StmtNode) (err error) {
sc.InExplainStmt = true
s = explainStmt.Stmt
}
if _, ok := s.(*ast.ExplainForStmt); ok {
sc.InExplainStmt = true
}
// TODO: Many same bool variables here.
// We should set only two variables (
// IgnoreErr and StrictSQLMode) to avoid setting the same bool variables and
Expand Down
35 changes: 35 additions & 0 deletions executor/explainfor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ package executor_test
import (
"crypto/tls"
"fmt"
"math"

. "github.com/pingcap/check"
"github.com/pingcap/parser/auth"
"github.com/pingcap/tidb/planner/core"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/kvcache"
"github.com/pingcap/tidb/util/testkit"
)

Expand Down Expand Up @@ -119,3 +122,35 @@ func (s *testSuite) TestExplainMetricTable(c *C) {
tk.MustQuery("desc select * from information_schema.cluster_log where type in ('high_cpu_1','high_memory_1') and time >= '2019-12-23 16:10:13' and time <= '2019-12-23 16:30:13'").Check(testkit.Rows(
`MemTableScan_5 10000.00 root table:CLUSTER_LOG start_time:2019-12-23 16:10:13, end_time:2019-12-23 16:30:13, node_types:["high_cpu_1","high_memory_1"]`))
}

func (s *testSuite) TestExplainForConnPlanCache(c *C) {
tk := testkit.NewTestKit(c, s.store)
orgEnable := core.PreparedPlanCacheEnabled()
defer func() {
core.SetPreparedPlanCache(orgEnable)
}()
core.SetPreparedPlanCache(true)
var err error
tk.Se, err = session.CreateSession4TestWithOpt(s.store, &session.Opt{
PreparedPlanCache: kvcache.NewSimpleLRUCache(100, 0.1, math.MaxUint64),
})
c.Assert(err, IsNil)

tk.MustExec("use test")
tk.MustExec("drop table if exists t")
tk.MustExec("create table t(a int)")
rows := tk.MustQuery("select connection_id()").Rows()
c.Assert(len(rows), Equals, 1)
connID := rows[0][0].(string)
tk.MustExec("prepare stmt from 'select * from t where a = ?'")
tk.MustExec("set @p0='1'")
tk.MustExec("execute stmt using @p0")
tkProcess := tk.Se.ShowProcess()
ps := []*util.ProcessInfo{tkProcess}
tk.Se.SetSessionManager(&mockSessionManager1{PS: ps})
tk.MustQuery(fmt.Sprintf("explain for connection %s", connID)).Check(testkit.Rows(
"TableReader_7 8000.00 root data:Selection_6",
"└─Selection_6 8000.00 cop[tikv] eq(cast(test.t.a), 1)",
" └─TableFullScan_5 10000.00 cop[tikv] table:t keep order:false, stats:pseudo",
))
}
13 changes: 9 additions & 4 deletions expression/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,11 @@ func (c *Constant) Clone() Expression {

// GetType implements Expression interface.
func (c *Constant) GetType() *types.FieldType {
if c.ParamMarker != nil {
if p := c.ParamMarker; p != nil && !p.ctx.GetSessionVars().StmtCtx.InExplainStmt {
// GetType() may be called in multi-threaded context, e.g, in building inner executors of IndexJoin,
// so it should avoid data race. We achieve this by returning different FieldType pointer for each call.
tp := types.NewFieldType(mysql.TypeUnspecified)
dt := c.ParamMarker.GetUserVar()
dt := p.GetUserVar()
types.DefaultParamTypeForValue(dt.GetValue(), tp)
return tp
}
Expand Down Expand Up @@ -176,8 +176,13 @@ func (c *Constant) VecEvalJSON(ctx sessionctx.Context, input *chunk.Chunk, resul
}

func (c *Constant) getLazyDatum() (dt types.Datum, isLazy bool, err error) {
if c.ParamMarker != nil {
dt = c.ParamMarker.GetUserVar()
if p := c.ParamMarker; p != nil {
if p.ctx.GetSessionVars().StmtCtx.InExplainStmt {
// Since `ParamMarker` is not nil only in prepare/execute context, the query must be `explain for connection` when coming here.
// The PreparedParams may have been reset already, to avoid panic, we just use the pre-evaluated datum for this constant.
return dt, false, nil
}
dt = p.GetUserVar()
isLazy = true
return
} else if c.DeferredExpr != nil {
Expand Down

0 comments on commit 03a8b4f

Please sign in to comment.