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 }