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  }