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  }