github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/colexec/select_in_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  	"math/rand"
    17  	"testing"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    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/execinfrapb"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/parser"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/rowexec"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    28  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    29  )
    30  
    31  func TestSelectInInt64(t *testing.T) {
    32  	defer leaktest.AfterTest(t)()
    33  	testCases := []struct {
    34  		desc         string
    35  		inputTuples  tuples
    36  		outputTuples tuples
    37  		filterRow    []int64
    38  		hasNulls     bool
    39  		negate       bool
    40  	}{
    41  		{
    42  			desc:         "Simple in test",
    43  			inputTuples:  tuples{{0}, {1}, {2}},
    44  			outputTuples: tuples{{0}, {1}},
    45  			filterRow:    []int64{0, 1},
    46  			hasNulls:     false,
    47  			negate:       false,
    48  		},
    49  		{
    50  			desc:         "Simple not in test",
    51  			inputTuples:  tuples{{0}, {1}, {2}},
    52  			outputTuples: tuples{{2}},
    53  			filterRow:    []int64{0, 1},
    54  			hasNulls:     false,
    55  			negate:       true,
    56  		},
    57  		{
    58  			desc:         "In test with NULLs",
    59  			inputTuples:  tuples{{nil}, {1}, {2}},
    60  			outputTuples: tuples{{1}},
    61  			filterRow:    []int64{1},
    62  			hasNulls:     true,
    63  			negate:       false,
    64  		},
    65  		{
    66  			desc:         "Not in test with NULLs",
    67  			inputTuples:  tuples{{nil}, {1}, {2}},
    68  			outputTuples: tuples{},
    69  			filterRow:    []int64{1},
    70  			hasNulls:     true,
    71  			negate:       true,
    72  		},
    73  	}
    74  
    75  	for _, c := range testCases {
    76  		t.Run(c.desc, func(t *testing.T) {
    77  			opConstructor := func(input []colexecbase.Operator) (colexecbase.Operator, error) {
    78  				op := selectInOpInt64{
    79  					OneInputNode: NewOneInputNode(input[0]),
    80  					colIdx:       0,
    81  					filterRow:    c.filterRow,
    82  					negate:       c.negate,
    83  					hasNulls:     c.hasNulls,
    84  				}
    85  				return &op, nil
    86  			}
    87  			if !c.hasNulls || !c.negate {
    88  				runTests(t, []tuples{c.inputTuples}, c.outputTuples, orderedVerifier, opConstructor)
    89  			} else {
    90  				// When the input tuples already have nulls and we have NOT IN
    91  				// operator, then the nulls injection might not change the output. For
    92  				// example, we have this test case "1 NOT IN (NULL, 1, 2)" with the
    93  				// output of length 0; similarly, we will get the same zero-length
    94  				// output for the corresponding nulls injection test case
    95  				// "1 NOT IN (NULL, NULL, NULL)".
    96  				runTestsWithoutAllNullsInjection(t, []tuples{c.inputTuples}, nil /* typs */, c.outputTuples, orderedVerifier, opConstructor)
    97  			}
    98  		})
    99  	}
   100  }
   101  
   102  func benchmarkSelectInInt64(b *testing.B, useSelectionVector bool, hasNulls bool) {
   103  	ctx := context.Background()
   104  	typs := []*types.T{types.Int}
   105  	batch := testAllocator.NewMemBatch(typs)
   106  	col1 := batch.ColVec(0).Int64()
   107  
   108  	for i := 0; i < coldata.BatchSize(); i++ {
   109  		if float64(i) < float64(coldata.BatchSize())*selectivity {
   110  			col1[i] = -1
   111  		} else {
   112  			col1[i] = 1
   113  		}
   114  	}
   115  
   116  	if hasNulls {
   117  		for i := 0; i < coldata.BatchSize(); i++ {
   118  			if rand.Float64() < nullProbability {
   119  				batch.ColVec(0).Nulls().SetNull(i)
   120  			}
   121  		}
   122  	}
   123  
   124  	batch.SetLength(coldata.BatchSize())
   125  
   126  	if useSelectionVector {
   127  		batch.SetSelection(true)
   128  		sel := batch.Selection()
   129  		for i := 0; i < coldata.BatchSize(); i++ {
   130  			sel[i] = i
   131  		}
   132  	}
   133  
   134  	source := colexecbase.NewRepeatableBatchSource(testAllocator, batch, typs)
   135  	source.Init()
   136  	inOp := &selectInOpInt64{
   137  		OneInputNode: NewOneInputNode(source),
   138  		colIdx:       0,
   139  		filterRow:    []int64{1, 2, 3},
   140  	}
   141  	inOp.Init()
   142  
   143  	b.SetBytes(int64(8 * coldata.BatchSize()))
   144  	b.ResetTimer()
   145  	for i := 0; i < b.N; i++ {
   146  		inOp.Next(ctx)
   147  	}
   148  }
   149  
   150  func BenchmarkSelectInInt64(b *testing.B) {
   151  	for _, useSel := range []bool{true, false} {
   152  		for _, hasNulls := range []bool{true, false} {
   153  			b.Run(fmt.Sprintf("useSel=%t,hasNulls=%t", useSel, hasNulls), func(b *testing.B) {
   154  				benchmarkSelectInInt64(b, useSel, hasNulls)
   155  			})
   156  		}
   157  	}
   158  }
   159  
   160  func TestProjectInInt64(t *testing.T) {
   161  	defer leaktest.AfterTest(t)()
   162  	ctx := context.Background()
   163  	st := cluster.MakeTestingClusterSettings()
   164  	evalCtx := tree.MakeTestingEvalContext(st)
   165  	defer evalCtx.Stop(ctx)
   166  	flowCtx := &execinfra.FlowCtx{
   167  		EvalCtx: &evalCtx,
   168  		Cfg: &execinfra.ServerConfig{
   169  			Settings: st,
   170  		},
   171  	}
   172  	testCases := []struct {
   173  		desc         string
   174  		inputTuples  tuples
   175  		outputTuples tuples
   176  		inClause     string
   177  	}{
   178  		{
   179  			desc:         "Simple in test",
   180  			inputTuples:  tuples{{0}, {1}},
   181  			outputTuples: tuples{{0, true}, {1, true}},
   182  			inClause:     "IN (0, 1)",
   183  		},
   184  		{
   185  			desc:         "Simple not in test",
   186  			inputTuples:  tuples{{2}},
   187  			outputTuples: tuples{{2, true}},
   188  			inClause:     "NOT IN (0, 1)",
   189  		},
   190  		{
   191  			desc:         "In test with NULLs",
   192  			inputTuples:  tuples{{1}, {2}, {nil}},
   193  			outputTuples: tuples{{1, true}, {2, nil}, {nil, nil}},
   194  			inClause:     "IN (1, NULL)",
   195  		},
   196  		{
   197  			desc:         "Not in test with NULLs",
   198  			inputTuples:  tuples{{1}, {2}, {nil}},
   199  			outputTuples: tuples{{1, false}, {2, nil}, {nil, nil}},
   200  			inClause:     "NOT IN (1, NULL)",
   201  		},
   202  		{
   203  			desc:         "Not in test with NULLs and no nulls in filter",
   204  			inputTuples:  tuples{{1}, {2}, {nil}},
   205  			outputTuples: tuples{{1, false}, {2, true}, {nil, nil}},
   206  			inClause:     "NOT IN (1)",
   207  		},
   208  		{
   209  			desc:         "Test with false values",
   210  			inputTuples:  tuples{{1}, {2}},
   211  			outputTuples: tuples{{1, false}, {2, false}},
   212  			inClause:     "IN (3)",
   213  		},
   214  	}
   215  
   216  	for _, c := range testCases {
   217  		t.Run(c.desc, func(t *testing.T) {
   218  			runTests(t, []tuples{c.inputTuples}, c.outputTuples, orderedVerifier,
   219  				func(input []colexecbase.Operator) (colexecbase.Operator, error) {
   220  					expr, err := parser.ParseExpr(fmt.Sprintf("@1 %s", c.inClause))
   221  					if err != nil {
   222  						return nil, err
   223  					}
   224  					p := &mockTypeContext{typs: []*types.T{types.Int, types.MakeTuple([]*types.T{types.Int})}}
   225  					semaCtx := tree.MakeSemaContext()
   226  					semaCtx.IVarContainer = p
   227  					typedExpr, err := tree.TypeCheck(ctx, expr, &semaCtx, types.Any)
   228  					if err != nil {
   229  						return nil, err
   230  					}
   231  					spec := &execinfrapb.ProcessorSpec{
   232  						Input: []execinfrapb.InputSyncSpec{{ColumnTypes: []*types.T{types.Int}}},
   233  						Core: execinfrapb.ProcessorCoreUnion{
   234  							Noop: &execinfrapb.NoopCoreSpec{},
   235  						},
   236  						Post: execinfrapb.PostProcessSpec{
   237  							RenderExprs: []execinfrapb.Expression{
   238  								{Expr: "@1"},
   239  								{LocalExpr: typedExpr},
   240  							},
   241  						},
   242  					}
   243  					args := NewColOperatorArgs{
   244  						Spec:                spec,
   245  						Inputs:              input,
   246  						StreamingMemAccount: testMemAcc,
   247  						// TODO(yuzefovich): figure out how to make the second
   248  						// argument of IN comparison as DTuple not Tuple.
   249  						// TODO(yuzefovich): reuse createTestProjectingOperator
   250  						// once we don't need to provide the processor
   251  						// constructor.
   252  						ProcessorConstructor: rowexec.NewProcessor,
   253  					}
   254  					args.TestingKnobs.UseStreamingMemAccountForBuffering = true
   255  					result, err := NewColOperator(ctx, flowCtx, args)
   256  					if err != nil {
   257  						return nil, err
   258  					}
   259  					return result.Op, nil
   260  				})
   261  		})
   262  	}
   263  }