github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/props/histogram_test.go (about) 1 // Copyright 2019 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 12 13 import ( 14 "math" 15 "reflect" 16 "testing" 17 "time" 18 19 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt" 21 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/sql/types" 25 ) 26 27 func TestCanFilter(t *testing.T) { 28 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 29 30 // The histogram column ID is 1 for all test cases. CanFilter should only 31 // return true for constraints in which column ID 1 is part of the exact 32 // prefix or the first column after. 33 testData := []struct { 34 constraint string 35 canFilter bool 36 colIdx int 37 }{ 38 { 39 constraint: "/1: [/0 - /0]", 40 canFilter: true, 41 colIdx: 0, 42 }, 43 { 44 constraint: "/2: [/0 - /0]", 45 canFilter: false, 46 }, 47 { 48 constraint: "/1: [/0 - /0] [/2 - /2]", 49 canFilter: true, 50 colIdx: 0, 51 }, 52 { 53 constraint: "/1: [/3 - /20] [/22 - ]", 54 canFilter: true, 55 colIdx: 0, 56 }, 57 { 58 constraint: "/1/2: [/0/3 - /0/3] [/2/3 - /2/3]", 59 canFilter: true, 60 colIdx: 0, 61 }, 62 { 63 constraint: "/2/-1: [/0/3 - /0/3] [/2/3 - /2/3]", 64 canFilter: false, 65 }, 66 { 67 constraint: "/2/1: [/0/3 - /0/3] [/0/5 - /0/5]", 68 canFilter: true, 69 colIdx: 1, 70 }, 71 { 72 constraint: "/2/-1: [/0/5 - /0/3] [/0/1 - /0/1]", 73 canFilter: true, 74 colIdx: 1, 75 }, 76 { 77 constraint: "/2/3/1: [/0/3/NULL - /0/3/100] [/0/5/NULL - /0/5/100]", 78 canFilter: false, 79 }, 80 { 81 constraint: "/2/-3/1: [/0/5/NULL - /0/5/100] [/0/3/NULL - /0/3/100]", 82 canFilter: false, 83 }, 84 { 85 constraint: "/2/1/3: [/0/3/NULL - /0/3/100] [/0/3/200 - /0/3/300]", 86 canFilter: true, 87 colIdx: 1, 88 }, 89 } 90 91 h := Histogram{} 92 h.Init(&evalCtx, opt.ColumnID(1), []cat.HistogramBucket{}) 93 for _, tc := range testData { 94 c := constraint.ParseConstraint(&evalCtx, tc.constraint) 95 colIdx, _, ok := h.CanFilter(&c) 96 if ok != tc.canFilter { 97 t.Fatalf( 98 "for constraint %s, expected canFilter=%v but found %v", tc.constraint, tc.canFilter, ok, 99 ) 100 } 101 if ok && colIdx != tc.colIdx { 102 t.Fatalf( 103 "for constraint %s, expected colIdx=%d but found %d", tc.constraint, tc.colIdx, colIdx, 104 ) 105 } 106 } 107 } 108 109 func TestHistogram(t *testing.T) { 110 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 111 112 // 0 1 3 3 4 5 0 0 40 35 113 // <--- 1 --- 10 --- 25 --- 30 ---- 42 114 histData := []cat.HistogramBucket{ 115 {NumRange: 0, DistinctRange: 0, NumEq: 1, UpperBound: tree.NewDInt(1)}, 116 {NumRange: 3, DistinctRange: 2, NumEq: 3, UpperBound: tree.NewDInt(10)}, 117 {NumRange: 4, DistinctRange: 2, NumEq: 5, UpperBound: tree.NewDInt(25)}, 118 {NumRange: 0, DistinctRange: 0, NumEq: 0, UpperBound: tree.NewDInt(30)}, 119 {NumRange: 40, DistinctRange: 7, NumEq: 35, UpperBound: tree.NewDInt(42)}, 120 } 121 h := &Histogram{} 122 h.Init(&evalCtx, opt.ColumnID(1), histData) 123 count, expected := h.ValuesCount(), float64(91) 124 if count != expected { 125 t.Fatalf("expected %f but found %f", expected, count) 126 } 127 maxDistinct, expected := h.maxDistinctValuesCount(), float64(22) 128 if maxDistinct != expected { 129 t.Fatalf("expected %f but found %f", expected, maxDistinct) 130 } 131 distinct, expected := h.DistinctValuesCount(), float64(15) 132 if distinct != expected { 133 t.Fatalf("expected %f but found %f", expected, distinct) 134 } 135 136 testData := []struct { 137 constraint string 138 buckets []cat.HistogramBucket 139 count float64 140 maxDistinct float64 141 distinct float64 142 }{ 143 { 144 constraint: "/1: [/0 - /0]", 145 buckets: []cat.HistogramBucket{}, 146 count: 0, 147 maxDistinct: 0, 148 distinct: 0, 149 }, 150 { 151 constraint: "/1: [/50 - /100]", 152 buckets: []cat.HistogramBucket{}, 153 count: 0, 154 maxDistinct: 0, 155 distinct: 0, 156 }, 157 { 158 constraint: "/1: [ - /1] [/11 - /24] [/30 - /45]", 159 // 0 1 0 0 3.7143 0.28571 0 0 40 35 160 // <--- 1 --- 10 --------- 24 ----- 30 ---- 42 161 buckets: []cat.HistogramBucket{ 162 {NumRange: 0, NumEq: 1, DistinctRange: 0, UpperBound: tree.NewDInt(1)}, 163 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(10)}, 164 {NumRange: 3.71, NumEq: 0.29, DistinctRange: 1.86, UpperBound: tree.NewDInt(24)}, 165 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 166 {NumRange: 40, NumEq: 35, DistinctRange: 7, UpperBound: tree.NewDInt(42)}, 167 }, 168 count: 80, 169 maxDistinct: 17, 170 distinct: 11.14, 171 }, 172 { 173 constraint: "/1: [/5 - /10] [/15 - /32] [/34 - /36] [/38 - ]", 174 // 0 0 1.875 3 0 0 2.8571 5 0 0 3.6364 3.6364 0 0 7.2727 3.6364 0 0 14.545 35 175 // <--- 4 ------- 10 --- 14 -------- 25 --- 30 --------- 32 ---- 33 --------- 36 ---- 37 -------- 42 176 buckets: []cat.HistogramBucket{ 177 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(4)}, 178 {NumRange: 1.88, NumEq: 3, DistinctRange: 1.25, UpperBound: tree.NewDInt(10)}, 179 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(14)}, 180 {NumRange: 2.86, NumEq: 5, DistinctRange: 1.43, UpperBound: tree.NewDInt(25)}, 181 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 182 {NumRange: 3.64, NumEq: 3.64, DistinctRange: 0.64, UpperBound: tree.NewDInt(32)}, 183 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(33)}, 184 {NumRange: 7.27, NumEq: 3.64, DistinctRange: 1.27, UpperBound: tree.NewDInt(36)}, 185 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(37)}, 186 {NumRange: 14.55, NumEq: 35, DistinctRange: 2.55, UpperBound: tree.NewDInt(42)}, 187 }, 188 count: 80.46, 189 maxDistinct: 16.73, 190 distinct: 12.13, 191 }, 192 { 193 constraint: "/1: [ - /41]", 194 // 0 1 3 3 4 5 0 0 36.364 3.6364 195 // <--- 1 --- 10 --- 25 --- 30 --------- 41 - 196 buckets: []cat.HistogramBucket{ 197 {NumRange: 0, NumEq: 1, DistinctRange: 0, UpperBound: tree.NewDInt(1)}, 198 {NumRange: 3, NumEq: 3, DistinctRange: 2, UpperBound: tree.NewDInt(10)}, 199 {NumRange: 4, NumEq: 5, DistinctRange: 2, UpperBound: tree.NewDInt(25)}, 200 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 201 {NumRange: 36.36, NumEq: 3.64, DistinctRange: 6.36, UpperBound: tree.NewDInt(41)}, 202 }, 203 count: 56, 204 maxDistinct: 21, 205 distinct: 14.36, 206 }, 207 { 208 constraint: "/1: [/1 - ]", 209 // 0 1 3 3 4 5 0 0 40 35 210 // <--- 1 --- 10 --- 25 --- 30 ---- 42 211 buckets: []cat.HistogramBucket{ 212 {NumRange: 0, NumEq: 1, DistinctRange: 0, UpperBound: tree.NewDInt(1)}, 213 {NumRange: 3, NumEq: 3, DistinctRange: 2, UpperBound: tree.NewDInt(10)}, 214 {NumRange: 4, NumEq: 5, DistinctRange: 2, UpperBound: tree.NewDInt(25)}, 215 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 216 {NumRange: 40, NumEq: 35, DistinctRange: 7, UpperBound: tree.NewDInt(42)}, 217 }, 218 count: 91, 219 maxDistinct: 22, 220 distinct: 15, 221 }, 222 { 223 constraint: "/1: [/40 - /40]", 224 // 0 5.7143 225 // <---- 40 - 226 buckets: []cat.HistogramBucket{ 227 {NumRange: 0, NumEq: 5.71, DistinctRange: 0, UpperBound: tree.NewDInt(40)}, 228 }, 229 count: 5.71, 230 maxDistinct: 1, 231 distinct: 1, 232 }, 233 { 234 constraint: "/1: [/0 - /100]", 235 // 0 1 3 3 4 5 0 0 40 35 236 // <--- 1 --- 10 --- 25 --- 30 ---- 42 237 buckets: []cat.HistogramBucket{ 238 {NumRange: 0, DistinctRange: 0, NumEq: 1, UpperBound: tree.NewDInt(1)}, 239 {NumRange: 3, DistinctRange: 2, NumEq: 3, UpperBound: tree.NewDInt(10)}, 240 {NumRange: 4, DistinctRange: 2, NumEq: 5, UpperBound: tree.NewDInt(25)}, 241 {NumRange: 0, DistinctRange: 0, NumEq: 0, UpperBound: tree.NewDInt(30)}, 242 {NumRange: 40, DistinctRange: 7, NumEq: 35, UpperBound: tree.NewDInt(42)}, 243 }, 244 count: 91, 245 maxDistinct: 22, 246 distinct: 15, 247 }, 248 249 // Tests with multiple columns. 250 { 251 constraint: "/1/2: [ - /1/3] [/11 - /24/3] [/30 - /45/3]", 252 // 0 1 0 0 3.7143 0.28571 0 0 40 35 253 // <--- 1 --- 10 --------- 24 ----- 30 ---- 42 254 buckets: []cat.HistogramBucket{ 255 {NumRange: 0, NumEq: 1, DistinctRange: 0, UpperBound: tree.NewDInt(1)}, 256 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(10)}, 257 {NumRange: 3.71, NumEq: 0.29, DistinctRange: 1.86, UpperBound: tree.NewDInt(24)}, 258 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 259 {NumRange: 40, NumEq: 35, DistinctRange: 7, UpperBound: tree.NewDInt(42)}, 260 }, 261 count: 80, 262 maxDistinct: 17, 263 distinct: 11.14, 264 }, 265 { 266 constraint: "/2/1: [/3 - /3/1] [/3/11 - /3/24] [/3/30 - /3/45]", 267 // 0 1 0 0 3.7143 0.28571 0 0 40 35 268 // <--- 1 --- 10 --------- 24 ----- 30 ---- 42 269 buckets: []cat.HistogramBucket{ 270 {NumRange: 0, NumEq: 1, DistinctRange: 0, UpperBound: tree.NewDInt(1)}, 271 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(10)}, 272 {NumRange: 3.71, NumEq: 0.29, DistinctRange: 1.86, UpperBound: tree.NewDInt(24)}, 273 {NumRange: 0, NumEq: 0, DistinctRange: 0, UpperBound: tree.NewDInt(30)}, 274 {NumRange: 40, NumEq: 35, DistinctRange: 7, UpperBound: tree.NewDInt(42)}, 275 }, 276 count: 80, 277 maxDistinct: 17, 278 distinct: 11.14, 279 }, 280 { 281 constraint: "/2/1/3: [/1/40/2 - /1/40/3]", 282 // 0 5.7143 283 // <---- 40 - 284 buckets: []cat.HistogramBucket{ 285 {NumRange: 0, NumEq: 5.71, DistinctRange: 0, UpperBound: tree.NewDInt(40)}, 286 }, 287 count: 5.71, 288 maxDistinct: 1, 289 distinct: 1, 290 }, 291 { 292 constraint: "/2/1/3: [/1/40/2 - /1/40/2] [/1/40/4 - /1/40/4] [/1/40/6 - /1/40/6]", 293 // 0 5.7143 294 // <---- 40 - 295 buckets: []cat.HistogramBucket{ 296 {NumRange: 0, NumEq: 5.71, DistinctRange: 0, UpperBound: tree.NewDInt(40)}, 297 }, 298 count: 5.71, 299 maxDistinct: 1, 300 distinct: 1, 301 }, 302 } 303 304 for i := range testData { 305 c := constraint.ParseConstraint(&evalCtx, testData[i].constraint) 306 ascAndDesc := []constraint.Constraint{c, makeDescConstraint(&c)} 307 308 // Make sure all test cases work with both ascending and descending columns. 309 for _, c := range ascAndDesc { 310 if _, _, ok := h.CanFilter(&c); !ok { 311 t.Fatalf("constraint %s cannot filter histogram %v", c.String(), *h) 312 } 313 filtered := h.Filter(&c) 314 count := roundVal(filtered.ValuesCount()) 315 if testData[i].count != count { 316 t.Fatalf("expected %f but found %f", testData[i].count, count) 317 } 318 maxDistinct := roundVal(filtered.maxDistinctValuesCount()) 319 if testData[i].maxDistinct != maxDistinct { 320 t.Fatalf("expected %f but found %f", testData[i].maxDistinct, maxDistinct) 321 } 322 distinct := roundVal(filtered.DistinctValuesCount()) 323 if testData[i].distinct != distinct { 324 t.Fatalf("expected %f but found %f", testData[i].distinct, distinct) 325 } 326 roundHistogram(filtered) 327 if !reflect.DeepEqual(testData[i].buckets, filtered.buckets) { 328 t.Fatalf("expected %v but found %v", testData[i].buckets, filtered.buckets) 329 } 330 } 331 } 332 } 333 334 func TestFilterBucket(t *testing.T) { 335 evalCtx := tree.MakeTestingEvalContext(cluster.MakeTestingClusterSettings()) 336 keyCtx := constraint.KeyContext{EvalCtx: &evalCtx} 337 col := opt.ColumnID(1) 338 339 type testCase struct { 340 span string 341 expected *cat.HistogramBucket 342 isError bool 343 } 344 345 runTestCase := func( 346 h *Histogram, span *constraint.Span, desc bool, 347 ) (actual *cat.HistogramBucket, err error) { 348 defer func() { 349 // Any errors will be propagated as panics. 350 if r := recover(); r != nil { 351 if e, ok := r.(error); ok { 352 err = e 353 return 354 } 355 panic(r) 356 } 357 }() 358 359 keyCtx.Columns.InitSingle(opt.MakeOrderingColumn(col, desc)) 360 var iter histogramIter 361 iter.init(h, desc) 362 363 // All test cases have two buckets. The first bucket is empty and used to 364 // mark the lower bound of the second bucket. Set the iterator to point to 365 // the second bucket. 366 iter.setIdx(1) 367 b := getFilteredBucket(&iter, &keyCtx, span, 0 /* colIdx */) 368 roundBucket(b) 369 return b, nil 370 } 371 372 runTest := func(h *Histogram, testData []testCase, typ types.Family) { 373 for _, testCase := range testData { 374 span := constraint.ParseSpan(&evalCtx, testCase.span, typ) 375 ascAndDesc := []constraint.Span{span, makeDescSpan(&span)} 376 377 // Make sure all test cases work with both ascending and descending columns. 378 for i, span := range ascAndDesc { 379 actual, err := runTestCase(h, &span, i == 1 /* desc */) 380 if err != nil && !testCase.isError { 381 t.Fatalf("for span %s got error %v", testCase.span, err) 382 } else if err == nil { 383 if testCase.isError { 384 t.Fatalf("for span %s expected an error", testCase.span) 385 } 386 if !reflect.DeepEqual(testCase.expected, actual) { 387 t.Fatalf("for span %s exected %v but found %v", testCase.span, testCase.expected, actual) 388 } 389 } 390 } 391 } 392 } 393 394 // Each of the tests below have a histogram with two buckets. The first 395 // bucket is empty and simply used to mark the lower bound of the second 396 // bucket. 397 // 398 // getPrevUpperBound is used to find the upper bound of the first bucket so 399 // that the lower bound of the second bucket will equal upperBound.Next(). 400 getPrevUpperBound := func(lowerBound tree.Datum) tree.Datum { 401 res, ok := lowerBound.Prev(&evalCtx) 402 if !ok { 403 res = lowerBound 404 } 405 return res 406 } 407 408 t.Run("int", func(t *testing.T) { 409 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 410 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(tree.NewDInt(0))}, 411 {NumEq: 5, NumRange: 10, DistinctRange: 10, UpperBound: tree.NewDInt(10)}, 412 }} 413 testData := []testCase{ 414 { 415 span: "[/0 - /0]", 416 expected: &cat.HistogramBucket{NumEq: 1, NumRange: 0, DistinctRange: 0, UpperBound: tree.NewDInt(0)}, 417 }, 418 { 419 span: "[/0 - /5]", 420 expected: &cat.HistogramBucket{NumEq: 1, NumRange: 5, DistinctRange: 5, UpperBound: tree.NewDInt(5)}, 421 }, 422 { 423 span: "[/2 - /9]", 424 expected: &cat.HistogramBucket{NumEq: 1, NumRange: 7, DistinctRange: 7, UpperBound: tree.NewDInt(9)}, 425 }, 426 { 427 span: "[/2 - /10]", 428 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 8, DistinctRange: 8, UpperBound: tree.NewDInt(10)}, 429 }, 430 { 431 span: "[/10 - /10]", 432 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 0, DistinctRange: 0, UpperBound: tree.NewDInt(10)}, 433 }, 434 { 435 span: "[/20 - /30]", 436 isError: true, 437 }, 438 } 439 440 runTest(h, testData, types.IntFamily) 441 }) 442 443 t.Run("float", func(t *testing.T) { 444 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 445 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(tree.NewDFloat(0))}, 446 {NumEq: 5, NumRange: 10, DistinctRange: 10, UpperBound: tree.NewDFloat(10)}, 447 }} 448 testData := []testCase{ 449 { 450 span: "[/0 - /0]", 451 expected: &cat.HistogramBucket{NumEq: 1, NumRange: 0, DistinctRange: 0, UpperBound: tree.NewDFloat(0)}, 452 }, 453 { 454 span: "(/0 - /5]", 455 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 5, DistinctRange: 5, UpperBound: tree.NewDFloat(5)}, 456 }, 457 { 458 span: "[/2.5 - /9)", 459 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 6.5, DistinctRange: 6.5, UpperBound: tree.NewDFloat(9)}, 460 }, 461 { 462 span: "[/2 - /10]", 463 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 8, DistinctRange: 8, UpperBound: tree.NewDFloat(10)}, 464 }, 465 { 466 span: "[/10 - /10]", 467 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 0, DistinctRange: 0, UpperBound: tree.NewDFloat(10)}, 468 }, 469 { 470 span: "[/10 - /20]", 471 isError: true, 472 }, 473 } 474 475 runTest(h, testData, types.FloatFamily) 476 }) 477 478 t.Run("decimal", func(t *testing.T) { 479 upperBound, err := tree.ParseDDecimal("10") 480 if err != nil { 481 t.Fatal(err) 482 } 483 lowerBound, err := tree.ParseDDecimal("0") 484 if err != nil { 485 t.Fatal(err) 486 } 487 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 488 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(lowerBound)}, 489 {NumEq: 5, NumRange: 10, DistinctRange: 10, UpperBound: upperBound}, 490 }} 491 492 ub1, err := tree.ParseDDecimal("9") 493 if err != nil { 494 t.Fatal(err) 495 } 496 ub2, err := tree.ParseDDecimal("10.00") 497 if err != nil { 498 t.Fatal(err) 499 } 500 501 testData := []testCase{ 502 { 503 span: "[/2.50 - /9)", 504 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 6.5, DistinctRange: 6.5, UpperBound: ub1}, 505 }, 506 { 507 span: "[/2 - /10.00]", 508 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 8, DistinctRange: 8, UpperBound: ub2}, 509 }, 510 } 511 512 runTest(h, testData, types.DecimalFamily) 513 }) 514 515 t.Run("date", func(t *testing.T) { 516 upperBound, err := tree.ParseDDate(&evalCtx, "2019-08-01") 517 if err != nil { 518 t.Fatal(err) 519 } 520 lowerBound, err := tree.ParseDDate(&evalCtx, "2019-07-01") 521 if err != nil { 522 t.Fatal(err) 523 } 524 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 525 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(lowerBound)}, 526 {NumEq: 1, NumRange: 62, DistinctRange: 31, UpperBound: upperBound}, 527 }} 528 529 ub1, err := tree.ParseDDate(&evalCtx, "2019-07-02") 530 if err != nil { 531 t.Fatal(err) 532 } 533 534 testData := []testCase{ 535 { 536 span: "[/2019-07-01 - /2019-07-02]", 537 expected: &cat.HistogramBucket{NumEq: 2, NumRange: 2, DistinctRange: 1, UpperBound: ub1}, 538 }, 539 { 540 span: "[/2019-07-05 - /2019-08-01]", 541 expected: &cat.HistogramBucket{NumEq: 1, NumRange: 54, DistinctRange: 27, UpperBound: upperBound}, 542 }, 543 } 544 545 runTest(h, testData, types.DateFamily) 546 }) 547 548 t.Run("timestamp", func(t *testing.T) { 549 upperBound, err := tree.ParseDTimestamp(&evalCtx, "2019-08-01 12:00:00.000000", time.Microsecond) 550 if err != nil { 551 t.Fatal(err) 552 } 553 lowerBound, err := tree.ParseDTimestamp(&evalCtx, "2019-07-01 12:00:00.000000", time.Microsecond) 554 if err != nil { 555 t.Fatal(err) 556 } 557 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 558 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(lowerBound)}, 559 {NumEq: 1, NumRange: 62, DistinctRange: 31, UpperBound: upperBound}, 560 }} 561 562 ub1, err := tree.ParseDTimestamp(&evalCtx, "2019-07-02 00:00:00.000000", time.Microsecond) 563 if err != nil { 564 t.Fatal(err) 565 } 566 567 testData := []testCase{ 568 { 569 span: "[/2019-07-01 12:00:00.000000 - /2019-07-02 00:00:00.000000)", 570 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 1, DistinctRange: 0.5, UpperBound: ub1}, 571 }, 572 { 573 span: "[/2019-07-05 12:00:00.000000 - /2019-08-01 12:00:00.000000)", 574 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 54, DistinctRange: 27, UpperBound: upperBound}, 575 }, 576 } 577 578 runTest(h, testData, types.TimestampFamily) 579 }) 580 581 t.Run("string", func(t *testing.T) { 582 h := &Histogram{evalCtx: &evalCtx, col: col, buckets: []cat.HistogramBucket{ 583 {NumEq: 0, NumRange: 0, DistinctRange: 0, UpperBound: getPrevUpperBound(tree.NewDString("baq"))}, 584 {NumEq: 5, NumRange: 10, DistinctRange: 10, UpperBound: tree.NewDString("foo")}, 585 }} 586 testData := []testCase{ 587 { 588 span: "[/bar - /baz]", 589 expected: &cat.HistogramBucket{NumEq: 0, NumRange: 5, DistinctRange: 5, UpperBound: tree.NewDString("baz")}, 590 }, 591 { 592 span: "[/baz - /foo]", 593 expected: &cat.HistogramBucket{NumEq: 5, NumRange: 5, DistinctRange: 5, UpperBound: tree.NewDString("foo")}, 594 }, 595 } 596 597 runTest(h, testData, types.StringFamily) 598 }) 599 600 } 601 602 // makeDescSpan makes an equivalent version of s in which the start and end 603 // keys are swapped. 604 func makeDescSpan(s *constraint.Span) constraint.Span { 605 var desc constraint.Span 606 desc.Init(s.EndKey(), s.EndBoundary(), s.StartKey(), s.StartBoundary()) 607 return desc 608 } 609 610 // makeDescConstraint makes an equivalent version of c in which all columns 611 // are descending. 612 func makeDescConstraint(c *constraint.Constraint) constraint.Constraint { 613 var desc constraint.Constraint 614 615 // Negate all the columns. 616 cols := make([]opt.OrderingColumn, c.Columns.Count()) 617 for i := range cols { 618 cols[i] = -c.Columns.Get(i) 619 } 620 desc.Columns.Init(cols) 621 622 // Add all the spans in reverse order, with their start and end keys 623 // swapped. 624 desc.Spans.Alloc(c.Spans.Count()) 625 for i := c.Spans.Count() - 1; i >= 0; i-- { 626 s := makeDescSpan(c.Spans.Get(i)) 627 desc.Spans.Append(&s) 628 } 629 630 return desc 631 } 632 633 // Round all values to two decimal places. 634 func roundVal(val float64) float64 { 635 return math.Round(val*100.0) / 100.0 636 } 637 638 func roundBucket(b *cat.HistogramBucket) { 639 b.NumRange = roundVal(b.NumRange) 640 b.NumEq = roundVal(b.NumEq) 641 b.DistinctRange = roundVal(b.DistinctRange) 642 } 643 644 func roundHistogram(h *Histogram) { 645 for i := range h.buckets { 646 roundBucket(&h.buckets[i]) 647 } 648 }