github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/col/coldata/nulls_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
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  // nulls3 is a nulls vector with every third value set to null.
    22  var nulls3 Nulls
    23  
    24  // nulls5 is a nulls vector with every fifth value set to null.
    25  var nulls5 Nulls
    26  
    27  // nulls10 is a double-length nulls vector with every tenth value set to null.
    28  var nulls10 Nulls
    29  
    30  // pos is a collection of interesting boundary indices to use in tests.
    31  var pos = []int{0, 1, 63, 64, 65, BatchSize() - 1, BatchSize()}
    32  
    33  func init() {
    34  	nulls3 = NewNulls(BatchSize())
    35  	nulls5 = NewNulls(BatchSize())
    36  	nulls10 = NewNulls(BatchSize() * 2)
    37  	for i := 0; i < BatchSize(); i++ {
    38  		if i%3 == 0 {
    39  			nulls3.SetNull(i)
    40  		}
    41  		if i%5 == 0 {
    42  			nulls5.SetNull(i)
    43  		}
    44  	}
    45  	for i := 0; i < BatchSize()*2; i++ {
    46  		if i%10 == 0 {
    47  			nulls10.SetNull(i)
    48  		}
    49  	}
    50  }
    51  
    52  func TestNullAt(t *testing.T) {
    53  	for i := 0; i < BatchSize(); i++ {
    54  		if i%3 == 0 {
    55  			require.True(t, nulls3.NullAt(i))
    56  		} else {
    57  			require.False(t, nulls3.NullAt(i))
    58  		}
    59  	}
    60  }
    61  
    62  func TestSetNullRange(t *testing.T) {
    63  	for _, start := range pos {
    64  		for _, end := range pos {
    65  			n := NewNulls(BatchSize())
    66  			n.SetNullRange(start, end)
    67  			for i := 0; i < BatchSize(); i++ {
    68  				expected := i >= start && i < end
    69  				require.Equal(t, expected, n.NullAt(i),
    70  					"NullAt(%d) should be %t after SetNullRange(%d, %d)", i, expected, start, end)
    71  			}
    72  		}
    73  	}
    74  }
    75  
    76  func TestUnsetNullRange(t *testing.T) {
    77  	for _, start := range pos {
    78  		for _, end := range pos {
    79  			n := NewNulls(BatchSize())
    80  			n.SetNulls()
    81  			n.UnsetNullRange(start, end)
    82  			for i := 0; i < BatchSize(); i++ {
    83  				notExpected := i >= start && i < end
    84  				require.NotEqual(t, notExpected, n.NullAt(i),
    85  					"NullAt(%d) saw %t, expected %t, after SetNullRange(%d, %d)", i, n.NullAt(i), !notExpected, start, end)
    86  			}
    87  		}
    88  	}
    89  }
    90  
    91  func TestSwapNulls(t *testing.T) {
    92  	n := NewNulls(BatchSize())
    93  	swapPos := []int{0, 1, 63, 64, 65, BatchSize() - 1}
    94  	idxInSwapPos := func(idx int) bool {
    95  		for _, p := range swapPos {
    96  			if p == idx {
    97  				return true
    98  			}
    99  		}
   100  		return false
   101  	}
   102  
   103  	t.Run("TestSwapNullWithNull", func(t *testing.T) {
   104  		// Test that swapping null with null doesn't change anything.
   105  		for _, p := range swapPos {
   106  			n.SetNull(p)
   107  		}
   108  		for _, i := range swapPos {
   109  			for _, j := range swapPos {
   110  				n.swap(i, j)
   111  				for k := 0; k < BatchSize(); k++ {
   112  					require.Equal(t, idxInSwapPos(k), n.NullAt(k),
   113  						"after swapping NULLS (%d, %d), NullAt(%d) saw %t, expected %t", i, j, k, n.NullAt(k), idxInSwapPos(k))
   114  				}
   115  			}
   116  		}
   117  	})
   118  
   119  	t.Run("TestSwapNullWithNotNull", func(t *testing.T) {
   120  		// Test that swapping null with not null changes things appropriately.
   121  		n.UnsetNulls()
   122  		swaps := map[int]int{
   123  			0:  BatchSize() - 1,
   124  			1:  62,
   125  			2:  3,
   126  			63: 65,
   127  			68: 120,
   128  		}
   129  		idxInSwaps := func(idx int) bool {
   130  			for k, v := range swaps {
   131  				if idx == k || idx == v {
   132  					return true
   133  				}
   134  			}
   135  			return false
   136  		}
   137  		for _, j := range swaps {
   138  			n.SetNull(j)
   139  		}
   140  		for i, j := range swaps {
   141  			n.swap(i, j)
   142  			require.Truef(t, n.NullAt(i), "after swapping not null and null (%d, %d), found null=%t at %d", i, j, n.NullAt(i), i)
   143  			require.Truef(t, !n.NullAt(j), "after swapping not null and null (%d, %d), found null=%t at %d", i, j, !n.NullAt(j), j)
   144  			for k := 0; k < BatchSize(); k++ {
   145  				if idxInSwaps(k) {
   146  					continue
   147  				}
   148  				require.Falsef(t, n.NullAt(k),
   149  					"after swapping NULLS (%d, %d), NullAt(%d) saw %t, expected false", i, j, k, n.NullAt(k))
   150  			}
   151  		}
   152  	})
   153  
   154  	t.Run("TestSwapNullWithNull", func(t *testing.T) {
   155  		// Test that swapping not null with not null doesn't do anything.
   156  		n.SetNulls()
   157  		for _, p := range swapPos {
   158  			n.UnsetNull(p)
   159  		}
   160  		for _, i := range swapPos {
   161  			for _, j := range swapPos {
   162  				n.swap(i, j)
   163  				for k := 0; k < BatchSize(); k++ {
   164  					require.Equal(t, idxInSwapPos(k), !n.NullAt(k),
   165  						"after swapping NULLS (%d, %d), NullAt(%d) saw %t, expected %t", i, j, k, !n.NullAt(k), idxInSwapPos(k))
   166  				}
   167  			}
   168  		}
   169  	})
   170  }
   171  
   172  func TestNullsTruncate(t *testing.T) {
   173  	for _, size := range pos {
   174  		n := NewNulls(BatchSize())
   175  		n.Truncate(size)
   176  		for i := 0; i < BatchSize(); i++ {
   177  			expected := i >= size
   178  			require.Equal(t, expected, n.NullAt(i),
   179  				"NullAt(%d) should be %t after Truncate(%d)", i, expected, size)
   180  		}
   181  	}
   182  }
   183  
   184  func TestUnsetNullsAfter(t *testing.T) {
   185  	for _, size := range pos {
   186  		n := NewNulls(BatchSize())
   187  		n.SetNulls()
   188  		n.UnsetNullsAfter(size)
   189  		for i := 0; i < BatchSize(); i++ {
   190  			expected := i < size
   191  			require.Equal(t, expected, n.NullAt(i),
   192  				"NullAt(%d) should be %t after UnsetNullsAfter(%d)", i, expected, size)
   193  		}
   194  	}
   195  }
   196  
   197  func TestSetAndUnsetNulls(t *testing.T) {
   198  	n := NewNulls(BatchSize())
   199  	for i := 0; i < BatchSize(); i++ {
   200  		require.False(t, n.NullAt(i))
   201  	}
   202  	n.SetNulls()
   203  	for i := 0; i < BatchSize(); i++ {
   204  		require.True(t, n.NullAt(i))
   205  	}
   206  
   207  	for i := 0; i < BatchSize(); i += 3 {
   208  		n.UnsetNull(i)
   209  	}
   210  	for i := 0; i < BatchSize(); i++ {
   211  		if i%3 == 0 {
   212  			require.False(t, n.NullAt(i))
   213  		} else {
   214  			require.True(t, n.NullAt(i))
   215  		}
   216  	}
   217  
   218  	n.UnsetNulls()
   219  	for i := 0; i < BatchSize(); i++ {
   220  		require.False(t, n.NullAt(i))
   221  	}
   222  }
   223  
   224  func TestNullsSet(t *testing.T) {
   225  	args := SliceArgs{
   226  		// Neither type nor the length here matter.
   227  		Src: NewMemColumn(types.Bool, 0, StandardColumnFactory),
   228  	}
   229  	for _, withSel := range []bool{false, true} {
   230  		t.Run(fmt.Sprintf("WithSel=%t", withSel), func(t *testing.T) {
   231  			var srcNulls *Nulls
   232  			if withSel {
   233  				args.Sel = make([]int, BatchSize())
   234  				// Make a selection vector with every even index. (This turns nulls10 into
   235  				// nulls5.)
   236  				for i := range args.Sel {
   237  					args.Sel[i] = i * 2
   238  				}
   239  				srcNulls = &nulls10
   240  			} else {
   241  				args.Sel = nil
   242  				srcNulls = &nulls5
   243  			}
   244  			for _, destStartIdx := range pos {
   245  				for _, srcStartIdx := range pos {
   246  					for _, srcEndIdx := range pos {
   247  						if destStartIdx <= srcStartIdx && srcStartIdx <= srcEndIdx {
   248  							toAppend := srcEndIdx - srcStartIdx
   249  							name := fmt.Sprintf("destStartIdx=%d,srcStartIdx=%d,toAppend=%d", destStartIdx,
   250  								srcStartIdx, toAppend)
   251  							t.Run(name, func(t *testing.T) {
   252  								n := nulls3.Copy()
   253  								args.Src.SetNulls(srcNulls)
   254  								args.DestIdx = destStartIdx
   255  								args.SrcStartIdx = srcStartIdx
   256  								args.SrcEndIdx = srcEndIdx
   257  								// Set some garbage values in the destination nulls that should
   258  								// be overwritten.
   259  								n.SetNullRange(destStartIdx, destStartIdx+toAppend)
   260  								n.set(args)
   261  								for i := 0; i < destStartIdx; i++ {
   262  									require.Equal(t, nulls3.NullAt(i), n.NullAt(i))
   263  								}
   264  								for i := 0; i < toAppend; i++ {
   265  									require.Equal(t, nulls5.NullAt(srcStartIdx+i), n.NullAt(destStartIdx+i))
   266  								}
   267  								for i := destStartIdx + toAppend; i < BatchSize(); i++ {
   268  									require.Equal(t, nulls3.NullAt(i), n.NullAt(i))
   269  								}
   270  							})
   271  						}
   272  					}
   273  				}
   274  			}
   275  		})
   276  	}
   277  }
   278  
   279  func TestSlice(t *testing.T) {
   280  	for _, start := range pos {
   281  		for _, end := range pos {
   282  			n := nulls3.Slice(start, end)
   283  			for i := 0; i < 8*len(n.nulls); i++ {
   284  				expected := start+i < end && nulls3.NullAt(start+i)
   285  				require.Equal(t, expected, n.NullAt(i),
   286  					"expected nulls3.Slice(%d, %d).NullAt(%d) to be %b", start, end, i, expected)
   287  			}
   288  		}
   289  	}
   290  	// Ensure we haven't modified the receiver.
   291  	for i := 0; i < BatchSize(); i++ {
   292  		expected := i%3 == 0
   293  		require.Equal(t, expected, nulls3.NullAt(i))
   294  	}
   295  }
   296  
   297  func TestNullsOr(t *testing.T) {
   298  	length1, length2 := 300, 400
   299  	n1 := nulls3.Slice(0, length1)
   300  	n2 := nulls5.Slice(0, length2)
   301  	or := n1.Or(&n2)
   302  	require.True(t, or.maybeHasNulls)
   303  	for i := 0; i < length2; i++ {
   304  		if i < length1 && n1.NullAt(i) || i < length2 && n2.NullAt(i) {
   305  			require.True(t, or.NullAt(i), "or.NullAt(%d) should be true", i)
   306  		} else {
   307  			require.False(t, or.NullAt(i), "or.NullAt(%d) should be false", i)
   308  		}
   309  	}
   310  }