github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/logical_props_builder_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 memo
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/sql/opt"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/opt/props"
    19  )
    20  
    21  func TestJoinCardinality(t *testing.T) {
    22  	c := func(min, max uint32) props.Cardinality {
    23  		return props.Cardinality{Min: min, Max: max}
    24  	}
    25  
    26  	type testCase struct {
    27  		left     props.Cardinality
    28  		right    props.Cardinality
    29  		expected props.Cardinality
    30  	}
    31  
    32  	testCaseGroups := []struct {
    33  		joinType  opt.Operator
    34  		filter    string // "true", "false", or "other"
    35  		testCases []testCase
    36  	}{
    37  		{ // Inner join, true filter.
    38  			joinType: opt.InnerJoinOp,
    39  			filter:   "true",
    40  			testCases: []testCase{
    41  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
    42  				{left: c(5, 10), right: c(0, 10), expected: c(0, 100)},
    43  				{left: c(0, 10), right: c(5, 10), expected: c(0, 100)},
    44  				{left: c(5, 10), right: c(5, 10), expected: c(25, 100)},
    45  			},
    46  		},
    47  
    48  		{ // Inner join, false filter.
    49  			joinType: opt.InnerJoinOp,
    50  			filter:   "false",
    51  			testCases: []testCase{
    52  				{left: c(0, 10), right: c(0, 10), expected: c(0, 0)},
    53  				{left: c(5, 10), right: c(0, 10), expected: c(0, 0)},
    54  				{left: c(0, 10), right: c(5, 10), expected: c(0, 0)},
    55  				{left: c(5, 10), right: c(5, 10), expected: c(0, 0)},
    56  			},
    57  		},
    58  
    59  		{ // Inner join, other filter.
    60  			joinType: opt.InnerJoinOp,
    61  			filter:   "other",
    62  			testCases: []testCase{
    63  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
    64  				{left: c(5, 10), right: c(0, 10), expected: c(0, 100)},
    65  				{left: c(0, 10), right: c(5, 10), expected: c(0, 100)},
    66  				{left: c(5, 10), right: c(5, 10), expected: c(0, 100)},
    67  			},
    68  		},
    69  
    70  		{ // Left join, true filter.
    71  			joinType: opt.LeftJoinOp,
    72  			filter:   "true",
    73  			testCases: []testCase{
    74  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
    75  				{left: c(5, 10), right: c(0, 10), expected: c(5, 100)},
    76  				{left: c(0, 10), right: c(5, 10), expected: c(0, 100)},
    77  				{left: c(5, 10), right: c(5, 10), expected: c(25, 100)},
    78  			},
    79  		},
    80  
    81  		{ // Left join, false filter.
    82  			joinType: opt.LeftJoinOp,
    83  			filter:   "false",
    84  			testCases: []testCase{
    85  				{left: c(0, 10), right: c(0, 10), expected: c(0, 10)},
    86  				{left: c(5, 10), right: c(0, 10), expected: c(5, 10)},
    87  				{left: c(0, 10), right: c(5, 10), expected: c(0, 10)},
    88  				{left: c(5, 10), right: c(5, 10), expected: c(5, 10)},
    89  			},
    90  		},
    91  
    92  		{ // Left join, other filter.
    93  			joinType: opt.LeftJoinOp,
    94  			filter:   "other",
    95  			testCases: []testCase{
    96  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
    97  				{left: c(5, 10), right: c(0, 10), expected: c(5, 100)},
    98  				{left: c(0, 10), right: c(5, 10), expected: c(0, 100)},
    99  				{left: c(5, 10), right: c(5, 10), expected: c(5, 100)},
   100  			},
   101  		},
   102  
   103  		{ // Right join, true filter.
   104  			joinType: opt.RightJoinOp,
   105  			filter:   "true",
   106  			testCases: []testCase{
   107  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
   108  				{left: c(5, 10), right: c(0, 10), expected: c(0, 100)},
   109  				{left: c(0, 10), right: c(5, 10), expected: c(5, 100)},
   110  				{left: c(5, 10), right: c(5, 10), expected: c(25, 100)},
   111  			},
   112  		},
   113  
   114  		{ // Right join, false filter.
   115  			joinType: opt.RightJoinOp,
   116  			filter:   "false",
   117  			testCases: []testCase{
   118  				{left: c(0, 10), right: c(0, 10), expected: c(0, 10)},
   119  				{left: c(5, 10), right: c(0, 10), expected: c(0, 10)},
   120  				{left: c(0, 10), right: c(5, 10), expected: c(5, 10)},
   121  				{left: c(5, 10), right: c(5, 10), expected: c(5, 10)},
   122  			},
   123  		},
   124  
   125  		{ // Right join, other filter.
   126  			joinType: opt.RightJoinOp,
   127  			filter:   "other",
   128  			testCases: []testCase{
   129  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
   130  				{left: c(5, 10), right: c(0, 10), expected: c(0, 100)},
   131  				{left: c(0, 10), right: c(5, 10), expected: c(5, 100)},
   132  				{left: c(5, 10), right: c(5, 10), expected: c(5, 100)},
   133  			},
   134  		},
   135  
   136  		{ // Full join, true filter.
   137  			joinType: opt.FullJoinOp,
   138  			filter:   "true",
   139  			testCases: []testCase{
   140  				{left: c(0, 1), right: c(0, 1), expected: c(0, 2)},
   141  				{left: c(1, 1), right: c(1, 1), expected: c(1, 2)},
   142  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
   143  				{left: c(5, 10), right: c(0, 10), expected: c(5, 100)},
   144  				{left: c(0, 10), right: c(5, 10), expected: c(5, 100)},
   145  				{left: c(5, 10), right: c(5, 10), expected: c(25, 100)},
   146  				{left: c(7, 10), right: c(8, 10), expected: c(56, 100)},
   147  				{left: c(8, 10), right: c(7, 10), expected: c(56, 100)},
   148  			},
   149  		},
   150  
   151  		{ // Full join, false filter.
   152  			joinType: opt.FullJoinOp,
   153  			filter:   "false",
   154  			testCases: []testCase{
   155  				{left: c(0, 1), right: c(0, 1), expected: c(0, 2)},
   156  				{left: c(1, 1), right: c(1, 1), expected: c(2, 2)},
   157  				{left: c(2, 5), right: c(3, 8), expected: c(5, 13)},
   158  				{left: c(0, 10), right: c(0, 10), expected: c(0, 20)},
   159  				{left: c(5, 10), right: c(0, 10), expected: c(5, 20)},
   160  				{left: c(0, 10), right: c(5, 10), expected: c(5, 20)},
   161  				{left: c(5, 10), right: c(5, 10), expected: c(10, 20)},
   162  				{left: c(7, 10), right: c(8, 10), expected: c(15, 20)},
   163  				{left: c(8, 10), right: c(7, 10), expected: c(15, 20)},
   164  			},
   165  		},
   166  
   167  		{ // Full join, other filter.
   168  			joinType: opt.FullJoinOp,
   169  			filter:   "other",
   170  			testCases: []testCase{
   171  				{left: c(0, 1), right: c(0, 1), expected: c(0, 2)},
   172  				{left: c(1, 1), right: c(1, 1), expected: c(1, 2)},
   173  				{left: c(2, 5), right: c(3, 8), expected: c(3, 40)},
   174  				{left: c(0, 10), right: c(0, 10), expected: c(0, 100)},
   175  				{left: c(5, 10), right: c(0, 10), expected: c(5, 100)},
   176  				{left: c(0, 10), right: c(5, 10), expected: c(5, 100)},
   177  				{left: c(5, 10), right: c(5, 10), expected: c(5, 100)},
   178  				{left: c(7, 10), right: c(8, 10), expected: c(8, 100)},
   179  				{left: c(8, 10), right: c(7, 10), expected: c(8, 100)},
   180  			},
   181  		},
   182  	}
   183  
   184  	for _, group := range testCaseGroups {
   185  		t.Run(fmt.Sprintf("%s/%s", group.joinType, group.filter), func(t *testing.T) {
   186  			for i, tc := range group.testCases {
   187  				t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   188  					h := &joinPropsHelper{}
   189  					h.joinType = group.joinType
   190  					h.leftProps = &props.Relational{Cardinality: tc.left}
   191  					h.rightProps = &props.Relational{Cardinality: tc.right}
   192  					h.filterIsTrue = (group.filter == "true")
   193  					h.filterIsFalse = (group.filter == "false")
   194  
   195  					res := h.cardinality()
   196  					if res != tc.expected {
   197  						t.Errorf(
   198  							"left=%s right=%s: expected %s, got %s\n", tc.left, tc.right, tc.expected, res,
   199  						)
   200  					}
   201  				})
   202  			}
   203  		})
   204  	}
   205  }