github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/cast_test.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 colexec 12 13 import ( 14 "context" 15 "fmt" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/col/coldata" 19 "github.com/cockroachdb/cockroach/pkg/col/coldatatestutils" 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/sql/colexecbase" 22 "github.com/cockroachdb/cockroach/pkg/sql/execinfra" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 25 "github.com/cockroachdb/cockroach/pkg/sql/types" 26 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 27 "github.com/cockroachdb/cockroach/pkg/util/randutil" 28 "github.com/stretchr/testify/require" 29 ) 30 31 func TestRandomizedCast(t *testing.T) { 32 defer leaktest.AfterTest(t)() 33 ctx := context.Background() 34 st := cluster.MakeTestingClusterSettings() 35 evalCtx := tree.MakeTestingEvalContext(st) 36 defer evalCtx.Stop(ctx) 37 flowCtx := &execinfra.FlowCtx{ 38 EvalCtx: &evalCtx, 39 Cfg: &execinfra.ServerConfig{ 40 Settings: st, 41 }, 42 } 43 44 datumAsBool := func(d tree.Datum) interface{} { 45 return bool(tree.MustBeDBool(d)) 46 } 47 datumAsInt := func(d tree.Datum) interface{} { 48 return int(tree.MustBeDInt(d)) 49 } 50 datumAsFloat := func(d tree.Datum) interface{} { 51 return float64(tree.MustBeDFloat(d)) 52 } 53 datumAsDecimal := func(d tree.Datum) interface{} { 54 return tree.MustBeDDecimal(d).Decimal 55 } 56 57 tc := []struct { 58 fromTyp *types.T 59 fromPhysType func(tree.Datum) interface{} 60 toTyp *types.T 61 toPhysType func(tree.Datum) interface{} 62 // Some types casting can fail, so retry if we 63 // generate a datum that is unable to be casted. 64 retryGeneration bool 65 }{ 66 //bool -> t tests 67 {types.Bool, datumAsBool, types.Bool, datumAsBool, false}, 68 {types.Bool, datumAsBool, types.Int, datumAsInt, false}, 69 {types.Bool, datumAsBool, types.Float, datumAsFloat, false}, 70 // decimal -> t tests 71 {types.Decimal, datumAsDecimal, types.Bool, datumAsBool, false}, 72 // int -> t tests 73 {types.Int, datumAsInt, types.Bool, datumAsBool, false}, 74 {types.Int, datumAsInt, types.Float, datumAsFloat, false}, 75 {types.Int, datumAsInt, types.Decimal, datumAsDecimal, false}, 76 // float -> t tests 77 {types.Float, datumAsFloat, types.Bool, datumAsBool, false}, 78 // We can sometimes generate a float outside of the range of the integers, 79 // so we want to retry with generation if that occurs. 80 {types.Float, datumAsFloat, types.Int, datumAsInt, true}, 81 {types.Float, datumAsFloat, types.Decimal, datumAsDecimal, false}, 82 } 83 84 rng, _ := randutil.NewPseudoRand() 85 86 for _, c := range tc { 87 t.Run(fmt.Sprintf("%sTo%s", c.fromTyp.String(), c.toTyp.String()), func(t *testing.T) { 88 n := 100 89 // Make an input vector of length n. 90 input := tuples{} 91 output := tuples{} 92 for i := 0; i < n; i++ { 93 // We don't allow any NULL datums to be generated, so disable 94 // this ability in the RandDatum function. 95 fromDatum := sqlbase.RandDatum(rng, c.fromTyp, false) 96 var ( 97 toDatum tree.Datum 98 err error 99 ) 100 toDatum, err = tree.PerformCast(&evalCtx, fromDatum, c.toTyp) 101 if c.retryGeneration { 102 for err != nil { 103 // If we are allowed to retry, make a new datum and cast it on error. 104 fromDatum = sqlbase.RandDatum(rng, c.fromTyp, false) 105 toDatum, err = tree.PerformCast(&evalCtx, fromDatum, c.toTyp) 106 } 107 } else { 108 if err != nil { 109 t.Fatal(err) 110 } 111 } 112 input = append(input, tuple{c.fromPhysType(fromDatum)}) 113 output = append(output, tuple{c.fromPhysType(fromDatum), c.toPhysType(toDatum)}) 114 } 115 runTests(t, []tuples{input}, output, orderedVerifier, 116 func(input []colexecbase.Operator) (colexecbase.Operator, error) { 117 return createTestCastOperator(ctx, flowCtx, input[0], c.fromTyp, c.toTyp) 118 }) 119 }) 120 } 121 } 122 123 func BenchmarkCastOp(b *testing.B) { 124 ctx := context.Background() 125 st := cluster.MakeTestingClusterSettings() 126 evalCtx := tree.MakeTestingEvalContext(st) 127 defer evalCtx.Stop(ctx) 128 flowCtx := &execinfra.FlowCtx{ 129 EvalCtx: &evalCtx, 130 Cfg: &execinfra.ServerConfig{ 131 Settings: st, 132 }, 133 } 134 rng, _ := randutil.NewPseudoRand() 135 for _, typePair := range [][]*types.T{ 136 {types.Int, types.Float}, 137 {types.Int, types.Decimal}, 138 {types.Float, types.Decimal}, 139 } { 140 for _, useSel := range []bool{true, false} { 141 for _, hasNulls := range []bool{true, false} { 142 b.Run( 143 fmt.Sprintf("useSel=%t/hasNulls=%t/%s_to_%s", 144 useSel, hasNulls, typePair[0].Name(), typePair[1].Name(), 145 ), func(b *testing.B) { 146 nullProbability := nullProbability 147 if !hasNulls { 148 nullProbability = 0 149 } 150 selectivity := selectivity 151 if !useSel { 152 selectivity = 1.0 153 } 154 typs := []*types.T{typePair[0]} 155 batch := coldatatestutils.RandomBatchWithSel( 156 testAllocator, rng, typs, 157 coldata.BatchSize(), nullProbability, selectivity, 158 ) 159 source := colexecbase.NewRepeatableBatchSource(testAllocator, batch, typs) 160 op, err := createTestCastOperator(ctx, flowCtx, source, typePair[0], typePair[1]) 161 require.NoError(b, err) 162 b.SetBytes(int64(8 * coldata.BatchSize())) 163 b.ResetTimer() 164 op.Init() 165 for i := 0; i < b.N; i++ { 166 op.Next(ctx) 167 } 168 }) 169 } 170 } 171 } 172 } 173 174 func createTestCastOperator( 175 ctx context.Context, 176 flowCtx *execinfra.FlowCtx, 177 input colexecbase.Operator, 178 fromTyp *types.T, 179 toTyp *types.T, 180 ) (colexecbase.Operator, error) { 181 // We currently don't support casting to decimal type (other than when 182 // casting from decimal with the same precision), so we will allow falling 183 // back to row-by-row engine. 184 return createTestProjectingOperator( 185 ctx, flowCtx, input, []*types.T{fromTyp}, 186 fmt.Sprintf("@1::%s", toTyp.Name()), true, /* canFallbackToRowexec */ 187 ) 188 }