vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/distinct_test.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "context" 21 "fmt" 22 "testing" 23 24 "vitess.io/vitess/go/mysql/collations" 25 26 "vitess.io/vitess/go/test/utils" 27 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/sqltypes" 31 ) 32 33 func TestDistinct(t *testing.T) { 34 type testCase struct { 35 testName string 36 inputs *sqltypes.Result 37 collations []collations.ID 38 expectedResult *sqltypes.Result 39 expectedError string 40 } 41 42 testCases := []*testCase{{ 43 testName: "empty", 44 inputs: r("id1|col11|col12", "int64|varbinary|varbinary"), 45 expectedResult: r("id1|col11|col12", "int64|varbinary|varbinary"), 46 }, { 47 testName: "int64 numbers", 48 inputs: r("myid", "int64", "0", "1", "1", "null", "null"), 49 expectedResult: r("myid", "int64", "0", "1", "null"), 50 }, { 51 testName: "int64 numbers, two columns", 52 inputs: r("a|b", "int64|int64", "0|0", "1|1", "1|1", "null|null", "null|null", "1|2"), 53 expectedResult: r("a|b", "int64|int64", "0|0", "1|1", "null|null", "1|2"), 54 }, { 55 testName: "int64 numbers, two columns", 56 inputs: r("a|b", "int64|int64", "3|3", "3|3", "3|4", "5|1", "5|1"), 57 expectedResult: r("a|b", "int64|int64", "3|3", "3|4", "5|1"), 58 }, { 59 testName: "float64 columns designed to produce the same hashcode but not be equal", 60 inputs: r("a|b", "float64|float64", "0.1|0.2", "0.1|0.3", "0.1|0.4", "0.1|0.5"), 61 expectedResult: r("a|b", "float64|float64", "0.1|0.2", "0.1|0.3", "0.1|0.4", "0.1|0.5"), 62 }, { 63 testName: "varchar columns without collations", 64 inputs: r("myid", "varchar", "monkey", "horse"), 65 expectedError: "text type with an unknown/unsupported collation cannot be hashed", 66 }, { 67 testName: "varchar columns with collations", 68 collations: []collations.ID{collations.ID(0x21)}, 69 inputs: r("myid", "varchar", "monkey", "horse", "Horse", "Monkey", "horses", "MONKEY"), 70 expectedResult: r("myid", "varchar", "monkey", "horse", "horses"), 71 }, { 72 testName: "mixed columns", 73 collations: []collations.ID{collations.ID(0x21), collations.Unknown}, 74 inputs: r("myid|id", "varchar|int64", "monkey|1", "horse|1", "Horse|1", "Monkey|1", "horses|1", "MONKEY|2"), 75 expectedResult: r("myid|id", "varchar|int64", "monkey|1", "horse|1", "horses|1", "MONKEY|2"), 76 }} 77 78 for _, tc := range testCases { 79 var checkCols []CheckCol 80 if len(tc.inputs.Rows) > 0 { 81 for i := range tc.inputs.Rows[0] { 82 collID := collations.Unknown 83 if tc.collations != nil { 84 collID = tc.collations[i] 85 } 86 if sqltypes.IsNumber(tc.inputs.Fields[i].Type) { 87 collID = collations.CollationBinaryID 88 } 89 checkCols = append(checkCols, CheckCol{ 90 Col: i, 91 Collation: collID, 92 }) 93 } 94 } 95 t.Run(tc.testName+"-Execute", func(t *testing.T) { 96 distinct := &Distinct{ 97 Source: &fakePrimitive{results: []*sqltypes.Result{tc.inputs}}, 98 CheckCols: checkCols, 99 Truncate: false, 100 } 101 102 qr, err := distinct.TryExecute(context.Background(), &noopVCursor{}, nil, true) 103 if tc.expectedError == "" { 104 require.NoError(t, err) 105 got := fmt.Sprintf("%v", qr.Rows) 106 expected := fmt.Sprintf("%v", tc.expectedResult.Rows) 107 utils.MustMatch(t, expected, got, "result not what correct") 108 } else { 109 require.EqualError(t, err, tc.expectedError) 110 } 111 }) 112 t.Run(tc.testName+"-StreamExecute", func(t *testing.T) { 113 distinct := &Distinct{ 114 Source: &fakePrimitive{results: []*sqltypes.Result{tc.inputs}}, 115 CheckCols: checkCols, 116 } 117 118 result, err := wrapStreamExecute(distinct, &noopVCursor{}, nil, true) 119 120 if tc.expectedError == "" { 121 require.NoError(t, err) 122 got := fmt.Sprintf("%v", result.Rows) 123 expected := fmt.Sprintf("%v", tc.expectedResult.Rows) 124 utils.MustMatch(t, expected, got, "result not what correct") 125 } else { 126 require.EqualError(t, err, tc.expectedError) 127 } 128 }) 129 } 130 } 131 132 func TestWeightStringFallBack(t *testing.T) { 133 offsetOne := 1 134 checkCols := []CheckCol{{ 135 Col: 0, 136 WsCol: &offsetOne, 137 Collation: collations.Unknown, 138 }} 139 input := r("myid|weightstring(myid)", 140 "varchar|varbinary", 141 "monkey|monkey", 142 "horse|horse", 143 "horse|horse") 144 145 distinct := &Distinct{ 146 Source: &fakePrimitive{results: []*sqltypes.Result{input}}, 147 CheckCols: checkCols, 148 Truncate: true, 149 } 150 151 qr, err := distinct.TryExecute(context.Background(), &noopVCursor{}, nil, true) 152 require.NoError(t, err) 153 154 got := fmt.Sprintf("%v", qr.Rows) 155 expected := fmt.Sprintf("%v", r("myid", "varchar", "monkey", "horse").Rows) 156 utils.MustMatch(t, expected, got) 157 158 // the primitive must not change just because one run needed weight strings 159 utils.MustMatch(t, []CheckCol{{ 160 Col: 0, 161 WsCol: &offsetOne, 162 Collation: collations.Unknown, 163 }}, distinct.CheckCols, "checkCols should not be updated") 164 }