github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/statistics_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 "testing" 15 16 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt" 18 "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" 19 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt/testutils/testcat" 21 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 22 ) 23 24 // Most of the functionality in statistics.go is tested by the data-driven 25 // testing in logical_props_factory_test.go. This file contains tests for 26 // functions in statistics.go that cannot be tested using the data-driven 27 // testing framework. 28 29 // Test getting statistics from constraints that cannot yet be inferred 30 // by the optimizer. 31 func TestGetStatsFromConstraint(t *testing.T) { 32 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 33 evalCtx.SessionData.OptimizerUseMultiColStats = true 34 35 catalog := testcat.New() 36 if _, err := catalog.ExecuteDDL( 37 "CREATE TABLE sel (a INT, b INT, c INT, d STRING, e STRING)", 38 ); err != nil { 39 t.Fatal(err) 40 } 41 42 if _, err := catalog.ExecuteDDL( 43 `ALTER TABLE sel INJECT STATISTICS '[ 44 { 45 "columns": ["a"], 46 "created_at": "2018-01-01 1:00:00.00000+00:00", 47 "row_count": 10000000000, 48 "distinct_count": 500 49 }, 50 { 51 "columns": ["b"], 52 "created_at": "2018-01-01 1:30:00.00000+00:00", 53 "row_count": 10000000000, 54 "distinct_count": 500 55 }, 56 { 57 "columns": ["c"], 58 "created_at": "2018-01-01 1:30:00.00000+00:00", 59 "row_count": 10000000000, 60 "distinct_count": 500 61 }, 62 { 63 "columns": ["a","b","c"], 64 "created_at": "2018-01-01 1:30:00.00000+00:00", 65 "row_count": 10000000000, 66 "distinct_count": 9900 67 }, 68 { 69 "columns": ["d"], 70 "created_at": "2018-01-01 1:30:00.00000+00:00", 71 "row_count": 10000000000, 72 "distinct_count": 10 73 }, 74 { 75 "columns": ["e"], 76 "created_at": "2018-01-01 1:30:00.00000+00:00", 77 "row_count": 10000000000, 78 "distinct_count": 10 79 }, 80 { 81 "columns": ["d","e"], 82 "created_at": "2018-01-01 1:30:00.00000+00:00", 83 "row_count": 10000000000, 84 "distinct_count": 100 85 } 86 ]'`); err != nil { 87 t.Fatal(err) 88 } 89 90 var mem Memo 91 mem.Init(&evalCtx) 92 tn := tree.NewUnqualifiedTableName("sel") 93 tab := catalog.Table(tn) 94 tabID := mem.Metadata().AddTable(tab, tn) 95 96 // Test that applyConstraintSet correctly updates the statistics from 97 // constraint set cs. 98 statsFunc := func(cs *constraint.Set, expectedStats string) { 99 t.Helper() 100 101 var cols opt.ColSet 102 for i := 0; i < tab.ColumnCount(); i++ { 103 cols.Add(tabID.ColumnID(i)) 104 } 105 106 sb := &statisticsBuilder{} 107 sb.init(&evalCtx, mem.Metadata()) 108 109 // Make the scan. 110 scan := mem.MemoizeScan(&ScanPrivate{Table: tabID, Cols: cols}) 111 112 // Make the select. 113 sel := mem.MemoizeSelect(scan, TrueFilter) 114 115 relProps := &props.Relational{Cardinality: props.AnyCardinality} 116 relProps.NotNullCols = cs.ExtractNotNullCols(&evalCtx) 117 s := &relProps.Stats 118 s.Init(relProps) 119 120 // Calculate distinct counts. 121 sb.applyConstraintSet(cs, true /* tight */, sel, relProps) 122 123 // Calculate row count and selectivity. 124 s.RowCount = scan.Relational().Stats.RowCount 125 s.ApplySelectivity(sb.selectivityFromMultiColDistinctCounts(cols, sel, s)) 126 127 // Update null counts. 128 sb.updateNullCountsFromProps(sel, relProps) 129 130 // Check if the statistics match the expected value. 131 testStats(t, s, expectedStats) 132 } 133 134 c1 := constraint.ParseConstraint(&evalCtx, "/1: [/2 - /5] [/8 - /10]") 135 c2 := constraint.ParseConstraint(&evalCtx, "/2: [/3 - ]") 136 c3 := constraint.ParseConstraint(&evalCtx, "/3: [/6 - /6]") 137 c12 := constraint.ParseConstraint(&evalCtx, "/1/2/3: [/1/2 - /1/3] [/1/4 - /1]") 138 c123 := constraint.ParseConstraint(&evalCtx, "/1/2/3: [/1/2/3 - /1/2/3] [/1/2/5 - /1/2/8]") 139 c123n := constraint.ParseConstraint(&evalCtx, "/1/2/3: [/1/2/NULL - /1/2/3] [/1/2/5 - /1/2/8]") 140 c32 := constraint.ParseConstraint(&evalCtx, "/3/-2: [/5/3 - /5/2]") 141 c321 := constraint.ParseConstraint(&evalCtx, "/-3/2/1: [/5/3/1 - /5/3/4] [/3/5/1 - /3/5/4]") 142 c312 := constraint.ParseConstraint(&evalCtx, "/3/1/-2: [/5/3/8 - /5/3/6] [/9/5/4 - /9/5/1]") 143 c312n := constraint.ParseConstraint(&evalCtx, "/3/1/-2: [/5/3/8 - /5/3/6] [/9/5/4 - /9/5/NULL]") 144 145 // /4/5: [/'apple'/'cherry' - /'apple'/'mango'] 146 appleCherry := constraint.MakeCompositeKey(tree.NewDString("apple"), tree.NewDString("cherry")) 147 appleMango := constraint.MakeCompositeKey(tree.NewDString("apple"), tree.NewDString("mango")) 148 var sp45 constraint.Span 149 sp45.Init(appleCherry, constraint.IncludeBoundary, appleMango, constraint.IncludeBoundary) 150 151 var columns45 constraint.Columns 152 columns45.Init([]opt.OrderingColumn{4, 5}) 153 keyCtx45 := constraint.MakeKeyContext(&columns45, &evalCtx) 154 155 cs1 := constraint.SingleConstraint(&c1) 156 statsFunc( 157 cs1, 158 "[rows=140000000, distinct(1)=7, null(1)=0]", 159 ) 160 161 cs2 := constraint.SingleConstraint(&c2) 162 statsFunc( 163 cs2, 164 "[rows=3.33333333e+09, distinct(2)=166.666667, null(2)=0]", 165 ) 166 167 cs3 := constraint.SingleConstraint(&c3) 168 statsFunc( 169 cs3, 170 "[rows=20000000, distinct(3)=1, null(3)=0]", 171 ) 172 173 cs12 := constraint.SingleConstraint(&c12) 174 statsFunc( 175 cs12, 176 "[rows=20000000, distinct(1)=1, null(1)=0]", 177 ) 178 179 cs123 := constraint.SingleConstraint(&c123) 180 statsFunc( 181 cs123, 182 "[rows=36040, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=5, null(3)=0, distinct(1,2)=1, null(1,2)=0, distinct(1-3)=5, null(1-3)=0]", 183 ) 184 185 cs123n := constraint.SingleConstraint(&c123n) 186 statsFunc( 187 cs123n, 188 "[rows=40000, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(1,2)=1, null(1,2)=0]", 189 ) 190 191 cs32 := constraint.SingleConstraint(&c32) 192 statsFunc( 193 cs32, 194 "[rows=80000, distinct(2)=2, null(2)=0, distinct(3)=1, null(3)=0, distinct(2,3)=2, null(2,3)=0]", 195 ) 196 197 cs321 := constraint.SingleConstraint(&c321) 198 statsFunc( 199 cs321, 200 "[rows=160000, distinct(2)=2, null(2)=0, distinct(3)=2, null(3)=0, distinct(2,3)=4, null(2,3)=0]", 201 ) 202 203 cs312 := constraint.SingleConstraint(&c312) 204 statsFunc( 205 cs312, 206 "[rows=24490654.6, distinct(1)=2, null(1)=0, distinct(2)=7, null(2)=0, distinct(3)=2, null(3)=0, distinct(1-3)=26.9394737, null(1-3)=0]", 207 ) 208 209 cs312n := constraint.SingleConstraint(&c312n) 210 statsFunc( 211 cs312n, 212 "[rows=160000, distinct(1)=2, null(1)=0, distinct(3)=2, null(3)=0, distinct(1,3)=4, null(1,3)=0]", 213 ) 214 215 cs := cs3.Intersect(&evalCtx, cs123) 216 statsFunc( 217 cs, 218 "[rows=909098.909, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=1, null(3)=0, distinct(1-3)=1, null(1-3)=0]", 219 ) 220 221 cs = cs32.Intersect(&evalCtx, cs123) 222 statsFunc( 223 cs, 224 "[rows=909098.909, distinct(1)=1, null(1)=0, distinct(2)=1, null(2)=0, distinct(3)=1, null(3)=0, distinct(1-3)=1, null(1-3)=0]", 225 ) 226 227 cs45 := constraint.SingleSpanConstraint(&keyCtx45, &sp45) 228 statsFunc( 229 cs45, 230 "[rows=1e+09, distinct(4)=1, null(4)=0]", 231 ) 232 } 233 234 func testStats(t *testing.T, s *props.Statistics, expectedStats string) { 235 t.Helper() 236 237 actual := s.String() 238 if actual != expectedStats { 239 t.Fatalf("\nexpected: %s\nactual : %s", expectedStats, actual) 240 } 241 }