github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/internal.go (about)

     1  // Copyright 2016 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"context"
    15  	"math"
    16  	"strings"
    17  	"sync"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/kv"
    20  	"github.com/cockroachdb/cockroach/pkg/security"
    21  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    22  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/descs"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgwirebase"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    29  	"github.com/cockroachdb/cockroach/pkg/sql/sqlutil"
    30  	"github.com/cockroachdb/cockroach/pkg/util/mon"
    31  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    32  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    33  	"github.com/cockroachdb/errors"
    34  	"github.com/cockroachdb/logtags"
    35  )
    36  
    37  var _ sqlutil.InternalExecutor = &InternalExecutor{}
    38  
    39  // InternalExecutor can be used internally by code modules to execute SQL
    40  // statements without needing to open a SQL connection.
    41  //
    42  // InternalExecutor can execute one statement at a time. As of 03/2018, it
    43  // doesn't offer a session interface for maintaining session state or for
    44  // running explicit SQL transactions. However, it supports running SQL
    45  // statements inside a higher-lever (KV) txn and inheriting session variables
    46  // from another session.
    47  //
    48  // Methods not otherwise specified are safe for concurrent execution.
    49  type InternalExecutor struct {
    50  	s *Server
    51  
    52  	// mon is the monitor used by all queries executed through the
    53  	// InternalExecutor.
    54  	mon *mon.BytesMonitor
    55  
    56  	// memMetrics is the memory metrics that queries executed through the
    57  	// InternalExecutor will contribute to.
    58  	memMetrics MemoryMetrics
    59  
    60  	// sessionData, if not nil, represents the session variables used by
    61  	// statements executed on this internalExecutor. Note that queries executed by
    62  	// the executor will run on copies of this data.
    63  	sessionData *sessiondata.SessionData
    64  
    65  	// The internal executor uses its own Collection. A Collection
    66  	// is a schema cache for each transaction and contains data like the schema
    67  	// modified by a transaction. Occasionally an internal executor is called
    68  	// within the context of a transaction that has modified the schema, the
    69  	// internal executor should see the modified schema. This interface allows
    70  	// the internal executor to modify its Collection to match the
    71  	// Collection of the parent executor.
    72  	tcModifier descs.ModifiedCollectionCopier
    73  }
    74  
    75  // MakeInternalExecutor creates an InternalExecutor.
    76  func MakeInternalExecutor(
    77  	ctx context.Context, s *Server, memMetrics MemoryMetrics, settings *cluster.Settings,
    78  ) InternalExecutor {
    79  	monitor := mon.MakeUnlimitedMonitor(
    80  		ctx,
    81  		"internal SQL executor",
    82  		mon.MemoryResource,
    83  		memMetrics.CurBytesCount,
    84  		memMetrics.MaxBytesHist,
    85  		math.MaxInt64, /* noteworthy */
    86  		settings,
    87  	)
    88  	return InternalExecutor{
    89  		s:          s,
    90  		mon:        &monitor,
    91  		memMetrics: memMetrics,
    92  	}
    93  }
    94  
    95  // SetSessionData binds the session variables that will be used by queries
    96  // performed through this executor from now on.
    97  //
    98  // SetSessionData cannot be called concurently with query execution.
    99  func (ie *InternalExecutor) SetSessionData(sessionData *sessiondata.SessionData) {
   100  	ie.s.populateMinimalSessionData(sessionData)
   101  	ie.sessionData = sessionData
   102  }
   103  
   104  // initConnEx creates a connExecutor and runs it on a separate goroutine. It
   105  // returns a StmtBuf into which commands can be pushed and a WaitGroup that will
   106  // be signaled when connEx.run() returns.
   107  //
   108  // If txn is not nil, the statement will be executed in the respective txn.
   109  //
   110  // sd will constitute the executor's session state.
   111  func (ie *InternalExecutor) initConnEx(
   112  	ctx context.Context,
   113  	txn *kv.Txn,
   114  	sd *sessiondata.SessionData,
   115  	syncCallback func([]resWithPos),
   116  	errCallback func(error),
   117  ) (*StmtBuf, *sync.WaitGroup, error) {
   118  	clientComm := &internalClientComm{
   119  		sync: syncCallback,
   120  		// init lastDelivered below the position of the first result (0).
   121  		lastDelivered: -1,
   122  	}
   123  
   124  	// When the connEx is serving an internal executor, it can inherit the
   125  	// application name from an outer session. This happens e.g. during ::regproc
   126  	// casts and built-in functions that use SQL internally. In that case, we do
   127  	// not want to record statistics against the outer application name directly;
   128  	// instead we want to use a separate bucket. However we will still want to
   129  	// have separate buckets for different applications so that we can measure
   130  	// their respective "pressure" on internal queries. Hence the choice here to
   131  	// add the delegate prefix to the current app name.
   132  	var appStatsBucketName string
   133  	if !strings.HasPrefix(sd.ApplicationName, sqlbase.InternalAppNamePrefix) {
   134  		appStatsBucketName = sqlbase.DelegatedAppNamePrefix + sd.ApplicationName
   135  	} else {
   136  		// If this is already an "internal app", don't put more prefix.
   137  		appStatsBucketName = sd.ApplicationName
   138  	}
   139  	appStats := ie.s.sqlStats.getStatsForApplication(appStatsBucketName)
   140  
   141  	stmtBuf := NewStmtBuf()
   142  	var ex *connExecutor
   143  	if txn == nil {
   144  		ex = ie.s.newConnExecutor(
   145  			ctx,
   146  			sd,
   147  			nil, /* sdDefaults */
   148  			stmtBuf,
   149  			clientComm,
   150  			ie.memMetrics,
   151  			&ie.s.InternalMetrics,
   152  			appStats,
   153  		)
   154  	} else {
   155  		ex = ie.s.newConnExecutorWithTxn(
   156  			ctx,
   157  			sd,
   158  			nil, /* sdDefaults */
   159  			stmtBuf,
   160  			clientComm,
   161  			ie.mon,
   162  			ie.memMetrics,
   163  			&ie.s.InternalMetrics,
   164  			txn,
   165  			ie.tcModifier,
   166  			appStats,
   167  		)
   168  	}
   169  	ex.executorType = executorTypeInternal
   170  
   171  	var wg sync.WaitGroup
   172  	wg.Add(1)
   173  	go func() {
   174  		if err := ex.run(ctx, ie.mon, mon.BoundAccount{} /*reserved*/, nil /* cancel */); err != nil {
   175  			sqltelemetry.RecordError(ctx, err, &ex.server.cfg.Settings.SV)
   176  			errCallback(err)
   177  		}
   178  		closeMode := normalClose
   179  		if txn != nil {
   180  			closeMode = externalTxnClose
   181  		}
   182  		ex.close(ctx, closeMode)
   183  		wg.Done()
   184  	}()
   185  	return stmtBuf, &wg, nil
   186  }
   187  
   188  // Query executes the supplied SQL statement and returns the resulting rows.
   189  // If no user has been previously set through SetSessionData, the statement is
   190  // executed as the root user.
   191  //
   192  // If txn is not nil, the statement will be executed in the respective txn.
   193  //
   194  // Query is deprecated because it may transparently execute a query as root. Use
   195  // QueryEx instead.
   196  func (ie *InternalExecutor) Query(
   197  	ctx context.Context, opName string, txn *kv.Txn, stmt string, qargs ...interface{},
   198  ) ([]tree.Datums, error) {
   199  	return ie.QueryEx(ctx, opName, txn, ie.maybeRootSessionDataOverride(opName), stmt, qargs...)
   200  }
   201  
   202  // QueryEx is like Query, but allows the caller to override some session data
   203  // fields (e.g. the user).
   204  //
   205  // The fields set in session that are set override the respective fields if they
   206  // have previously been set through SetSessionData().
   207  func (ie *InternalExecutor) QueryEx(
   208  	ctx context.Context,
   209  	opName string,
   210  	txn *kv.Txn,
   211  	session sqlbase.InternalExecutorSessionDataOverride,
   212  	stmt string,
   213  	qargs ...interface{},
   214  ) ([]tree.Datums, error) {
   215  	datums, _, err := ie.queryInternal(ctx, opName, txn, session, stmt, qargs...)
   216  	return datums, err
   217  }
   218  
   219  // QueryWithCols is like QueryEx, but it also returns the computed ResultColumns
   220  // of the input query.
   221  func (ie *InternalExecutor) QueryWithCols(
   222  	ctx context.Context,
   223  	opName string,
   224  	txn *kv.Txn,
   225  	session sqlbase.InternalExecutorSessionDataOverride,
   226  	stmt string,
   227  	qargs ...interface{},
   228  ) ([]tree.Datums, sqlbase.ResultColumns, error) {
   229  	return ie.queryInternal(ctx, opName, txn, session, stmt, qargs...)
   230  }
   231  
   232  func (ie *InternalExecutor) queryInternal(
   233  	ctx context.Context,
   234  	opName string,
   235  	txn *kv.Txn,
   236  	sessionDataOverride sqlbase.InternalExecutorSessionDataOverride,
   237  	stmt string,
   238  	qargs ...interface{},
   239  ) ([]tree.Datums, sqlbase.ResultColumns, error) {
   240  	res, err := ie.execInternal(ctx, opName, txn, sessionDataOverride, stmt, qargs...)
   241  	if err != nil {
   242  		return nil, nil, err
   243  	}
   244  	return res.rows, res.cols, res.err
   245  }
   246  
   247  // QueryRow is like Query, except it returns a single row, or nil if not row is
   248  // found, or an error if more that one row is returned.
   249  //
   250  // QueryRow is deprecated (like Query). Use QueryRowEx() instead.
   251  func (ie *InternalExecutor) QueryRow(
   252  	ctx context.Context, opName string, txn *kv.Txn, stmt string, qargs ...interface{},
   253  ) (tree.Datums, error) {
   254  	return ie.QueryRowEx(ctx, opName, txn, ie.maybeRootSessionDataOverride(opName), stmt, qargs...)
   255  }
   256  
   257  // QueryRowEx is like QueryRow, but allows the caller to override some session data
   258  // fields (e.g. the user).
   259  //
   260  // The fields set in session that are set override the respective fields if they
   261  // have previously been set through SetSessionData().
   262  func (ie *InternalExecutor) QueryRowEx(
   263  	ctx context.Context,
   264  	opName string,
   265  	txn *kv.Txn,
   266  	session sqlbase.InternalExecutorSessionDataOverride,
   267  	stmt string,
   268  	qargs ...interface{},
   269  ) (tree.Datums, error) {
   270  	rows, err := ie.QueryEx(ctx, opName, txn, session, stmt, qargs...)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	switch len(rows) {
   275  	case 0:
   276  		return nil, nil
   277  	case 1:
   278  		return rows[0], nil
   279  	default:
   280  		return nil, &tree.MultipleResultsError{SQL: stmt}
   281  	}
   282  }
   283  
   284  // Exec executes the supplied SQL statement and returns the number of rows
   285  // affected (not like the results; see Query()). If no user has been previously
   286  // set through SetSessionData, the statement is executed as the root user.
   287  //
   288  // If txn is not nil, the statement will be executed in the respective txn.
   289  //
   290  // Exec is deprecated because it may transparently execute a query as root. Use
   291  // ExecEx instead.
   292  func (ie *InternalExecutor) Exec(
   293  	ctx context.Context, opName string, txn *kv.Txn, stmt string, qargs ...interface{},
   294  ) (int, error) {
   295  	return ie.ExecEx(ctx, opName, txn, ie.maybeRootSessionDataOverride(opName), stmt, qargs...)
   296  }
   297  
   298  // ExecEx is like Exec, but allows the caller to override some session data
   299  // fields (e.g. the user).
   300  //
   301  // The fields set in session that are set override the respective fields if they
   302  // have previously been set through SetSessionData().
   303  func (ie *InternalExecutor) ExecEx(
   304  	ctx context.Context,
   305  	opName string,
   306  	txn *kv.Txn,
   307  	session sqlbase.InternalExecutorSessionDataOverride,
   308  	stmt string,
   309  	qargs ...interface{},
   310  ) (int, error) {
   311  	res, err := ie.execInternal(ctx, opName, txn, session, stmt, qargs...)
   312  	if err != nil {
   313  		return 0, err
   314  	}
   315  	return res.rowsAffected, res.err
   316  }
   317  
   318  type result struct {
   319  	rows         []tree.Datums
   320  	rowsAffected int
   321  	cols         sqlbase.ResultColumns
   322  	err          error
   323  }
   324  
   325  // applyOverrides overrides the respective fields from sd for all the fields set on o.
   326  func applyOverrides(o sqlbase.InternalExecutorSessionDataOverride, sd *sessiondata.SessionData) {
   327  	if o.User != "" {
   328  		sd.User = o.User
   329  	}
   330  	if o.Database != "" {
   331  		sd.Database = o.Database
   332  	}
   333  	if o.ApplicationName != "" {
   334  		sd.ApplicationName = o.ApplicationName
   335  	}
   336  	if o.SearchPath != nil {
   337  		sd.SearchPath = *o.SearchPath
   338  	}
   339  }
   340  
   341  func (ie *InternalExecutor) maybeRootSessionDataOverride(
   342  	opName string,
   343  ) sqlbase.InternalExecutorSessionDataOverride {
   344  	if ie.sessionData == nil {
   345  		return sqlbase.InternalExecutorSessionDataOverride{
   346  			User:            security.RootUser,
   347  			ApplicationName: sqlbase.InternalAppNamePrefix + "-" + opName,
   348  		}
   349  	}
   350  	o := sqlbase.InternalExecutorSessionDataOverride{}
   351  	if ie.sessionData.User == "" {
   352  		o.User = security.RootUser
   353  	}
   354  	if ie.sessionData.ApplicationName == "" {
   355  		o.ApplicationName = sqlbase.InternalAppNamePrefix + "-" + opName
   356  	}
   357  	return o
   358  }
   359  
   360  // execInternal executes a statement.
   361  //
   362  // sessionDataOverride can be used to control select fields in the executor's
   363  // session data. It overrides what has been previously set through
   364  // SetSessionData(), if anything.
   365  func (ie *InternalExecutor) execInternal(
   366  	ctx context.Context,
   367  	opName string,
   368  	txn *kv.Txn,
   369  	sessionDataOverride sqlbase.InternalExecutorSessionDataOverride,
   370  	stmt string,
   371  	qargs ...interface{},
   372  ) (retRes result, retErr error) {
   373  	ctx = logtags.AddTag(ctx, "intExec", opName)
   374  
   375  	var sd *sessiondata.SessionData
   376  	if ie.sessionData != nil {
   377  		// TODO(andrei): Properly clone (deep copy) ie.sessionData.
   378  		sdCopy := *ie.sessionData
   379  		sd = &sdCopy
   380  	} else {
   381  		sd = ie.s.newSessionData(SessionArgs{})
   382  	}
   383  	applyOverrides(sessionDataOverride, sd)
   384  	if sd.User == "" {
   385  		return result{}, errors.AssertionFailedf("no user specified for internal query")
   386  	}
   387  	if sd.ApplicationName == "" {
   388  		sd.ApplicationName = sqlbase.InternalAppNamePrefix + "-" + opName
   389  	}
   390  
   391  	defer func() {
   392  		// We wrap errors with the opName, but not if they're retriable - in that
   393  		// case we need to leave the error intact so that it can be retried at a
   394  		// higher level.
   395  		//
   396  		// TODO(knz): track the callers and check whether opName could be turned
   397  		// into a type safe for reporting.
   398  		if retErr != nil && !errIsRetriable(retErr) {
   399  			retErr = errors.Wrapf(retErr, "%s", opName)
   400  		}
   401  		if retRes.err != nil && !errIsRetriable(retRes.err) {
   402  			retRes.err = errors.Wrapf(retRes.err, "%s", opName)
   403  		}
   404  	}()
   405  
   406  	ctx, sp := tracing.EnsureChildSpan(ctx, ie.s.cfg.AmbientCtx.Tracer, opName)
   407  	defer sp.Finish()
   408  
   409  	timeReceived := timeutil.Now()
   410  	parseStart := timeReceived
   411  	parsed, err := parser.ParseOne(stmt)
   412  	if err != nil {
   413  		return result{}, err
   414  	}
   415  	parseEnd := timeutil.Now()
   416  
   417  	// resPos will be set to the position of the command that represents the
   418  	// statement we care about before that command is sent for execution.
   419  	var resPos CmdPos
   420  
   421  	resCh := make(chan result)
   422  	var resultsReceived bool
   423  	syncCallback := func(results []resWithPos) {
   424  		resultsReceived = true
   425  		for _, res := range results {
   426  			if res.pos == resPos {
   427  				resCh <- result{rows: res.rows, rowsAffected: res.RowsAffected(), cols: res.cols, err: res.Err()}
   428  				return
   429  			}
   430  			if res.err != nil {
   431  				// If we encounter an error, there's no point in looking further; the
   432  				// rest of the commands in the batch have been skipped.
   433  				resCh <- result{err: res.Err()}
   434  				return
   435  			}
   436  		}
   437  		resCh <- result{err: errors.AssertionFailedf("missing result for pos: %d and no previous error", resPos)}
   438  	}
   439  	errCallback := func(err error) {
   440  		if resultsReceived {
   441  			return
   442  		}
   443  		resCh <- result{err: err}
   444  	}
   445  	stmtBuf, wg, err := ie.initConnEx(ctx, txn, sd, syncCallback, errCallback)
   446  	if err != nil {
   447  		return result{}, err
   448  	}
   449  
   450  	// Transforms the args to datums. The datum types will be passed as type hints
   451  	// to the PrepareStmt command.
   452  	datums, err := golangFillQueryArguments(qargs...)
   453  	if err != nil {
   454  		return result{}, err
   455  	}
   456  	typeHints := make(tree.PlaceholderTypes, len(datums))
   457  	for i, d := range datums {
   458  		// Arg numbers start from 1.
   459  		typeHints[tree.PlaceholderIdx(i)] = d.ResolvedType()
   460  	}
   461  	if len(qargs) == 0 {
   462  		resPos = 0
   463  		if err := stmtBuf.Push(
   464  			ctx,
   465  			ExecStmt{
   466  				Statement:    parsed,
   467  				TimeReceived: timeReceived,
   468  				ParseStart:   parseStart,
   469  				ParseEnd:     parseEnd,
   470  			}); err != nil {
   471  			return result{}, err
   472  		}
   473  	} else {
   474  		resPos = 2
   475  		if err := stmtBuf.Push(
   476  			ctx,
   477  			PrepareStmt{
   478  				Statement:  parsed,
   479  				ParseStart: parseStart,
   480  				ParseEnd:   parseEnd,
   481  				TypeHints:  typeHints,
   482  			},
   483  		); err != nil {
   484  			return result{}, err
   485  		}
   486  
   487  		if err := stmtBuf.Push(ctx, BindStmt{internalArgs: datums}); err != nil {
   488  			return result{}, err
   489  		}
   490  
   491  		if err := stmtBuf.Push(ctx, ExecPortal{TimeReceived: timeReceived}); err != nil {
   492  			return result{}, err
   493  		}
   494  	}
   495  	if err := stmtBuf.Push(ctx, Sync{}); err != nil {
   496  		return result{}, err
   497  	}
   498  
   499  	res := <-resCh
   500  	stmtBuf.Close()
   501  	wg.Wait()
   502  	return res, nil
   503  }
   504  
   505  // internalClientComm is an implementation of ClientComm used by the
   506  // InternalExecutor. Result rows are buffered in memory.
   507  type internalClientComm struct {
   508  	// results will contain the results of the commands executed by an
   509  	// InternalExecutor.
   510  	results []resWithPos
   511  
   512  	lastDelivered CmdPos
   513  
   514  	// sync, if set, is called whenever a Sync is executed. It returns all the
   515  	// results since the previous Sync.
   516  	sync func([]resWithPos)
   517  }
   518  
   519  type resWithPos struct {
   520  	*bufferedCommandResult
   521  	pos CmdPos
   522  }
   523  
   524  // CreateStatementResult is part of the ClientComm interface.
   525  func (icc *internalClientComm) CreateStatementResult(
   526  	_ tree.Statement,
   527  	_ RowDescOpt,
   528  	pos CmdPos,
   529  	_ []pgwirebase.FormatCode,
   530  	_ sessiondata.DataConversionConfig,
   531  	_ int,
   532  	_ string,
   533  	_ bool,
   534  ) CommandResult {
   535  	return icc.createRes(pos, nil /* onClose */)
   536  }
   537  
   538  // createRes creates a result. onClose, if not nil, is called when the result is
   539  // closed.
   540  func (icc *internalClientComm) createRes(pos CmdPos, onClose func(error)) *bufferedCommandResult {
   541  	res := &bufferedCommandResult{
   542  		closeCallback: func(res *bufferedCommandResult, typ resCloseType, err error) {
   543  			if typ == discarded {
   544  				return
   545  			}
   546  			icc.results = append(icc.results, resWithPos{bufferedCommandResult: res, pos: pos})
   547  			if onClose != nil {
   548  				onClose(err)
   549  			}
   550  		},
   551  	}
   552  	return res
   553  }
   554  
   555  // CreatePrepareResult is part of the ClientComm interface.
   556  func (icc *internalClientComm) CreatePrepareResult(pos CmdPos) ParseResult {
   557  	return icc.createRes(pos, nil /* onClose */)
   558  }
   559  
   560  // CreateBindResult is part of the ClientComm interface.
   561  func (icc *internalClientComm) CreateBindResult(pos CmdPos) BindResult {
   562  	return icc.createRes(pos, nil /* onClose */)
   563  }
   564  
   565  // CreateSyncResult is part of the ClientComm interface.
   566  //
   567  // The returned SyncResult will call the sync callback when its closed.
   568  func (icc *internalClientComm) CreateSyncResult(pos CmdPos) SyncResult {
   569  	return icc.createRes(pos, func(err error) {
   570  		results := make([]resWithPos, len(icc.results))
   571  		copy(results, icc.results)
   572  		icc.results = icc.results[:0]
   573  		icc.sync(results)
   574  		icc.lastDelivered = pos
   575  	} /* onClose */)
   576  }
   577  
   578  // LockCommunication is part of the ClientComm interface.
   579  func (icc *internalClientComm) LockCommunication() ClientLock {
   580  	return &noopClientLock{
   581  		clientComm: icc,
   582  	}
   583  }
   584  
   585  // Flush is part of the ClientComm interface.
   586  func (icc *internalClientComm) Flush(pos CmdPos) error {
   587  	return nil
   588  }
   589  
   590  // CreateDescribeResult is part of the ClientComm interface.
   591  func (icc *internalClientComm) CreateDescribeResult(pos CmdPos) DescribeResult {
   592  	return icc.createRes(pos, nil /* onClose */)
   593  }
   594  
   595  // CreateDeleteResult is part of the ClientComm interface.
   596  func (icc *internalClientComm) CreateDeleteResult(pos CmdPos) DeleteResult {
   597  	panic("unimplemented")
   598  }
   599  
   600  // CreateFlushResult is part of the ClientComm interface.
   601  func (icc *internalClientComm) CreateFlushResult(pos CmdPos) FlushResult {
   602  	panic("unimplemented")
   603  }
   604  
   605  // CreateErrorResult is part of the ClientComm interface.
   606  func (icc *internalClientComm) CreateErrorResult(pos CmdPos) ErrorResult {
   607  	panic("unimplemented")
   608  }
   609  
   610  // CreateEmptyQueryResult is part of the ClientComm interface.
   611  func (icc *internalClientComm) CreateEmptyQueryResult(pos CmdPos) EmptyQueryResult {
   612  	panic("unimplemented")
   613  }
   614  
   615  // CreateCopyInResult is part of the ClientComm interface.
   616  func (icc *internalClientComm) CreateCopyInResult(pos CmdPos) CopyInResult {
   617  	panic("unimplemented")
   618  }
   619  
   620  // CreateDrainResult is part of the ClientComm interface.
   621  func (icc *internalClientComm) CreateDrainResult(pos CmdPos) DrainResult {
   622  	panic("unimplemented")
   623  }
   624  
   625  // noopClientLock is an implementation of ClientLock that says that no results
   626  // have been communicated to the client.
   627  type noopClientLock struct {
   628  	clientComm *internalClientComm
   629  }
   630  
   631  // Close is part of the ClientLock interface.
   632  func (ncl *noopClientLock) Close() {}
   633  
   634  // ClientPos is part of the ClientLock interface.
   635  func (ncl *noopClientLock) ClientPos() CmdPos {
   636  	return ncl.clientComm.lastDelivered
   637  }
   638  
   639  // RTrim is part of the ClientLock interface.
   640  func (ncl *noopClientLock) RTrim(_ context.Context, pos CmdPos) {
   641  	var i int
   642  	var r resWithPos
   643  	for i, r = range ncl.clientComm.results {
   644  		if r.pos >= pos {
   645  			break
   646  		}
   647  	}
   648  	ncl.clientComm.results = ncl.clientComm.results[:i]
   649  }