github.com/dolthub/go-mysql-server@v0.18.0/sql/stats/join_test.go (about)

     1  package stats
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/dolthub/go-mysql-server/sql"
    10  	"github.com/dolthub/go-mysql-server/sql/types"
    11  )
    12  
    13  func TestBinMerge(t *testing.T) {
    14  	tests := []struct {
    15  		inp []sql.HistogramBucket
    16  		exp []sql.HistogramBucket
    17  	}{
    18  		{
    19  			inp: sql.Histogram{
    20  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{2}, BoundCnt: 5},
    21  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{2}, BoundCnt: 5},
    22  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{3}, BoundCnt: 5},
    23  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{4}, BoundCnt: 5},
    24  			},
    25  			exp: sql.Histogram{
    26  				&Bucket{RowCnt: 10, DistinctCnt: 5, BoundVal: sql.Row{2}, BoundCnt: 5},
    27  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{3}, BoundCnt: 5},
    28  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{4}, BoundCnt: 5},
    29  			},
    30  		},
    31  		{
    32  			inp: sql.Histogram{
    33  				&Bucket{RowCnt: 5, DistinctCnt: 10, BoundVal: sql.Row{2}, BoundCnt: 5},
    34  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{3}, BoundCnt: 5},
    35  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{3}, BoundCnt: 5},
    36  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{4}, BoundCnt: 5},
    37  			},
    38  			exp: sql.Histogram{
    39  				&Bucket{RowCnt: 5, DistinctCnt: 10, BoundVal: sql.Row{2}, BoundCnt: 5},
    40  				&Bucket{RowCnt: 10, DistinctCnt: 1, BoundVal: sql.Row{3}, BoundCnt: 10},
    41  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{4}, BoundCnt: 5},
    42  			},
    43  		},
    44  		{
    45  			inp: sql.Histogram{
    46  				&Bucket{RowCnt: 5, DistinctCnt: 10, BoundVal: sql.Row{2}, BoundCnt: 5},
    47  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{2}, BoundCnt: 5},
    48  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{4}, BoundCnt: 5},
    49  				&Bucket{RowCnt: 5, DistinctCnt: 1, BoundVal: sql.Row{4}, BoundCnt: 5},
    50  			},
    51  			exp: sql.Histogram{
    52  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{2}, BoundCnt: 10},
    53  				&Bucket{RowCnt: 10, DistinctCnt: 5, BoundVal: sql.Row{4}, BoundCnt: 10},
    54  			},
    55  		},
    56  	}
    57  	for i, tt := range tests {
    58  		t.Run(fmt.Sprintf("bin merge %d", i), func(t *testing.T) {
    59  			cmp, err := mergeOverlappingBuckets(tt.inp, []sql.Type{types.Int64})
    60  			require.NoError(t, err)
    61  			compareHist(t, tt.exp, cmp)
    62  		})
    63  	}
    64  }
    65  
    66  func TestEuclideanDistance(t *testing.T) {
    67  	tests := []struct {
    68  		x, y sql.Row
    69  		dist float64
    70  	}{
    71  		{
    72  			x:    sql.Row{0, 3},
    73  			y:    sql.Row{4, 0},
    74  			dist: 5,
    75  		},
    76  		{
    77  			x:    sql.Row{5, 0, 0},
    78  			y:    sql.Row{0, 12, 0},
    79  			dist: 13,
    80  		},
    81  	}
    82  	for _, tt := range tests {
    83  		t.Run(fmt.Sprintf("%v x %v = %.2f", tt.x, tt.y, tt.dist), func(t *testing.T) {
    84  			cmp, err := euclideanDistance(tt.x, tt.y, len(tt.x))
    85  			require.NoError(t, err)
    86  			require.Equal(t, tt.dist, cmp)
    87  		})
    88  	}
    89  }
    90  
    91  func TestBinAlignment(t *testing.T) {
    92  	tests := []struct {
    93  		left     []sql.HistogramBucket
    94  		right    []sql.HistogramBucket
    95  		expLeft  []sql.HistogramBucket
    96  		expRight []sql.HistogramBucket
    97  	}{
    98  		{
    99  			left: []sql.HistogramBucket{
   100  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   101  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   102  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   103  			},
   104  			right: []sql.HistogramBucket{
   105  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   106  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   107  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   108  			},
   109  			expLeft: []sql.HistogramBucket{
   110  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   111  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   112  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{20}, BoundCnt: 1},
   113  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{30}, BoundCnt: 1},
   114  			},
   115  			expRight: []sql.HistogramBucket{
   116  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   117  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{10}, BoundCnt: 1},
   118  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{20}, BoundCnt: 1},
   119  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   120  			},
   121  		},
   122  		{
   123  			left: []sql.HistogramBucket{
   124  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   125  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   126  			},
   127  			right: []sql.HistogramBucket{
   128  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   129  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   130  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   131  			},
   132  			expLeft: []sql.HistogramBucket{
   133  				&Bucket{RowCnt: 12, DistinctCnt: 12, BoundVal: sql.Row{10}, BoundCnt: 1},
   134  				&Bucket{RowCnt: 2, DistinctCnt: 2, BoundVal: sql.Row{20}, BoundCnt: 1},
   135  				&Bucket{RowCnt: 6, DistinctCnt: 6, BoundVal: sql.Row{50}, BoundCnt: 1},
   136  			},
   137  			expRight: []sql.HistogramBucket{
   138  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   139  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   140  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   141  			},
   142  		},
   143  		{
   144  			left: []sql.HistogramBucket{
   145  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   146  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   147  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{40}, BoundCnt: 1},
   148  			},
   149  			right: []sql.HistogramBucket{
   150  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   151  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   152  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   153  			},
   154  			expLeft: []sql.HistogramBucket{
   155  				&Bucket{RowCnt: 23, DistinctCnt: 23, BoundVal: sql.Row{20}, BoundCnt: 1},
   156  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{30}, BoundCnt: 1},
   157  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{40}, BoundCnt: 1},
   158  			},
   159  			expRight: []sql.HistogramBucket{
   160  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   161  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   162  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   163  			},
   164  		},
   165  		{
   166  			left: []sql.HistogramBucket{
   167  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   168  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   169  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{40}, BoundCnt: 1},
   170  			},
   171  			right: []sql.HistogramBucket{
   172  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   173  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   174  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{60}, BoundCnt: 1},
   175  			},
   176  			expLeft: []sql.HistogramBucket{
   177  				&Bucket{RowCnt: 26, DistinctCnt: 26, BoundVal: sql.Row{30}, BoundCnt: 1},
   178  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{40}, BoundCnt: 1},
   179  			},
   180  			expRight: []sql.HistogramBucket{
   181  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   182  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   183  			},
   184  		},
   185  		{
   186  			left: []sql.HistogramBucket{
   187  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   188  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   189  			},
   190  			right: []sql.HistogramBucket{
   191  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   192  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   193  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{60}, BoundCnt: 1},
   194  			},
   195  			expLeft: []sql.HistogramBucket{
   196  				&Bucket{RowCnt: 20, DistinctCnt: 20, BoundVal: sql.Row{10}, BoundCnt: 1},
   197  			},
   198  			expRight: []sql.HistogramBucket{
   199  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   200  			},
   201  		},
   202  		{
   203  			left: []sql.HistogramBucket{
   204  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   205  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   206  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   207  			},
   208  			right: []sql.HistogramBucket{
   209  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   210  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   211  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{60}, BoundCnt: 1},
   212  			},
   213  			expLeft: []sql.HistogramBucket{
   214  				&Bucket{RowCnt: 30, DistinctCnt: 30, BoundVal: sql.Row{30}, BoundCnt: 1},
   215  			},
   216  			expRight: []sql.HistogramBucket{
   217  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   218  			},
   219  		},
   220  		{
   221  			left: []sql.HistogramBucket{
   222  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   223  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   224  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   225  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   226  			},
   227  			right: []sql.HistogramBucket{
   228  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   229  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   230  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{60}, BoundCnt: 1},
   231  			},
   232  			expLeft: []sql.HistogramBucket{
   233  				&Bucket{RowCnt: 30, DistinctCnt: 30, BoundVal: sql.Row{30}, BoundCnt: 1},
   234  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   235  			},
   236  			expRight: []sql.HistogramBucket{
   237  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   238  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   239  			},
   240  		},
   241  		{
   242  			left: []sql.HistogramBucket{
   243  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   244  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   245  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   246  			},
   247  			right: []sql.HistogramBucket{
   248  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   249  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   250  			},
   251  			expLeft: []sql.HistogramBucket{
   252  				&Bucket{RowCnt: 30, DistinctCnt: 30, BoundVal: sql.Row{20}, BoundCnt: 1},
   253  			},
   254  			expRight: []sql.HistogramBucket{
   255  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   256  			},
   257  		},
   258  		{
   259  			left: []sql.HistogramBucket{
   260  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   261  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   262  			},
   263  			right: []sql.HistogramBucket{
   264  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   265  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   266  			},
   267  			expLeft: []sql.HistogramBucket{
   268  				&Bucket{RowCnt: 20, DistinctCnt: 20, BoundVal: sql.Row{10}, BoundCnt: 1},
   269  			},
   270  			expRight: []sql.HistogramBucket{
   271  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   272  			},
   273  		},
   274  	}
   275  
   276  	cmp := func(i, j sql.Row) (int, error) {
   277  		return types.Int64.Compare(i[0], j[0])
   278  	}
   279  
   280  	for i, tt := range tests {
   281  		t.Run(fmt.Sprintf("alignment test %d", i), func(t *testing.T) {
   282  			lCmp, rCmp, err := AlignBuckets(tt.left, tt.right, nil, nil, []sql.Type{types.Int64}, []sql.Type{types.Int64}, cmp)
   283  			require.NoError(t, err)
   284  			compareHist(t, tt.expLeft, lCmp)
   285  			compareHist(t, tt.expRight, rCmp)
   286  		})
   287  	}
   288  }
   289  
   290  func TestJoin(t *testing.T) {
   291  	tests := []struct {
   292  		left  sql.Histogram
   293  		right sql.Histogram
   294  		exp   sql.Histogram
   295  	}{
   296  		{
   297  			left: []sql.HistogramBucket{
   298  				&Bucket{RowCnt: 20, DistinctCnt: 20, BoundVal: sql.Row{10}, BoundCnt: 1},
   299  			},
   300  			right: []sql.HistogramBucket{
   301  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   302  			},
   303  			exp: []sql.HistogramBucket{
   304  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   305  			},
   306  		},
   307  		{
   308  			left: []sql.HistogramBucket{
   309  				&Bucket{RowCnt: 20, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   310  			},
   311  			right: []sql.HistogramBucket{
   312  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   313  			},
   314  			exp: []sql.HistogramBucket{
   315  				&Bucket{RowCnt: 20, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   316  			},
   317  		},
   318  		{
   319  			left: []sql.HistogramBucket{
   320  				&Bucket{RowCnt: 20, DistinctCnt: 11, BoundVal: sql.Row{10}, McvVals: []sql.Row{{1}, {2}}, McvsCnt: []uint64{5, 5}, BoundCnt: 1},
   321  			},
   322  			right: []sql.HistogramBucket{
   323  				&Bucket{RowCnt: 10, DistinctCnt: 6, BoundVal: sql.Row{10}, McvVals: []sql.Row{{2}}, McvsCnt: []uint64{4}, BoundCnt: 1},
   324  			},
   325  			exp: []sql.HistogramBucket{
   326  				&Bucket{RowCnt: 29, DistinctCnt: 6, BoundVal: sql.Row{10}, BoundCnt: 1},
   327  			},
   328  		},
   329  		{
   330  			left: []sql.HistogramBucket{
   331  				&Bucket{RowCnt: 20, DistinctCnt: 10, BoundVal: sql.Row{10}, McvVals: []sql.Row{{1}, {2}}, McvsCnt: []uint64{5, 5}, BoundCnt: 1},
   332  			},
   333  			right: []sql.HistogramBucket{
   334  				&Bucket{RowCnt: 10, DistinctCnt: 6, BoundVal: sql.Row{10}, McvVals: []sql.Row{{3}}, McvsCnt: []uint64{4}, BoundCnt: 1},
   335  			},
   336  			exp: []sql.HistogramBucket{
   337  				&Bucket{RowCnt: 20, DistinctCnt: 6, BoundVal: sql.Row{10}, BoundCnt: 1},
   338  			},
   339  		},
   340  		{
   341  			left: []sql.HistogramBucket{
   342  				&Bucket{RowCnt: 20, DistinctCnt: 10, BoundVal: sql.Row{10}, McvVals: []sql.Row{{1}, {2}}, McvsCnt: []uint64{5, 5}, BoundCnt: 1},
   343  			},
   344  			right: []sql.HistogramBucket{
   345  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, McvVals: []sql.Row{{3}}, McvsCnt: []uint64{4}, BoundCnt: 1},
   346  			},
   347  			exp: []sql.HistogramBucket{
   348  				&Bucket{RowCnt: 20, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   349  			},
   350  		},
   351  		{
   352  			left: []sql.HistogramBucket{
   353  				&Bucket{RowCnt: 23, DistinctCnt: 23, BoundVal: sql.Row{20}, BoundCnt: 1},
   354  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{30}, BoundCnt: 1},
   355  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{40}, BoundCnt: 1},
   356  			},
   357  			right: []sql.HistogramBucket{
   358  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   359  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   360  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{50}, BoundCnt: 1},
   361  			},
   362  			exp: []sql.HistogramBucket{
   363  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   364  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{30}, BoundCnt: 1},
   365  				&Bucket{RowCnt: 3, DistinctCnt: 3, BoundVal: sql.Row{40}, BoundCnt: 1},
   366  			},
   367  		},
   368  		{
   369  			left: []sql.HistogramBucket{
   370  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   371  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   372  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{20}, BoundCnt: 1},
   373  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{30}, BoundCnt: 1},
   374  			},
   375  			right: []sql.HistogramBucket{
   376  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   377  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{10}, BoundCnt: 1},
   378  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{20}, BoundCnt: 1},
   379  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   380  			},
   381  			exp: []sql.HistogramBucket{
   382  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{0}, BoundCnt: 1},
   383  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{10}, BoundCnt: 1},
   384  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{20}, BoundCnt: 1},
   385  				&Bucket{RowCnt: 5, DistinctCnt: 5, BoundVal: sql.Row{30}, BoundCnt: 1},
   386  			},
   387  		},
   388  		{
   389  			left: []sql.HistogramBucket{
   390  				&Bucket{RowCnt: 12, DistinctCnt: 12, BoundVal: sql.Row{10}, BoundCnt: 1},
   391  				&Bucket{RowCnt: 6, DistinctCnt: 6, BoundVal: sql.Row{20}, BoundCnt: 1},
   392  				&Bucket{RowCnt: 1, DistinctCnt: 1, BoundVal: sql.Row{50}, BoundCnt: 1},
   393  			},
   394  			right: []sql.HistogramBucket{
   395  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   396  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{20}, BoundCnt: 1},
   397  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{30}, BoundCnt: 1},
   398  			},
   399  			exp: []sql.HistogramBucket{
   400  				&Bucket{RowCnt: 10, DistinctCnt: 10, BoundVal: sql.Row{10}, BoundCnt: 1},
   401  				&Bucket{RowCnt: 6, DistinctCnt: 6, BoundVal: sql.Row{20}, BoundCnt: 1},
   402  				&Bucket{RowCnt: 1, DistinctCnt: 1, BoundVal: sql.Row{50}, BoundCnt: 1},
   403  			},
   404  		},
   405  		{
   406  			left: []sql.HistogramBucket{
   407  				&Bucket{RowCnt: 10, DistinctCnt: 3, BoundVal: sql.Row{0}, BoundCnt: 1},
   408  				&Bucket{RowCnt: 10, DistinctCnt: 3, BoundVal: sql.Row{10}, BoundCnt: 1},
   409  				&Bucket{RowCnt: 5, DistinctCnt: 2, BoundVal: sql.Row{20}, BoundCnt: 1},
   410  				&Bucket{RowCnt: 5, DistinctCnt: 2, BoundVal: sql.Row{30}, BoundCnt: 1},
   411  			},
   412  			right: []sql.HistogramBucket{
   413  				&Bucket{RowCnt: 10, DistinctCnt: 3, BoundVal: sql.Row{0}, BoundCnt: 1},
   414  				&Bucket{RowCnt: 5, DistinctCnt: 2, BoundVal: sql.Row{10}, BoundCnt: 1},
   415  				&Bucket{RowCnt: 5, DistinctCnt: 2, BoundVal: sql.Row{20}, BoundCnt: 1},
   416  				&Bucket{RowCnt: 10, DistinctCnt: 3, BoundVal: sql.Row{30}, BoundCnt: 1},
   417  			},
   418  			exp: []sql.HistogramBucket{
   419  				&Bucket{RowCnt: 33, DistinctCnt: 3, BoundVal: sql.Row{0}, BoundCnt: 1},
   420  				&Bucket{RowCnt: 16, DistinctCnt: 2, BoundVal: sql.Row{10}, BoundCnt: 1},
   421  				&Bucket{RowCnt: 12, DistinctCnt: 2, BoundVal: sql.Row{20}, BoundCnt: 1},
   422  				&Bucket{RowCnt: 16, DistinctCnt: 2, BoundVal: sql.Row{30}, BoundCnt: 1},
   423  			},
   424  		},
   425  	}
   426  
   427  	cmp := func(i, j sql.Row) (int, error) {
   428  		return types.Int64.Compare(i[0], j[0])
   429  	}
   430  
   431  	for i, tt := range tests {
   432  		t.Run(fmt.Sprintf("join test %d", i), func(t *testing.T) {
   433  			cmp, err := joinAlignedStats(tt.left, tt.right, cmp)
   434  			require.NoError(t, err)
   435  			cmpHist := make(sql.Histogram, len(cmp))
   436  			for i, v := range cmp {
   437  				cmpHist[i] = v
   438  			}
   439  			compareHist(t, tt.exp, cmpHist)
   440  		})
   441  	}
   442  }
   443  
   444  func compareHist(t *testing.T, exp, cmp sql.Histogram) {
   445  	if len(exp) != len(cmp) {
   446  		t.Errorf("histograms not same length: %d != %d\n%s\n%s\n", len(exp), len(cmp), exp.DebugString(), cmp.DebugString())
   447  	}
   448  	for i, b := range exp {
   449  		require.Equalf(t, b.UpperBound(), cmp[i].UpperBound(), "bound not equal: %v != %v\n%s\n%s", b.UpperBound(), cmp[i].UpperBound(), exp.DebugString(), cmp.DebugString())
   450  		if b.RowCount() != cmp[i].RowCount() {
   451  			t.Errorf("histograms row count not equal: %d != %d\n%s\n%s", b.RowCount(), cmp[i].RowCount(), exp.DebugString(), cmp.DebugString())
   452  		}
   453  		if b.DistinctCount() != cmp[i].DistinctCount() {
   454  			t.Errorf("histograms distinct not equal: %d != %d\n%s\n%s", b.DistinctCount(), cmp[i].DistinctCount(), exp.DebugString(), cmp.DebugString())
   455  		}
   456  		if b.NullCount() != cmp[i].NullCount() {
   457  			t.Errorf("histograms null not equal: %d != %d\n%s\n%s", b.NullCount(), cmp[i].NullCount(), exp.DebugString(), cmp.DebugString())
   458  		}
   459  	}
   460  }