github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/col/coldata/vec_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 coldata_test
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/col/coldata"
    18  	"github.com/cockroachdb/cockroach/pkg/col/coldatatestutils"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    20  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    21  	"github.com/cockroachdb/cockroach/pkg/util/randutil"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func TestMemColumnWindow(t *testing.T) {
    26  	defer leaktest.AfterTest(t)()
    27  
    28  	rng, _ := randutil.NewPseudoRand()
    29  
    30  	c := coldata.NewMemColumn(types.Int, coldata.BatchSize(), coldata.StandardColumnFactory)
    31  
    32  	ints := c.Int64()
    33  	for i := 0; i < coldata.BatchSize(); i++ {
    34  		ints[i] = int64(i)
    35  		if i%2 == 0 {
    36  			// Set every other value to null.
    37  			c.Nulls().SetNull(i)
    38  		}
    39  	}
    40  
    41  	startWindow := 1
    42  	endWindow := 0
    43  	for startWindow > endWindow {
    44  		startWindow = rng.Intn(coldata.BatchSize())
    45  		endWindow = 1 + rng.Intn(coldata.BatchSize())
    46  	}
    47  
    48  	window := c.Window(startWindow, endWindow)
    49  	windowInts := window.Int64()
    50  	// Verify that every other value is null.
    51  	for i, j := startWindow, 0; i < endWindow; i, j = i+1, j+1 {
    52  		if i%2 == 0 {
    53  			if !window.Nulls().NullAt(j) {
    54  				t.Fatalf("expected null at %d (original index: %d)", j, i)
    55  			}
    56  			continue
    57  		}
    58  		if ints[i] != windowInts[j] {
    59  			t.Fatalf("unexected value at index %d (original index: %d): expected %d got %d", j, i, ints[i], windowInts[j])
    60  		}
    61  	}
    62  }
    63  
    64  func TestNullRanges(t *testing.T) {
    65  	tcs := []struct {
    66  		start int
    67  		end   int
    68  	}{
    69  		{
    70  			start: 1,
    71  			end:   1,
    72  		},
    73  		{
    74  			start: 50,
    75  			end:   0,
    76  		},
    77  		{
    78  			start: 0,
    79  			end:   50,
    80  		},
    81  		{
    82  			start: 0,
    83  			end:   64,
    84  		},
    85  		{
    86  			start: 25,
    87  			end:   50,
    88  		},
    89  		{
    90  			start: 0,
    91  			end:   80,
    92  		},
    93  		{
    94  			start: 20,
    95  			end:   80,
    96  		},
    97  		{
    98  			start: 0,
    99  			end:   387,
   100  		},
   101  		{
   102  			start: 385,
   103  			end:   387,
   104  		},
   105  		{
   106  			start: 0,
   107  			end:   1023,
   108  		},
   109  		{
   110  			start: 1022,
   111  			end:   1023,
   112  		}, {
   113  			start: 1023,
   114  			end:   1023,
   115  		},
   116  	}
   117  
   118  	c := coldata.NewMemColumn(types.Int, coldata.BatchSize(), coldata.StandardColumnFactory)
   119  	for _, tc := range tcs {
   120  		c.Nulls().UnsetNulls()
   121  		c.Nulls().SetNullRange(tc.start, tc.end)
   122  
   123  		for i := 0; i < coldata.BatchSize(); i++ {
   124  			if i >= tc.start && i < tc.end {
   125  				if !c.Nulls().NullAt(i) {
   126  					t.Fatalf("expected null at %d, start: %d end: %d", i, tc.start, tc.end)
   127  				}
   128  			} else {
   129  				if c.Nulls().NullAt(i) {
   130  					t.Fatalf("expected non-null at %d, start: %d end: %d", i, tc.start, tc.end)
   131  				}
   132  			}
   133  		}
   134  	}
   135  }
   136  
   137  func TestAppend(t *testing.T) {
   138  	// TODO(asubiotto): Test nulls.
   139  	var typ = types.Int
   140  
   141  	src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   142  	sel := make([]int, len(src.Int64()))
   143  	for i := range sel {
   144  		sel[i] = i
   145  	}
   146  
   147  	testCases := []struct {
   148  		name           string
   149  		args           coldata.SliceArgs
   150  		expectedLength int
   151  	}{
   152  		{
   153  			name: "AppendSimple",
   154  			args: coldata.SliceArgs{
   155  				// DestIdx must be specified to append to the end of dest.
   156  				DestIdx: coldata.BatchSize(),
   157  			},
   158  			expectedLength: coldata.BatchSize() * 2,
   159  		},
   160  		{
   161  			name: "AppendOverwriteSimple",
   162  			args: coldata.SliceArgs{
   163  				// DestIdx 0, the default value, will start appending at index 0.
   164  				DestIdx: 0,
   165  			},
   166  			expectedLength: coldata.BatchSize(),
   167  		},
   168  		{
   169  			name: "AppendOverwriteSlice",
   170  			args: coldata.SliceArgs{
   171  				// Start appending at index 10.
   172  				DestIdx: 10,
   173  			},
   174  			expectedLength: coldata.BatchSize() + 10,
   175  		},
   176  		{
   177  			name: "AppendSlice",
   178  			args: coldata.SliceArgs{
   179  				DestIdx:     20,
   180  				SrcStartIdx: 10,
   181  				SrcEndIdx:   20,
   182  			},
   183  			expectedLength: 30,
   184  		},
   185  		{
   186  			name: "AppendWithSel",
   187  			args: coldata.SliceArgs{
   188  				DestIdx:     5,
   189  				SrcStartIdx: 10,
   190  				SrcEndIdx:   20,
   191  				Sel:         sel,
   192  			},
   193  			expectedLength: 15,
   194  		},
   195  		{
   196  			name: "AppendWithHalfSel",
   197  			args: coldata.SliceArgs{
   198  				DestIdx:   5,
   199  				Sel:       sel[:len(sel)/2],
   200  				SrcEndIdx: len(sel) / 2,
   201  			},
   202  			expectedLength: 5 + (coldata.BatchSize())/2,
   203  		},
   204  	}
   205  
   206  	for _, tc := range testCases {
   207  		tc.args.Src = src
   208  		if tc.args.SrcEndIdx == 0 {
   209  			// SrcEndIdx is always required.
   210  			tc.args.SrcEndIdx = coldata.BatchSize()
   211  		}
   212  		t.Run(tc.name, func(t *testing.T) {
   213  			dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   214  			dest.Append(tc.args)
   215  			require.Equal(t, tc.expectedLength, len(dest.Int64()))
   216  		})
   217  	}
   218  }
   219  
   220  func TestCopy(t *testing.T) {
   221  	// TODO(asubiotto): Test nulls.
   222  	var typ = types.Int
   223  
   224  	src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   225  	srcInts := src.Int64()
   226  	for i := range srcInts {
   227  		srcInts[i] = int64(i + 1)
   228  	}
   229  	sel := make([]int, len(src.Int64()))
   230  	for i := range sel {
   231  		sel[i] = i
   232  	}
   233  
   234  	sum := func(ints []int64) int {
   235  		s := 0
   236  		for _, i := range ints {
   237  			s += int(i)
   238  		}
   239  		return s
   240  	}
   241  
   242  	testCases := []struct {
   243  		name        string
   244  		args        coldata.CopySliceArgs
   245  		expectedSum int
   246  	}{
   247  		{
   248  			name:        "CopyNothing",
   249  			args:        coldata.CopySliceArgs{},
   250  			expectedSum: 0,
   251  		},
   252  		{
   253  			name: "CopyBatchSizeMinus1WithOffset1",
   254  			args: coldata.CopySliceArgs{
   255  				SliceArgs: coldata.SliceArgs{
   256  					// Use DestIdx 1 to make sure that it is respected.
   257  					DestIdx:   1,
   258  					SrcEndIdx: coldata.BatchSize() - 1,
   259  				},
   260  			},
   261  			// expectedSum uses sum of positive integers formula.
   262  			expectedSum: (coldata.BatchSize() - 1) * coldata.BatchSize() / 2,
   263  		},
   264  		{
   265  			name: "CopyWithSel",
   266  			args: coldata.CopySliceArgs{
   267  				SliceArgs: coldata.SliceArgs{
   268  					Sel:         sel[1:],
   269  					DestIdx:     25,
   270  					SrcStartIdx: 1,
   271  					SrcEndIdx:   2,
   272  				},
   273  			},
   274  			// We'll have just the third element in the resulting slice.
   275  			expectedSum: 3,
   276  		},
   277  	}
   278  
   279  	for _, tc := range testCases {
   280  		tc.args.Src = src
   281  		t.Run(tc.name, func(t *testing.T) {
   282  			dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   283  			dest.Copy(tc.args)
   284  			destInts := dest.Int64()
   285  			firstNonZero := 0
   286  			for i := range destInts {
   287  				if destInts[i] != 0 {
   288  					firstNonZero = i
   289  					break
   290  				}
   291  			}
   292  			// Verify that Copy started copying where we expected it to.
   293  			require.Equal(t, tc.args.DestIdx, firstNonZero)
   294  			require.Equal(t, tc.expectedSum, sum(destInts))
   295  		})
   296  	}
   297  }
   298  
   299  func TestCopyNulls(t *testing.T) {
   300  	var typ = types.Int
   301  
   302  	// Set up the destination vector.
   303  	dst := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   304  	dstInts := dst.Int64()
   305  	for i := range dstInts {
   306  		dstInts[i] = int64(1)
   307  	}
   308  	// Set some nulls in the destination vector.
   309  	for i := 0; i < 5; i++ {
   310  		dst.Nulls().SetNull(i)
   311  	}
   312  
   313  	// Set up the source vector.
   314  	src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   315  	srcInts := src.Int64()
   316  	for i := range srcInts {
   317  		srcInts[i] = 2
   318  	}
   319  	// Set some nulls in the source.
   320  	for i := 3; i < 8; i++ {
   321  		src.Nulls().SetNull(i)
   322  	}
   323  
   324  	copyArgs := coldata.CopySliceArgs{
   325  		SliceArgs: coldata.SliceArgs{
   326  			Src:         src,
   327  			DestIdx:     3,
   328  			SrcStartIdx: 3,
   329  			SrcEndIdx:   10,
   330  		},
   331  	}
   332  
   333  	dst.Copy(copyArgs)
   334  
   335  	// Verify that original nulls aren't deleted, and that
   336  	// the nulls in the source have been copied over.
   337  	for i := 0; i < 8; i++ {
   338  		require.True(t, dst.Nulls().NullAt(i), "expected null at %d, found not null", i)
   339  	}
   340  
   341  	// Verify that the data from src has been copied over.
   342  	for i := 8; i < 10; i++ {
   343  		require.True(t, dstInts[i] == 2, "data from src was not copied over")
   344  		require.True(t, !dst.Nulls().NullAt(i), "no extra nulls were added")
   345  	}
   346  
   347  	// Verify that the remaining elements in dst have not been touched.
   348  	for i := 10; i < coldata.BatchSize(); i++ {
   349  		require.True(t, dstInts[i] == 1, "data in dst outside copy range has been changed")
   350  		require.True(t, !dst.Nulls().NullAt(i), "no extra nulls were added")
   351  	}
   352  }
   353  
   354  func TestCopySelOnDestDoesNotUnsetOldNulls(t *testing.T) {
   355  	var typ = types.Int
   356  
   357  	// Set up the destination vector. It is all nulls except for a single
   358  	// non-null at index 0.
   359  	dst := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   360  	dstInts := dst.Int64()
   361  	for i := range dstInts {
   362  		dstInts[i] = 1
   363  	}
   364  	dst.Nulls().SetNulls()
   365  	dst.Nulls().UnsetNull(0)
   366  
   367  	// Set up the source vector with two nulls.
   368  	src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   369  	srcInts := src.Int64()
   370  	for i := range srcInts {
   371  		srcInts[i] = 2
   372  	}
   373  	src.Nulls().SetNull(0)
   374  	src.Nulls().SetNull(3)
   375  
   376  	// Using a small selection vector and SelOnDest, perform a copy and verify
   377  	// that nulls in between the selected tuples weren't unset.
   378  	copyArgs := coldata.CopySliceArgs{
   379  		SelOnDest: true,
   380  		SliceArgs: coldata.SliceArgs{
   381  			Src:         src,
   382  			SrcStartIdx: 1,
   383  			SrcEndIdx:   3,
   384  			Sel:         []int{0, 1, 3},
   385  		},
   386  	}
   387  
   388  	dst.Copy(copyArgs)
   389  
   390  	// 0 was not null in dest and null in source, but it wasn't selected. Not null.
   391  	require.False(t, dst.Nulls().NullAt(0))
   392  	// 1 was null in dest and not null in source: it becomes not null.
   393  	require.False(t, dst.Nulls().NullAt(1))
   394  	// 2 wasn't included in the selection vector: it stays null.
   395  	require.True(t, dst.Nulls().NullAt(2))
   396  	// 3 was null in dest and null in source: it stays null.
   397  	require.True(t, dst.Nulls().NullAt(3))
   398  	// 4 wasn't included: it stays null.
   399  	require.True(t, dst.Nulls().NullAt(4))
   400  }
   401  
   402  func BenchmarkAppend(b *testing.B) {
   403  	rng, _ := randutil.NewPseudoRand()
   404  	sel := rng.Perm(coldata.BatchSize())
   405  
   406  	benchCases := []struct {
   407  		name string
   408  		args coldata.SliceArgs
   409  	}{
   410  		{
   411  			name: "AppendSimple",
   412  			args: coldata.SliceArgs{},
   413  		},
   414  		{
   415  			name: "AppendWithSel",
   416  			args: coldata.SliceArgs{
   417  				Sel: sel,
   418  			},
   419  		},
   420  	}
   421  
   422  	for _, typ := range []*types.T{types.Bytes, types.Decimal, types.Int} {
   423  		for _, nullProbability := range []float64{0, 0.2} {
   424  			src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   425  			coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{
   426  				Rand:             rng,
   427  				Vec:              src,
   428  				N:                coldata.BatchSize(),
   429  				NullProbability:  nullProbability,
   430  				BytesFixedLength: 8,
   431  			})
   432  			for _, bc := range benchCases {
   433  				bc.args.Src = src
   434  				bc.args.SrcEndIdx = coldata.BatchSize()
   435  				dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   436  				b.Run(fmt.Sprintf("%s/%s/NullProbability=%.1f", typ, bc.name, nullProbability), func(b *testing.B) {
   437  					b.SetBytes(8 * int64(coldata.BatchSize()))
   438  					bc.args.DestIdx = 0
   439  					for i := 0; i < b.N; i++ {
   440  						dest.Append(bc.args)
   441  						bc.args.DestIdx += coldata.BatchSize()
   442  					}
   443  				})
   444  			}
   445  		}
   446  	}
   447  }
   448  
   449  func BenchmarkCopy(b *testing.B) {
   450  	rng, _ := randutil.NewPseudoRand()
   451  	sel := rng.Perm(coldata.BatchSize())
   452  
   453  	benchCases := []struct {
   454  		name string
   455  		args coldata.CopySliceArgs
   456  	}{
   457  		{
   458  			name: "CopySimple",
   459  			args: coldata.CopySliceArgs{},
   460  		},
   461  		{
   462  			name: "CopyWithSel",
   463  			args: coldata.CopySliceArgs{
   464  				SliceArgs: coldata.SliceArgs{
   465  					Sel: sel,
   466  				},
   467  			},
   468  		},
   469  	}
   470  
   471  	for _, typ := range []*types.T{types.Bytes, types.Decimal, types.Int} {
   472  		for _, nullProbability := range []float64{0, 0.2} {
   473  			src := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   474  			coldatatestutils.RandomVec(coldatatestutils.RandomVecArgs{
   475  				Rand:             rng,
   476  				Vec:              src,
   477  				N:                coldata.BatchSize(),
   478  				NullProbability:  nullProbability,
   479  				BytesFixedLength: 8,
   480  			})
   481  			for _, bc := range benchCases {
   482  				bc.args.Src = src
   483  				bc.args.SrcEndIdx = coldata.BatchSize()
   484  				dest := coldata.NewMemColumn(typ, coldata.BatchSize(), coldata.StandardColumnFactory)
   485  				b.Run(fmt.Sprintf("%s/%s/NullProbability=%.1f", typ, bc.name, nullProbability), func(b *testing.B) {
   486  					b.SetBytes(8 * int64(coldata.BatchSize()))
   487  					for i := 0; i < b.N; i++ {
   488  						dest.Copy(bc.args)
   489  						if typ.Identical(types.Bytes) {
   490  							// We need to reset flat bytes so that we could copy into it
   491  							// (otherwise it'll panic on the second copy due to maxSetIndex
   492  							// being not zero).
   493  							dest.Bytes().Reset()
   494  						}
   495  					}
   496  				})
   497  			}
   498  		}
   499  	}
   500  }