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

     1  // Copyright 2019 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 colexecbase
    12  
    13  import (
    14  	"context"
    15  	"fmt"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/colmem"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  // BatchBuffer exposes a buffer of coldata.Batches through an Operator
    25  // interface. If there are no batches to return, Next will panic.
    26  type BatchBuffer struct {
    27  	ZeroInputNode
    28  	buffer []coldata.Batch
    29  }
    30  
    31  var _ Operator = &BatchBuffer{}
    32  
    33  // NewBatchBuffer creates a new BatchBuffer.
    34  func NewBatchBuffer() *BatchBuffer {
    35  	return &BatchBuffer{
    36  		buffer: make([]coldata.Batch, 0, 2),
    37  	}
    38  }
    39  
    40  // Add adds a batch to the buffer.
    41  func (b *BatchBuffer) Add(batch coldata.Batch, _ []*types.T) {
    42  	b.buffer = append(b.buffer, batch)
    43  }
    44  
    45  // Init is part of the Operator interface.
    46  func (b *BatchBuffer) Init() {}
    47  
    48  // Next is part of the Operator interface.
    49  func (b *BatchBuffer) Next(context.Context) coldata.Batch {
    50  	batch := b.buffer[0]
    51  	b.buffer = b.buffer[1:]
    52  	return batch
    53  }
    54  
    55  // RepeatableBatchSource is an Operator that returns the same batch forever.
    56  type RepeatableBatchSource struct {
    57  	ZeroInputNode
    58  
    59  	colVecs  []coldata.Vec
    60  	typs     []*types.T
    61  	sel      []int
    62  	batchLen int
    63  	// numToCopy indicates the number of tuples that needs to be copied. It is
    64  	// equal to batchLen when sel is nil and is equal to maxSelIdx+1 when sel is
    65  	// non-nil.
    66  	numToCopy int
    67  	output    coldata.Batch
    68  
    69  	batchesToReturn int
    70  	batchesReturned int
    71  }
    72  
    73  var _ Operator = &RepeatableBatchSource{}
    74  
    75  // NewRepeatableBatchSource returns a new Operator initialized to return its
    76  // input batch forever. Note that it stores the contents of the input batch and
    77  // copies them into a separate output batch. The output batch is allowed to be
    78  // modified whereas the input batch is *not*.
    79  func NewRepeatableBatchSource(
    80  	allocator *colmem.Allocator, batch coldata.Batch, typs []*types.T,
    81  ) *RepeatableBatchSource {
    82  	sel := batch.Selection()
    83  	batchLen := batch.Length()
    84  	numToCopy := batchLen
    85  	if sel != nil {
    86  		maxIdx := 0
    87  		for _, selIdx := range sel[:batchLen] {
    88  			if selIdx > maxIdx {
    89  				maxIdx = selIdx
    90  			}
    91  		}
    92  		numToCopy = maxIdx + 1
    93  	}
    94  	output := allocator.NewMemBatchWithSize(typs, numToCopy)
    95  	src := &RepeatableBatchSource{
    96  		colVecs:   batch.ColVecs(),
    97  		typs:      typs,
    98  		sel:       sel,
    99  		batchLen:  batchLen,
   100  		numToCopy: numToCopy,
   101  		output:    output,
   102  	}
   103  	return src
   104  }
   105  
   106  // Next is part of the Operator interface.
   107  func (s *RepeatableBatchSource) Next(context.Context) coldata.Batch {
   108  	s.batchesReturned++
   109  	if s.batchesToReturn != 0 && s.batchesReturned > s.batchesToReturn {
   110  		return coldata.ZeroBatch
   111  	}
   112  	s.output.SetSelection(s.sel != nil)
   113  	if s.sel != nil {
   114  		copy(s.output.Selection()[:s.batchLen], s.sel[:s.batchLen])
   115  	}
   116  	for i, colVec := range s.colVecs {
   117  		// This Copy is outside of the allocator since the RepeatableBatchSource is
   118  		// a test utility which is often used in the benchmarks, and we want to
   119  		// reduce the performance impact of this operator.
   120  		s.output.ColVec(i).Copy(coldata.CopySliceArgs{
   121  			SliceArgs: coldata.SliceArgs{
   122  				Src:       colVec,
   123  				SrcEndIdx: s.numToCopy,
   124  			},
   125  		})
   126  	}
   127  	s.output.SetLength(s.batchLen)
   128  	return s.output
   129  }
   130  
   131  // Init is part of the Operator interface.
   132  func (s *RepeatableBatchSource) Init() {}
   133  
   134  // ResetBatchesToReturn sets a limit on how many batches the source returns, as
   135  // well as resetting how many batches the source has returned so far.
   136  func (s *RepeatableBatchSource) ResetBatchesToReturn(b int) {
   137  	s.batchesToReturn = b
   138  	s.batchesReturned = 0
   139  }
   140  
   141  // CallbackOperator is a testing utility struct that delegates Next calls to a
   142  // callback provided by the user.
   143  type CallbackOperator struct {
   144  	ZeroInputNode
   145  	NextCb func(ctx context.Context) coldata.Batch
   146  }
   147  
   148  // Init is part of the Operator interface.
   149  func (o *CallbackOperator) Init() {}
   150  
   151  // Next is part of the Operator interface.
   152  func (o *CallbackOperator) Next(ctx context.Context) coldata.Batch {
   153  	return o.NextCb(ctx)
   154  }
   155  
   156  // TestingSemaphore is a semaphore.Semaphore that never blocks and is always
   157  // successful. If the requested number of resources exceeds the given limit, an
   158  // error is returned. If too many resources are released, the semaphore panics.
   159  type TestingSemaphore struct {
   160  	count int
   161  	limit int
   162  }
   163  
   164  // NewTestingSemaphore initializes a new TestingSemaphore with the provided
   165  // limit. If limit is zero, there will be no limit. Can also use
   166  // &TestingSemaphore{} directly in this case.
   167  func NewTestingSemaphore(limit int) *TestingSemaphore {
   168  	return &TestingSemaphore{limit: limit}
   169  }
   170  
   171  // Acquire implements the semaphore.Semaphore interface.
   172  func (s *TestingSemaphore) Acquire(_ context.Context, n int) error {
   173  	if n < 0 {
   174  		return errors.New("acquiring a negative amount")
   175  	}
   176  	if s.limit != 0 && s.count+n > s.limit {
   177  		return errors.Errorf("testing semaphore limit exceeded: tried acquiring %d but already have a count of %d from a total limit of %d", n, s.count, s.limit)
   178  	}
   179  	s.count += n
   180  	return nil
   181  }
   182  
   183  // TryAcquire implements the semaphore.Semaphore interface.
   184  func (s *TestingSemaphore) TryAcquire(n int) bool {
   185  	if s.limit != 0 && s.count+n > s.limit {
   186  		return false
   187  	}
   188  	s.count += n
   189  	return true
   190  }
   191  
   192  // Release implements the semaphore.Semaphore interface.
   193  func (s *TestingSemaphore) Release(n int) int {
   194  	if n < 0 {
   195  		colexecerror.InternalError("releasing a negative amount")
   196  	}
   197  	if s.count-n < 0 {
   198  		colexecerror.InternalError(fmt.Sprintf("testing semaphore too many resources released, releasing %d, have %d", n, s.count))
   199  	}
   200  	pre := s.count
   201  	s.count -= n
   202  	return pre
   203  }
   204  
   205  // SetLimit implements the semaphore.Semaphore interface.
   206  func (s *TestingSemaphore) SetLimit(n int) {
   207  	s.limit = n
   208  }
   209  
   210  // GetLimit implements the semaphore.Semaphore interface.
   211  func (s *TestingSemaphore) GetLimit() int {
   212  	return s.limit
   213  }
   214  
   215  // GetCount implements the semaphore.Semaphore interface.
   216  func (s *TestingSemaphore) GetCount() int {
   217  	return s.count
   218  }