code: func (b *PlanBuilder) buildExplainFor(explainFor *ast.ExplainForStmt) (Plan, error) {
  	processInfo, ok := b.ctx.GetSessionManager().GetProcessInfo(explainFor.ConnectionID)
  	if !ok {
  		return nil, ErrNoSuchThread.GenWithStackByArgs(explainFor.ConnectionID)
  	}
  	if b.ctx.GetSessionVars() != nil && b.ctx.GetSessionVars().User != nil {
  		if b.ctx.GetSessionVars().User.Username != processInfo.User {
  			err := ErrAccessDenied.GenWithStackByArgs(b.ctx.GetSessionVars().User.Username, b.ctx.GetSessionVars().User.Hostname)
  			// Different from MySQL's behavior and document.
  			b.visitInfo = appendVisitInfo(b.visitInfo, mysql.SuperPriv, "", "", "", err)
  		}
  	}
  
  	targetPlan, ok := processInfo.Plan.(Plan)
  	if !ok || targetPlan == nil {
  		return &Explain{Format: explainFor.Format}, nil
  	}
  
  	return b.buildExplainPlan(targetPlan, explainFor.Format, false, nil)
  }
docs: // buildExplainFor gets *last* (maybe running or finished) query plan from connection #connection id.
  // See https://dev.mysql.com/doc/refman/8.0/en/explain-for-connection.html.