code: func (cc *clientConn) handleQuery(ctx context.Context, sql string) (err error) {
  	rs, err := cc.ctx.Execute(ctx, sql)
  	if err != nil {
  		metrics.ExecuteErrorCounter.WithLabelValues(metrics.ExecuteErrorToLabel(err)).Inc()
  		return err
  	}
  	cc.mu.Lock()
  	cc.mu.resultSets = rs
  	status := atomic.LoadInt32(&cc.status)
  	if status == connStatusShutdown || status == connStatusWaitShutdown {
  		cc.mu.Unlock()
  		killConn(cc)
  		return errors.New("killed by another connection")
  	}
  	cc.mu.Unlock()
  	if rs != nil {
  		if len(rs) == 1 {
  			err = cc.writeResultset(ctx, rs[0], false, 0, 0)
  		} else {
  			err = cc.writeMultiResultset(ctx, rs, false)
  		}
  	} else {
  		loadDataInfo := cc.ctx.Value(executor.LoadDataVarKey)
  		if loadDataInfo != nil {
  			defer cc.ctx.SetValue(executor.LoadDataVarKey, nil)
  			if err = cc.handleLoadData(ctx, loadDataInfo.(*executor.LoadDataInfo)); err != nil {
  				return err
  			}
  		}
  
  		loadStats := cc.ctx.Value(executor.LoadStatsVarKey)
  		if loadStats != nil {
  			defer cc.ctx.SetValue(executor.LoadStatsVarKey, nil)
  			if err = cc.handleLoadStats(ctx, loadStats.(*executor.LoadStatsInfo)); err != nil {
  				return err
  			}
  		}
  
  		err = cc.writeOK()
  	}
  	return err
  }
docs: // handleQuery executes the sql query string and writes result set or result ok to the client.
  // As the execution time of this function represents the performance of TiDB, we do time log and metrics here.
  // There is a special query `load data` that does not return result, which is handled differently.
  // Query `load stats` does not return result either.