github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/interner_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 "math" 15 "math/rand" 16 "reflect" 17 "testing" 18 "time" 19 "unsafe" 20 21 "github.com/cockroachdb/cockroach/pkg/sql/opt" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 25 "github.com/cockroachdb/cockroach/pkg/sql/types" 26 "github.com/cockroachdb/cockroach/pkg/util/timeofday" 27 "github.com/cockroachdb/cockroach/pkg/util/timeutil/pgdate" 28 "golang.org/x/tools/container/intsets" 29 ) 30 31 func TestInterner(t *testing.T) { 32 var in interner 33 34 json1, _ := tree.ParseDJSON(`{"a": 5, "b": [1, 2]}`) 35 json2, _ := tree.ParseDJSON(`{"a": 5, "b": [1, 2]}`) 36 json3, _ := tree.ParseDJSON(`[1, 2]`) 37 38 tupTyp1 := types.MakeLabeledTuple([]*types.T{types.Int, types.String}, []string{"a", "b"}) 39 tupTyp2 := types.MakeLabeledTuple([]*types.T{types.Int, types.String}, []string{"a", "b"}) 40 tupTyp3 := types.MakeTuple([]*types.T{types.Int, types.String}) 41 tupTyp4 := types.MakeTuple([]*types.T{types.Int, types.String, types.Bool}) 42 tupTyp5 := types.MakeLabeledTuple([]*types.T{types.Int, types.String}, []string{"c", "d"}) 43 tupTyp6 := types.MakeLabeledTuple([]*types.T{types.String, types.Int}, []string{"c", "d"}) 44 45 tup1 := tree.NewDTuple(tupTyp1, tree.NewDInt(100), tree.NewDString("foo")) 46 tup2 := tree.NewDTuple(tupTyp2, tree.NewDInt(100), tree.NewDString("foo")) 47 tup3 := tree.NewDTuple(tupTyp3, tree.NewDInt(100), tree.NewDString("foo")) 48 tup4 := tree.NewDTuple(tupTyp4, tree.NewDInt(100), tree.NewDString("foo"), tree.DBoolTrue) 49 tup5 := tree.NewDTuple(tupTyp5, tree.NewDInt(100), tree.NewDString("foo")) 50 tup6 := tree.NewDTuple(tupTyp5, tree.DNull, tree.DNull) 51 tup7 := tree.NewDTuple(tupTyp6, tree.DNull, tree.DNull) 52 53 arr1 := tree.NewDArray(tupTyp1) 54 arr1.Array = tree.Datums{tup1, tup2} 55 arr2 := tree.NewDArray(tupTyp2) 56 arr2.Array = tree.Datums{tup2, tup1} 57 arr3 := tree.NewDArray(tupTyp3) 58 arr3.Array = tree.Datums{tup2, tup3} 59 arr4 := tree.NewDArray(types.Int) 60 arr4.Array = tree.Datums{tree.DNull} 61 arr5 := tree.NewDArray(types.String) 62 arr5.Array = tree.Datums{tree.DNull} 63 arr6 := tree.NewDArray(types.Int) 64 arr6.Array = tree.Datums{} 65 arr7 := tree.NewDArray(types.String) 66 arr7.Array = tree.Datums{} 67 68 dec1, _ := tree.ParseDDecimal("1.0") 69 dec2, _ := tree.ParseDDecimal("1.0") 70 dec3, _ := tree.ParseDDecimal("1.00") 71 dec4, _ := tree.ParseDDecimal("1e0") 72 dec5, _ := tree.ParseDDecimal("1") 73 74 coll1, _ := tree.NewDCollatedString("foo", "sv_SE", &tree.CollationEnvironment{}) 75 coll2, _ := tree.NewDCollatedString("foo", "sv_SE", &tree.CollationEnvironment{}) 76 coll3, _ := tree.NewDCollatedString("foo", "en_US", &tree.CollationEnvironment{}) 77 coll4, _ := tree.NewDCollatedString("food", "en_US", &tree.CollationEnvironment{}) 78 79 tz1 := tree.MustMakeDTimestampTZ(time.Date(2018, 10, 6, 11, 49, 30, 123, time.UTC), 0) 80 tz2 := tree.MustMakeDTimestampTZ(time.Date(2018, 10, 6, 11, 49, 30, 123, time.UTC), 0) 81 tz3 := tree.MustMakeDTimestampTZ(time.Date(2018, 10, 6, 11, 49, 30, 124, time.UTC), 0) 82 tz4 := tree.MustMakeDTimestampTZ(time.Date(2018, 10, 6, 11, 49, 30, 124, time.FixedZone("PDT", -7)), 0) 83 84 explain1 := tree.ExplainOptions{Mode: tree.ExplainPlan} 85 explain1.Flags[1] = true 86 explain1.Flags[2] = true 87 explain2 := tree.ExplainOptions{Mode: tree.ExplainOpt} 88 explain2.Flags[1] = true 89 explain2.Flags[2] = true 90 explain3 := tree.ExplainOptions{Mode: tree.ExplainOpt} 91 explain3.Flags[1] = true 92 explain3.Flags[2] = true 93 explain3.Flags[3] = true 94 95 scanNode := &ScanExpr{} 96 andExpr := &AndExpr{} 97 98 projections1 := ProjectionsExpr{{Element: andExpr, Col: 0}} 99 projections2 := ProjectionsExpr{{Element: andExpr, Col: 0}} 100 projections3 := ProjectionsExpr{{Element: andExpr, Col: 1}} 101 projections4 := ProjectionsExpr{ 102 {Element: andExpr, Col: 1}, 103 {Element: andExpr, Col: 2}, 104 } 105 projections5 := ProjectionsExpr{{Element: &AndExpr{}, Col: 1}} 106 107 aggs1 := AggregationsExpr{{Agg: CountRowsSingleton, Col: 0}} 108 aggs2 := AggregationsExpr{{Agg: CountRowsSingleton, Col: 0}} 109 aggs3 := AggregationsExpr{{Agg: CountRowsSingleton, Col: 1}} 110 aggs4 := AggregationsExpr{ 111 {Agg: CountRowsSingleton, Col: 1}, 112 {Agg: CountRowsSingleton, Col: 2}, 113 } 114 aggs5 := AggregationsExpr{{Agg: &CountRowsExpr{}, Col: 1}} 115 116 int1 := &ConstExpr{Value: tree.NewDInt(10)} 117 int2 := &ConstExpr{Value: tree.NewDInt(20)} 118 frame1 := WindowFrame{ 119 Mode: tree.RANGE, 120 StartBoundType: tree.UnboundedPreceding, 121 EndBoundType: tree.CurrentRow, 122 FrameExclusion: tree.NoExclusion, 123 } 124 frame2 := WindowFrame{ 125 Mode: tree.ROWS, 126 StartBoundType: tree.UnboundedPreceding, 127 EndBoundType: tree.CurrentRow, 128 FrameExclusion: tree.NoExclusion, 129 } 130 131 wins1 := WindowsExpr{{ 132 Function: RankSingleton, 133 WindowsItemPrivate: WindowsItemPrivate{Col: 0, Frame: frame1}, 134 }} 135 wins2 := WindowsExpr{{ 136 Function: RankSingleton, 137 WindowsItemPrivate: WindowsItemPrivate{Col: 0, Frame: frame1}, 138 }} 139 wins3 := WindowsExpr{{ 140 Function: &WindowFromOffsetExpr{Input: RankSingleton, Offset: int1}, 141 WindowsItemPrivate: WindowsItemPrivate{Col: 0, Frame: frame1}, 142 }} 143 wins4 := WindowsExpr{{ 144 Function: &WindowFromOffsetExpr{Input: RankSingleton, Offset: int2}, 145 WindowsItemPrivate: WindowsItemPrivate{Col: 0, Frame: frame1}, 146 }} 147 wins5 := WindowsExpr{{ 148 Function: RankSingleton, 149 WindowsItemPrivate: WindowsItemPrivate{Col: 0, Frame: frame2}, 150 }} 151 152 type testVariation struct { 153 val1 interface{} 154 val2 interface{} 155 equal bool 156 } 157 158 testCases := []struct { 159 hashFn interface{} 160 eqFn interface{} 161 variations []testVariation 162 }{ 163 {hashFn: in.hasher.HashBool, eqFn: in.hasher.IsBoolEqual, variations: []testVariation{ 164 {val1: true, val2: true, equal: true}, 165 {val1: true, val2: false, equal: false}, 166 }}, 167 168 {hashFn: in.hasher.HashInt, eqFn: in.hasher.IsIntEqual, variations: []testVariation{ 169 {val1: 1, val2: 1, equal: true}, 170 {val1: 0, val2: 1, equal: false}, 171 {val1: intsets.MaxInt, val2: intsets.MaxInt, equal: true}, 172 {val1: intsets.MinInt, val2: intsets.MaxInt, equal: false}, 173 }}, 174 175 {hashFn: in.hasher.HashFloat64, eqFn: in.hasher.IsFloat64Equal, variations: []testVariation{ 176 {val1: float64(0), val2: float64(0), equal: true}, 177 {val1: float64(1e300), val2: float64(1e-300), equal: false}, 178 {val1: math.MaxFloat64, val2: math.MaxFloat64, equal: true}, 179 {val1: math.NaN(), val2: math.NaN(), equal: true}, 180 {val1: math.Inf(1), val2: math.Inf(1), equal: true}, 181 {val1: math.Inf(-1), val2: math.Inf(1), equal: false}, 182 {val1: math.NaN(), val2: math.Inf(1), equal: false}, 183 184 // Use Copysign to create negative zero float64. 185 {val1: float64(0), val2: math.Copysign(0, -1), equal: false}, 186 }}, 187 188 {hashFn: in.hasher.HashRune, eqFn: in.hasher.IsRuneEqual, variations: []testVariation{ 189 {val1: rune(0), val2: rune(0), equal: true}, 190 {val1: rune('a'), val2: rune('b'), equal: false}, 191 {val1: rune('a'), val2: rune('A'), equal: false}, 192 {val1: rune('🐛'), val2: rune('🐛'), equal: true}, 193 }}, 194 195 {hashFn: in.hasher.HashString, eqFn: in.hasher.IsStringEqual, variations: []testVariation{ 196 {val1: "", val2: "", equal: true}, 197 {val1: "abc", val2: "abcd", equal: false}, 198 {val1: "", val2: " ", equal: false}, 199 {val1: "the quick brown fox", val2: "the quick brown fox", equal: true}, 200 }}, 201 202 {hashFn: in.hasher.HashByte, eqFn: in.hasher.IsByteEqual, variations: []testVariation{ 203 {val1: byte(0), val2: byte(0), equal: true}, 204 {val1: byte('a'), val2: byte('b'), equal: false}, 205 {val1: byte('a'), val2: byte('A'), equal: false}, 206 {val1: byte('z'), val2: byte('z'), equal: true}, 207 }}, 208 209 {hashFn: in.hasher.HashBytes, eqFn: in.hasher.IsBytesEqual, variations: []testVariation{ 210 {val1: []byte{}, val2: []byte{}, equal: true}, 211 {val1: []byte{}, val2: []byte{0}, equal: false}, 212 {val1: []byte{1, 2, 3}, val2: []byte{1, 2, 3, 4}, equal: false}, 213 {val1: []byte{10, 1, 100}, val2: []byte{10, 1, 100}, equal: true}, 214 }}, 215 216 {hashFn: in.hasher.HashOperator, eqFn: in.hasher.IsOperatorEqual, variations: []testVariation{ 217 {val1: opt.AndOp, val2: opt.AndOp, equal: true}, 218 {val1: opt.SelectOp, val2: opt.InnerJoinOp, equal: false}, 219 }}, 220 221 {hashFn: in.hasher.HashGoType, eqFn: in.hasher.IsGoTypeEqual, variations: []testVariation{ 222 {val1: reflect.TypeOf(int(0)), val2: reflect.TypeOf(int(1)), equal: true}, 223 {val1: reflect.TypeOf(int64(0)), val2: reflect.TypeOf(int32(0)), equal: false}, 224 }}, 225 226 {hashFn: in.hasher.HashDatum, eqFn: in.hasher.IsDatumEqual, variations: []testVariation{ 227 {val1: tree.DBoolTrue, val2: tree.DBoolTrue, equal: true}, 228 {val1: tree.DBoolTrue, val2: tree.DBoolFalse, equal: false}, 229 230 {val1: tree.NewDInt(0), val2: tree.NewDInt(0), equal: true}, 231 {val1: tree.NewDInt(tree.DInt(intsets.MinInt)), val2: tree.NewDInt(tree.DInt(intsets.MinInt)), equal: true}, 232 {val1: tree.NewDInt(tree.DInt(intsets.MinInt)), val2: tree.NewDInt(tree.DInt(intsets.MaxInt)), equal: false}, 233 234 {val1: tree.NewDFloat(0), val2: tree.NewDFloat(0), equal: true}, 235 {val1: tree.NewDFloat(tree.DFloat(math.NaN())), val2: tree.NewDFloat(tree.DFloat(math.NaN())), equal: true}, 236 {val1: tree.NewDFloat(tree.DFloat(math.Inf(-1))), val2: tree.NewDFloat(tree.DFloat(math.Inf(1))), equal: false}, 237 238 {val1: tree.NewDString(""), val2: tree.NewDString(""), equal: true}, 239 {val1: tree.NewDString("123"), val2: tree.NewDString("123"), equal: true}, 240 {val1: tree.NewDString(" "), val2: tree.NewDString("\t"), equal: false}, 241 242 {val1: tree.NewDBytes(tree.DBytes([]byte{0})), val2: tree.NewDBytes(tree.DBytes([]byte{0})), equal: true}, 243 {val1: tree.NewDBytes("foo"), val2: tree.NewDBytes("foo2"), equal: false}, 244 245 {val1: tree.NewDDate(pgdate.LowDate), val2: tree.NewDDate(pgdate.LowDate), equal: true}, 246 {val1: tree.NewDDate(pgdate.LowDate), val2: tree.NewDDate(pgdate.HighDate), equal: false}, 247 248 {val1: tree.MakeDTime(timeofday.Min), val2: tree.MakeDTime(timeofday.Min), equal: true}, 249 {val1: tree.MakeDTime(timeofday.Min), val2: tree.MakeDTime(timeofday.Max), equal: false}, 250 251 {val1: json1, val2: json2, equal: true}, 252 {val1: json2, val2: json3, equal: false}, 253 254 {val1: tup1, val2: tup2, equal: true}, 255 {val1: tup2, val2: tup3, equal: false}, 256 {val1: tup3, val2: tup4, equal: false}, 257 {val1: tup1, val2: tup5, equal: false}, 258 {val1: tup6, val2: tup7, equal: false}, 259 260 {val1: arr1, val2: arr2, equal: true}, 261 {val1: arr2, val2: arr3, equal: false}, 262 {val1: arr4, val2: arr5, equal: false}, 263 {val1: arr4, val2: arr6, equal: false}, 264 {val1: arr6, val2: arr7, equal: false}, 265 266 {val1: dec1, val2: dec2, equal: true}, 267 {val1: dec2, val2: dec3, equal: false}, 268 {val1: dec3, val2: dec4, equal: false}, 269 {val1: dec4, val2: dec5, equal: true}, 270 271 {val1: coll1, val2: coll2, equal: true}, 272 {val1: coll2, val2: coll3, equal: false}, 273 {val1: coll3, val2: coll4, equal: false}, 274 275 {val1: tz1, val2: tz2, equal: true}, 276 {val1: tz2, val2: tz3, equal: false}, 277 {val1: tz3, val2: tz4, equal: false}, 278 279 {val1: tree.NewDOid(0), val2: tree.NewDOid(0), equal: true}, 280 {val1: tree.NewDOid(0), val2: tree.NewDOid(1), equal: false}, 281 282 {val1: tree.NewDInt(0), val2: tree.NewDFloat(0), equal: false}, 283 {val1: tree.NewDInt(0), val2: tree.NewDOid(0), equal: false}, 284 }}, 285 286 {hashFn: in.hasher.HashType, eqFn: in.hasher.IsTypeEqual, variations: []testVariation{ 287 {val1: types.Int, val2: types.Int, equal: true}, 288 {val1: tupTyp1, val2: tupTyp2, equal: true}, 289 {val1: tupTyp2, val2: tupTyp3, equal: false}, 290 {val1: tupTyp3, val2: tupTyp4, equal: false}, 291 }}, 292 293 {hashFn: in.hasher.HashTypedExpr, eqFn: in.hasher.IsTypedExprEqual, variations: []testVariation{ 294 {val1: tup1, val2: tup1, equal: true}, 295 {val1: tup1, val2: tup2, equal: false}, 296 {val1: tup2, val2: tup3, equal: false}, 297 }}, 298 299 {hashFn: in.hasher.HashColumnID, eqFn: in.hasher.IsColumnIDEqual, variations: []testVariation{ 300 {val1: opt.ColumnID(0), val2: opt.ColumnID(0), equal: true}, 301 {val1: opt.ColumnID(0), val2: opt.ColumnID(1), equal: false}, 302 }}, 303 304 {hashFn: in.hasher.HashColSet, eqFn: in.hasher.IsColSetEqual, variations: []testVariation{ 305 {val1: opt.MakeColSet(), val2: opt.MakeColSet(), equal: true}, 306 {val1: opt.MakeColSet(1, 2, 3), val2: opt.MakeColSet(3, 2, 1), equal: true}, 307 {val1: opt.MakeColSet(1, 2, 3), val2: opt.MakeColSet(1, 2), equal: false}, 308 }}, 309 310 {hashFn: in.hasher.HashColList, eqFn: in.hasher.IsColListEqual, variations: []testVariation{ 311 {val1: opt.ColList{}, val2: opt.ColList{}, equal: true}, 312 {val1: opt.ColList{1, 2, 3}, val2: opt.ColList{1, 2, 3}, equal: true}, 313 {val1: opt.ColList{1, 2, 3}, val2: opt.ColList{3, 2, 1}, equal: false}, 314 {val1: opt.ColList{1, 2}, val2: opt.ColList{1, 2, 3}, equal: false}, 315 }}, 316 317 {hashFn: in.hasher.HashOrdering, eqFn: in.hasher.IsOrderingEqual, variations: []testVariation{ 318 {val1: opt.Ordering{}, val2: opt.Ordering{}, equal: true}, 319 {val1: opt.Ordering{-1, 1}, val2: opt.Ordering{-1, 1}, equal: true}, 320 {val1: opt.Ordering{-1, 1}, val2: opt.Ordering{1, -1}, equal: false}, 321 {val1: opt.Ordering{-1, 1}, val2: opt.Ordering{-1, 1, 2}, equal: false}, 322 }}, 323 324 {hashFn: in.hasher.HashOrderingChoice, eqFn: in.hasher.IsOrderingChoiceEqual, variations: []testVariation{ 325 {val1: physical.ParseOrderingChoice(""), val2: physical.ParseOrderingChoice(""), equal: true}, 326 {val1: physical.ParseOrderingChoice("+1"), val2: physical.ParseOrderingChoice("+1"), equal: true}, 327 {val1: physical.ParseOrderingChoice("+(1|2)"), val2: physical.ParseOrderingChoice("+(2|1)"), equal: true}, 328 {val1: physical.ParseOrderingChoice("+1 opt(2)"), val2: physical.ParseOrderingChoice("+1 opt(2)"), equal: true}, 329 {val1: physical.ParseOrderingChoice("+1"), val2: physical.ParseOrderingChoice("-1"), equal: false}, 330 {val1: physical.ParseOrderingChoice("+1,+2"), val2: physical.ParseOrderingChoice("+1"), equal: false}, 331 {val1: physical.ParseOrderingChoice("+(1|2)"), val2: physical.ParseOrderingChoice("+1"), equal: false}, 332 {val1: physical.ParseOrderingChoice("+1 opt(2)"), val2: physical.ParseOrderingChoice("+1"), equal: false}, 333 }}, 334 335 {hashFn: in.hasher.HashTableID, eqFn: in.hasher.IsTableIDEqual, variations: []testVariation{ 336 {val1: opt.TableID(0), val2: opt.TableID(0), equal: true}, 337 {val1: opt.TableID(0), val2: opt.TableID(1), equal: false}, 338 }}, 339 340 {hashFn: in.hasher.HashSchemaID, eqFn: in.hasher.IsSchemaIDEqual, variations: []testVariation{ 341 {val1: opt.SchemaID(0), val2: opt.SchemaID(0), equal: true}, 342 {val1: opt.SchemaID(0), val2: opt.SchemaID(1), equal: false}, 343 }}, 344 345 {hashFn: in.hasher.HashScanLimit, eqFn: in.hasher.IsScanLimitEqual, variations: []testVariation{ 346 {val1: ScanLimit(100), val2: ScanLimit(100), equal: true}, 347 {val1: ScanLimit(0), val2: ScanLimit(1), equal: false}, 348 }}, 349 350 {hashFn: in.hasher.HashScanFlags, eqFn: in.hasher.IsScanFlagsEqual, variations: []testVariation{ 351 {val1: ScanFlags{}, val2: ScanFlags{}, equal: true}, 352 {val1: ScanFlags{NoIndexJoin: false}, val2: ScanFlags{NoIndexJoin: true}, equal: false}, 353 {val1: ScanFlags{NoIndexJoin: true}, val2: ScanFlags{NoIndexJoin: true}, equal: true}, 354 {val1: ScanFlags{ForceIndex: false}, val2: ScanFlags{ForceIndex: true}, equal: false}, 355 {val1: ScanFlags{ForceIndex: true}, val2: ScanFlags{ForceIndex: true}, equal: true}, 356 {val1: ScanFlags{Direction: tree.Descending}, val2: ScanFlags{Direction: tree.Ascending}, equal: false}, 357 {val1: ScanFlags{Direction: tree.Ascending}, val2: ScanFlags{Direction: tree.Ascending}, equal: true}, 358 {val1: ScanFlags{Index: 1}, val2: ScanFlags{Index: 2}, equal: false}, 359 {val1: ScanFlags{Index: 2}, val2: ScanFlags{Index: 2}, equal: true}, 360 {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: true, Index: 1}, equal: true}, 361 {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: true, Index: 2}, equal: false}, 362 {val1: ScanFlags{NoIndexJoin: true, Index: 1}, val2: ScanFlags{NoIndexJoin: false, Index: 1}, equal: false}, 363 { 364 val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 365 val2: ScanFlags{NoIndexJoin: false, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 366 equal: false, 367 }, 368 { 369 val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 370 val2: ScanFlags{NoIndexJoin: true, ForceIndex: false, Direction: tree.Ascending, Index: 1}, 371 equal: false, 372 }, 373 { 374 val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 375 val2: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Descending, Index: 1}, 376 equal: false, 377 }, 378 { 379 val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 380 val2: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 2}, 381 equal: false, 382 }, 383 { 384 val1: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 385 val2: ScanFlags{NoIndexJoin: true, ForceIndex: true, Direction: tree.Ascending, Index: 1}, 386 equal: true, 387 }, 388 }}, 389 390 {hashFn: in.hasher.HashPointer, eqFn: in.hasher.IsPointerEqual, variations: []testVariation{ 391 {val1: unsafe.Pointer((*tree.Subquery)(nil)), val2: unsafe.Pointer((*tree.Subquery)(nil)), equal: true}, 392 {val1: unsafe.Pointer(&tree.Subquery{}), val2: unsafe.Pointer(&tree.Subquery{}), equal: false}, 393 }}, 394 395 {hashFn: in.hasher.HashExplainOptions, eqFn: in.hasher.IsExplainOptionsEqual, variations: []testVariation{ 396 {val1: tree.ExplainOptions{}, val2: tree.ExplainOptions{}, equal: true}, 397 {val1: explain1, val2: explain1, equal: true}, 398 {val1: explain1, val2: explain2, equal: false}, 399 {val1: explain2, val2: explain3, equal: false}, 400 }}, 401 402 {hashFn: in.hasher.HashShowTraceType, eqFn: in.hasher.IsShowTraceTypeEqual, variations: []testVariation{ 403 {val1: tree.ShowTraceKV, val2: tree.ShowTraceKV, equal: true}, 404 {val1: tree.ShowTraceKV, val2: tree.ShowTraceRaw, equal: false}, 405 }}, 406 407 {hashFn: in.hasher.HashWindowFrame, eqFn: in.hasher.IsWindowFrameEqual, variations: []testVariation{ 408 { 409 val1: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 410 val2: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 411 equal: true, 412 }, 413 { 414 val1: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 415 val2: WindowFrame{tree.ROWS, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 416 equal: false, 417 }, 418 { 419 val1: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 420 val2: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.UnboundedFollowing, tree.NoExclusion}, 421 equal: false, 422 }, 423 { 424 val1: WindowFrame{tree.RANGE, tree.UnboundedPreceding, tree.CurrentRow, tree.NoExclusion}, 425 val2: WindowFrame{tree.RANGE, tree.CurrentRow, tree.CurrentRow, tree.NoExclusion}, 426 equal: false, 427 }, 428 }}, 429 430 {hashFn: in.hasher.HashTupleOrdinal, eqFn: in.hasher.IsTupleOrdinalEqual, variations: []testVariation{ 431 {val1: TupleOrdinal(0), val2: TupleOrdinal(0), equal: true}, 432 {val1: TupleOrdinal(0), val2: TupleOrdinal(1), equal: false}, 433 }}, 434 435 // PhysProps hash/isEqual methods are tested in TestInternerPhysProps. 436 437 {hashFn: in.hasher.HashLockingItem, eqFn: in.hasher.IsLockingItemEqual, variations: []testVariation{ 438 {val1: (*tree.LockingItem)(nil), val2: (*tree.LockingItem)(nil), equal: true}, 439 { 440 val1: (*tree.LockingItem)(nil), 441 val2: &tree.LockingItem{Strength: tree.ForUpdate}, 442 equal: false, 443 }, 444 { 445 val1: &tree.LockingItem{Strength: tree.ForShare}, 446 val2: &tree.LockingItem{Strength: tree.ForUpdate}, 447 equal: false, 448 }, 449 { 450 val1: &tree.LockingItem{WaitPolicy: tree.LockWaitSkip}, 451 val2: &tree.LockingItem{WaitPolicy: tree.LockWaitError}, 452 equal: false, 453 }, 454 { 455 val1: &tree.LockingItem{Strength: tree.ForUpdate, WaitPolicy: tree.LockWaitError}, 456 val2: &tree.LockingItem{Strength: tree.ForUpdate, WaitPolicy: tree.LockWaitError}, 457 equal: true, 458 }, 459 }}, 460 461 {hashFn: in.hasher.HashRelExpr, eqFn: in.hasher.IsRelExprEqual, variations: []testVariation{ 462 {val1: (*ScanExpr)(nil), val2: (*ScanExpr)(nil), equal: true}, 463 {val1: scanNode, val2: scanNode, equal: true}, 464 {val1: &ScanExpr{}, val2: &ScanExpr{}, equal: false}, 465 }}, 466 467 {hashFn: in.hasher.HashScalarExpr, eqFn: in.hasher.IsScalarExprEqual, variations: []testVariation{ 468 {val1: (*AndExpr)(nil), val2: (*AndExpr)(nil), equal: true}, 469 {val1: andExpr, val2: andExpr, equal: true}, 470 {val1: &AndExpr{}, val2: &AndExpr{}, equal: false}, 471 }}, 472 473 {hashFn: in.hasher.HashScalarListExpr, eqFn: in.hasher.IsScalarListExprEqual, variations: []testVariation{ 474 {val1: ScalarListExpr{andExpr, andExpr}, val2: ScalarListExpr{andExpr, andExpr}, equal: true}, 475 {val1: ScalarListExpr{andExpr, andExpr}, val2: ScalarListExpr{andExpr}, equal: false}, 476 {val1: ScalarListExpr{&AndExpr{}}, val2: ScalarListExpr{&AndExpr{}}, equal: false}, 477 }}, 478 479 {hashFn: in.hasher.HashFiltersExpr, eqFn: in.hasher.IsFiltersExprEqual, variations: []testVariation{ 480 {val1: FiltersExpr{{Condition: andExpr}}, val2: FiltersExpr{{Condition: andExpr}}, equal: true}, 481 {val1: FiltersExpr{{Condition: andExpr}}, val2: FiltersExpr{}, equal: false}, 482 {val1: FiltersExpr{{Condition: &AndExpr{}}}, val2: FiltersExpr{{Condition: &AndExpr{}}}, equal: false}, 483 }}, 484 485 {hashFn: in.hasher.HashProjectionsExpr, eqFn: in.hasher.IsProjectionsExprEqual, variations: []testVariation{ 486 {val1: projections1, val2: projections2, equal: true}, 487 {val1: projections2, val2: projections3, equal: false}, 488 {val1: projections3, val2: projections4, equal: false}, 489 {val1: projections3, val2: projections5, equal: false}, 490 }}, 491 492 {hashFn: in.hasher.HashAggregationsExpr, eqFn: in.hasher.IsAggregationsExprEqual, variations: []testVariation{ 493 {val1: aggs1, val2: aggs2, equal: true}, 494 {val1: aggs2, val2: aggs3, equal: false}, 495 {val1: aggs3, val2: aggs4, equal: false}, 496 {val1: aggs3, val2: aggs5, equal: false}, 497 }}, 498 499 {hashFn: in.hasher.HashWindowsExpr, eqFn: in.hasher.IsWindowsExprEqual, variations: []testVariation{ 500 {val1: wins1, val2: wins2, equal: true}, 501 {val1: wins1, val2: wins3, equal: false}, 502 {val1: wins2, val2: wins3, equal: false}, 503 {val1: wins3, val2: wins4, equal: false}, 504 {val1: wins1, val2: wins5, equal: false}, 505 }}, 506 } 507 508 computeHashValue := func(hashFn reflect.Value, val interface{}) internHash { 509 in.hasher.Init() 510 hashFn.Call([]reflect.Value{reflect.ValueOf(val)}) 511 return in.hasher.hash 512 } 513 514 isEqual := func(eqFn reflect.Value, val1, val2 interface{}) bool { 515 in.hasher.Init() 516 res := eqFn.Call([]reflect.Value{reflect.ValueOf(val1), reflect.ValueOf(val2)}) 517 return res[0].Interface().(bool) 518 } 519 520 for _, tc := range testCases { 521 hashFn := reflect.ValueOf(tc.hashFn) 522 eqFn := reflect.ValueOf(tc.eqFn) 523 524 for _, tv := range tc.variations { 525 hash1 := computeHashValue(hashFn, tv.val1) 526 hash2 := computeHashValue(hashFn, tv.val2) 527 528 if tv.equal && hash1 != hash2 { 529 t.Errorf("expected hash values to be equal for %v and %v", tv.val1, tv.val2) 530 } else if !tv.equal && hash1 == hash2 { 531 t.Errorf("expected hash values to not be equal for %v and %v", tv.val1, tv.val2) 532 } 533 534 eq := isEqual(eqFn, tv.val1, tv.val2) 535 if tv.equal && !eq { 536 t.Errorf("expected values to be equal for %v and %v", tv.val1, tv.val2) 537 } else if !tv.equal && eq { 538 t.Errorf("expected values to not be equal for %v and %v", tv.val1, tv.val2) 539 } 540 } 541 } 542 } 543 544 func TestInternerPhysProps(t *testing.T) { 545 var in interner 546 547 physProps1 := physical.Required{ 548 Presentation: physical.Presentation{{Alias: "c", ID: 1}}, 549 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5)"), 550 } 551 physProps2 := physical.Required{ 552 Presentation: physical.Presentation{{Alias: "c", ID: 1}}, 553 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5)"), 554 } 555 physProps3 := physical.Required{ 556 Presentation: physical.Presentation{{Alias: "d", ID: 1}}, 557 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5)"), 558 } 559 physProps4 := physical.Required{ 560 Presentation: physical.Presentation{{Alias: "d", ID: 2}}, 561 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5)"), 562 } 563 physProps5 := physical.Required{ 564 Presentation: physical.Presentation{{Alias: "d", ID: 2}, {Alias: "e", ID: 3}}, 565 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5)"), 566 } 567 physProps6 := physical.Required{ 568 Presentation: physical.Presentation{{Alias: "d", ID: 2}, {Alias: "e", ID: 3}}, 569 Ordering: physical.ParseOrderingChoice("+(1|2),+3 opt(4,5,6)"), 570 } 571 572 testCases := []struct { 573 phys *physical.Required 574 inCache bool 575 }{ 576 {phys: &physProps1, inCache: false}, 577 {phys: &physProps1, inCache: true}, 578 {phys: &physProps2, inCache: true}, 579 {phys: &physProps3, inCache: false}, 580 {phys: &physProps4, inCache: false}, 581 {phys: &physProps5, inCache: false}, 582 {phys: &physProps6, inCache: false}, 583 } 584 585 inCache := make(map[*physical.Required]bool) 586 587 for _, tc := range testCases { 588 interned := in.InternPhysicalProps(tc.phys) 589 if tc.inCache && !inCache[interned] { 590 t.Errorf("expected physical props to already be in cache: %s", tc.phys) 591 } else if !tc.inCache && inCache[interned] { 592 t.Errorf("expected physical props to not yet be in cache: %s", tc.phys) 593 } 594 inCache[interned] = true 595 } 596 } 597 598 func TestInternerCollision(t *testing.T) { 599 var in interner 600 601 // Start with a non-colliding value to make sure it doesn't interfere with 602 // subsequent values. 603 in.hasher.Init() 604 in.hasher.HashString("no-collide") 605 in.cache.Start(in.hasher.hash) 606 in.cache.Next() 607 in.cache.Add("no-collide") 608 609 // Intern a string that will "collide" with other values. 610 in.hasher.Init() 611 in.hasher.HashString("foo") 612 in.cache.Start(in.hasher.hash) 613 in.cache.Next() 614 in.cache.Add("foo") 615 616 // Now simulate a collision by using same hash as "foo". 617 in.cache.Start(in.hasher.hash) 618 in.cache.Next() 619 in.cache.Next() 620 in.cache.Add("bar") 621 622 // And another. 623 in.cache.Start(in.hasher.hash) 624 in.cache.Next() 625 in.cache.Next() 626 in.cache.Next() 627 in.cache.Add("baz") 628 629 // Ensure that first item can still be located. 630 in.cache.Start(in.hasher.hash) 631 if !in.cache.Next() || in.cache.Item() != "foo" { 632 t.Errorf("expected to find foo in cache after collision") 633 } 634 635 // Expect to find colliding item as well. 636 if !in.cache.Next() || in.cache.Item() != "bar" { 637 t.Errorf("expected to find bar in cache after collision") 638 } 639 640 // And last colliding item. 641 if !in.cache.Next() || in.cache.Item() != "baz" { 642 t.Errorf("expected to find baz in cache after collision") 643 } 644 645 // Should be no more items. 646 if in.cache.Next() { 647 t.Errorf("expected no more colliding items in cache") 648 } 649 } 650 651 func BenchmarkEncodeDatum(b *testing.B) { 652 r := rand.New(rand.NewSource(0)) 653 datums := make([]tree.Datum, 10000) 654 for i := range datums { 655 datums[i] = sqlbase.RandDatumWithNullChance(r, sqlbase.RandEncodableType(r), 0) 656 } 657 b.ResetTimer() 658 for i := 0; i < b.N; i++ { 659 for _, d := range datums { 660 encodeDatum(nil, d) 661 } 662 } 663 } 664 665 func BenchmarkIsDatumEqual(b *testing.B) { 666 r := rand.New(rand.NewSource(0)) 667 datums := make([]tree.Datum, 1000) 668 for i := range datums { 669 datums[i] = sqlbase.RandDatumWithNullChance(r, sqlbase.RandEncodableType(r), 0) 670 } 671 b.ResetTimer() 672 var h hasher 673 for i := 0; i < b.N; i++ { 674 for _, d := range datums { 675 // IsDatumEqual is only called on values that hash the 676 // same, so only benchmark it on identical datums. 677 h.IsDatumEqual(d, d) 678 } 679 } 680 }