github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/substring_tmpl.go (about) 1 // Copyright 2020 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 // {{/* 12 // +build execgen_template 13 // 14 // This file is the execgen template for substring.eg.go. It's formatted in a 15 // special way, so it's both valid Go and a valid text/template input. This 16 // permits editing this file with editor support. 17 // 18 // */}} 19 20 package colexec 21 22 import ( 23 "context" 24 "fmt" 25 26 "github.com/cockroachdb/cockroach/pkg/col/coldata" 27 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 28 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase/colexecerror" 29 "github.com/cockroachdb/cockroach/pkg/sql/colmem" 30 "github.com/cockroachdb/cockroach/pkg/sql/types" 31 "github.com/cockroachdb/errors" 32 ) 33 34 // {{/* 35 36 // _START_WIDTH is the template variable. 37 const _START_WIDTH = 0 38 39 // _LENGTH_WIDTH is the template variable. 40 const _LENGTH_WIDTH = 0 41 42 // */}} 43 44 func newSubstringOperator( 45 allocator *colmem.Allocator, 46 typs []*types.T, 47 argumentCols []int, 48 outputIdx int, 49 input colexecbase.Operator, 50 ) colexecbase.Operator { 51 startType := typs[argumentCols[1]] 52 lengthType := typs[argumentCols[2]] 53 base := substringFunctionBase{ 54 OneInputNode: NewOneInputNode(input), 55 allocator: allocator, 56 argumentCols: argumentCols, 57 outputIdx: outputIdx, 58 } 59 if startType.Family() != types.IntFamily { 60 colexecerror.InternalError(fmt.Sprintf("non-int start argument type %s", startType)) 61 } 62 if lengthType.Family() != types.IntFamily { 63 colexecerror.InternalError(fmt.Sprintf("non-int length argument type %s", lengthType)) 64 } 65 switch startType.Width() { 66 // {{range $startWidth, $lengthWidths := .}} 67 case _START_WIDTH: 68 switch lengthType.Width() { 69 // {{range $lengthWidth := $lengthWidths}} 70 case _LENGTH_WIDTH: 71 return &substring_StartType_LengthTypeOperator{base} 72 // {{end}} 73 } 74 // {{end}} 75 } 76 colexecerror.InternalError(errors.Errorf("unsupported substring argument types: %s %s", startType, lengthType)) 77 // This code is unreachable, but the compiler cannot infer that. 78 return nil 79 } 80 81 type substringFunctionBase struct { 82 OneInputNode 83 allocator *colmem.Allocator 84 argumentCols []int 85 outputIdx int 86 } 87 88 func (s *substringFunctionBase) Init() { 89 s.input.Init() 90 } 91 92 // {{range $startWidth, $lengthWidths := .}} 93 // {{range $lengthWidth := $lengthWidths}} 94 95 type substring_StartType_LengthTypeOperator struct { 96 substringFunctionBase 97 } 98 99 var _ colexecbase.Operator = &substring_StartType_LengthTypeOperator{} 100 101 func (s *substring_StartType_LengthTypeOperator) Next(ctx context.Context) coldata.Batch { 102 batch := s.input.Next(ctx) 103 n := batch.Length() 104 if n == 0 { 105 return coldata.ZeroBatch 106 } 107 108 sel := batch.Selection() 109 runeVec := batch.ColVec(s.argumentCols[0]).Bytes() 110 startVec := batch.ColVec(s.argumentCols[1])._StartType() 111 lengthVec := batch.ColVec(s.argumentCols[2])._LengthType() 112 outputVec := batch.ColVec(s.outputIdx) 113 if outputVec.MaybeHasNulls() { 114 // We need to make sure that there are no left over null values in the 115 // output vector. 116 outputVec.Nulls().UnsetNulls() 117 } 118 outputCol := outputVec.Bytes() 119 s.allocator.PerformOperation( 120 []coldata.Vec{outputVec}, 121 func() { 122 for i := 0; i < n; i++ { 123 rowIdx := i 124 if sel != nil { 125 rowIdx = sel[i] 126 } 127 128 // The substring operator does not support nulls. If any of the arguments 129 // are NULL, we output NULL. 130 isNull := false 131 for _, col := range s.argumentCols { 132 if batch.ColVec(col).Nulls().NullAt(rowIdx) { 133 isNull = true 134 break 135 } 136 } 137 if isNull { 138 batch.ColVec(s.outputIdx).Nulls().SetNull(rowIdx) 139 continue 140 } 141 142 runes := runeVec.Get(rowIdx) 143 // Substring start is 1 indexed. 144 start := int(startVec[rowIdx]) - 1 145 length := int(lengthVec[rowIdx]) 146 if length < 0 { 147 colexecerror.ExpectedError(errors.Errorf("negative substring length %d not allowed", length)) 148 } 149 150 end := start + length 151 // Check for integer overflow. 152 if end < start { 153 end = len(runes) 154 } else if end < 0 { 155 end = 0 156 } else if end > len(runes) { 157 end = len(runes) 158 } 159 160 if start < 0 { 161 start = 0 162 } else if start > len(runes) { 163 start = len(runes) 164 } 165 outputCol.Set(rowIdx, runes[start:end]) 166 } 167 }, 168 ) 169 // Although we didn't change the length of the batch, it is necessary to set 170 // the length anyway (this helps maintaining the invariant of flat bytes). 171 batch.SetLength(n) 172 return batch 173 } 174 175 // {{end}} 176 // {{end}}