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 }