github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/props/col_stats_map_test.go (about)

     1  // Copyright 2018 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 props_test
    12  
    13  import (
    14  	"fmt"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    20  )
    21  
    22  func TestColStatsMap(t *testing.T) {
    23  	testcases := []struct {
    24  		cols     []opt.ColumnID
    25  		remove   bool
    26  		clear    bool
    27  		expected string
    28  	}{
    29  		{cols: []opt.ColumnID{1}, expected: "(1)"},
    30  		{cols: []opt.ColumnID{1}, expected: "(1)"},
    31  		{cols: []opt.ColumnID{2}, expected: "(1)+(2)"},
    32  		{cols: []opt.ColumnID{1, 2}, expected: "(1)+(2)+(1,2)"},
    33  		{cols: []opt.ColumnID{1, 2}, expected: "(1)+(2)+(1,2)"},
    34  		{cols: []opt.ColumnID{2}, expected: "(1)+(2)+(1,2)"},
    35  		{cols: []opt.ColumnID{1}, remove: true, expected: "(2)"},
    36  
    37  		// Add after removing.
    38  		{cols: []opt.ColumnID{2, 3}, expected: "(2)+(2,3)"},
    39  		{cols: []opt.ColumnID{2, 3, 4}, expected: "(2)+(2,3)+(2-4)"},
    40  		{cols: []opt.ColumnID{3}, expected: "(2)+(2,3)+(2-4)+(3)"},
    41  		{cols: []opt.ColumnID{3, 4}, expected: "(2)+(2,3)+(2-4)+(3)+(3,4)"},
    42  		{cols: []opt.ColumnID{5, 7}, expected: "(2)+(2,3)+(2-4)+(3)+(3,4)+(5,7)"},
    43  		{cols: []opt.ColumnID{5}, expected: "(2)+(2,3)+(2-4)+(3)+(3,4)+(5,7)+(5)"},
    44  		{cols: []opt.ColumnID{3, 4}, remove: true, expected: "(2)+(5,7)+(5)"},
    45  
    46  		// Add after clearing.
    47  		{cols: []opt.ColumnID{}, clear: true, expected: ""},
    48  		{cols: []opt.ColumnID{5}, expected: "(5)"},
    49  		{cols: []opt.ColumnID{1}, expected: "(5)+(1)"},
    50  		{cols: []opt.ColumnID{1, 5}, expected: "(5)+(1)+(1,5)"},
    51  		{cols: []opt.ColumnID{5, 6}, expected: "(5)+(1)+(1,5)+(5,6)"},
    52  		{cols: []opt.ColumnID{2}, expected: "(5)+(1)+(1,5)+(5,6)+(2)"},
    53  		{cols: []opt.ColumnID{1, 2}, expected: "(5)+(1)+(1,5)+(5,6)+(2)+(1,2)"},
    54  
    55  		// Remove node, where remaining nodes still require prefix tree index.
    56  		{cols: []opt.ColumnID{6}, remove: true, expected: "(5)+(1)+(1,5)+(2)+(1,2)"},
    57  		{cols: []opt.ColumnID{3, 4}, expected: "(5)+(1)+(1,5)+(2)+(1,2)+(3,4)"},
    58  	}
    59  
    60  	tcStats := make([]props.ColStatsMap, len(testcases))
    61  	// First calculate the stats for all steps, making copies every time. This
    62  	// also tests that the stats are copied correctly and there is no aliasing.
    63  	for tcIdx, tc := range testcases {
    64  		stats := &tcStats[tcIdx]
    65  		if tcIdx > 0 {
    66  			stats.CopyFrom(&tcStats[tcIdx-1])
    67  		}
    68  		cols := opt.MakeColSet(tc.cols...)
    69  		if !tc.remove {
    70  			if tc.clear {
    71  				stats.Clear()
    72  			} else {
    73  				stats.Add(cols)
    74  			}
    75  		} else {
    76  			stats.RemoveIntersecting(cols)
    77  		}
    78  	}
    79  
    80  	for tcIdx, tc := range testcases {
    81  		stats := &tcStats[tcIdx]
    82  		var b strings.Builder
    83  		for i := 0; i < stats.Count(); i++ {
    84  			get := stats.Get(i)
    85  			if i != 0 {
    86  				b.WriteRune('+')
    87  			}
    88  			fmt.Fprint(&b, get.Cols)
    89  
    90  			lookup, ok := stats.Lookup(get.Cols)
    91  			if !ok {
    92  				t.Errorf("could not find cols in map: %s", get.Cols)
    93  			}
    94  			if get != lookup {
    95  				t.Errorf("lookup did not return expected colstat: %+v vs. %+v", get, lookup)
    96  			}
    97  		}
    98  
    99  		if b.String() != tc.expected {
   100  			t.Errorf("expected: %s, actual: %s", tc.expected, b.String())
   101  		}
   102  	}
   103  }