vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/concatenate_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 "errors" 22 "fmt" 23 "testing" 24 25 "vitess.io/vitess/go/test/utils" 26 27 "github.com/stretchr/testify/require" 28 29 "vitess.io/vitess/go/sqltypes" 30 ) 31 32 func r(names, types string, rows ...string) *sqltypes.Result { 33 return sqltypes.MakeTestResult(sqltypes.MakeTestFields(names, types), rows...) 34 } 35 36 func TestConcatenate_NoErrors(t *testing.T) { 37 type testCase struct { 38 testName string 39 inputs []*sqltypes.Result 40 expectedResult *sqltypes.Result 41 expectedError string 42 ignoreTypes []int 43 } 44 45 r1 := r("id|col1|col2", "int64|varbinary|varbinary", "1|a1|b1", "2|a2|b2") 46 r2 := r("id|col1|col2", "int32|varbinary|varbinary", "1|a1|b1", "2|a2|b2") 47 combinedResult := r1 48 combinedResult.Rows = append(combinedResult.Rows, r2.Rows...) 49 50 testCases := []*testCase{{ 51 testName: "empty results", 52 inputs: []*sqltypes.Result{ 53 r("id1|col11|col12", "int64|varbinary|varbinary"), 54 r("id2|col21|col22", "int64|varbinary|varbinary"), 55 r("id3|col31|col32", "int64|varbinary|varbinary"), 56 }, 57 expectedResult: r("id1|col11|col12", "int64|varbinary|varbinary"), 58 }, { 59 testName: "2 non empty result", 60 inputs: []*sqltypes.Result{ 61 r("myid|mycol1|mycol2", "int64|varchar|varbinary", "11|m1|n1", "22|m2|n2"), 62 r("id|col1|col2", "int64|varchar|varbinary", "1|a1|b1", "2|a2|b2"), 63 r("id2|col2|col3", "int64|varchar|varbinary", "3|a3|b3"), 64 r("id2|col2|col3", "int64|varchar|varbinary", "4|a4|b4"), 65 }, 66 expectedResult: r("myid|mycol1|mycol2", "int64|varchar|varbinary", "11|m1|n1", "22|m2|n2", "1|a1|b1", "2|a2|b2", "3|a3|b3", "4|a4|b4"), 67 }, { 68 testName: "mismatch field type", 69 inputs: []*sqltypes.Result{ 70 r("id|col1|col2", "int64|varbinary|varbinary", "1|a1|b1", "2|a2|b2"), 71 r("id|col1|col2", "int64|varbinary|varbinary", "1|a1|b1", "2|a2|b2"), 72 r("id|col3|col4", "int64|varchar|varbinary", "1|a1|b1", "2|a2|b2"), 73 }, 74 expectedError: "merging field of different types is not supported", 75 }, { 76 testName: "ignored field types - ignored", 77 inputs: []*sqltypes.Result{ 78 r("id|col1|col2", "int64|varbinary|varbinary", "1|a1|b1", "2|a2|b2"), 79 r("id|col1|col2", "int32|varbinary|varbinary", "1|a1|b1", "2|a2|b2"), 80 }, 81 expectedResult: combinedResult, 82 ignoreTypes: []int{0}, 83 }, { 84 testName: "input source has different column count", 85 inputs: []*sqltypes.Result{ 86 r("id|col1|col2", "int64|varchar|varchar", "1|a1|b1", "2|a2|b2"), 87 r("id|col1|col2", "int64|varchar|varchar", "1|a1|b1", "2|a2|b2"), 88 r("id|col3|col4|col5", "int64|varchar|varchar|int32", "1|a1|b1|5", "2|a2|b2|6"), 89 }, 90 expectedError: "The used SELECT statements have a different number of columns", 91 }, { 92 testName: "1 empty result and 1 non empty result", 93 inputs: []*sqltypes.Result{ 94 r("myid|mycol1|mycol2", "int64|varchar|varbinary"), 95 r("id|col1|col2", "int64|varchar|varbinary", "1|a1|b1", "2|a2|b2"), 96 }, 97 expectedResult: r("myid|mycol1|mycol2", "int64|varchar|varbinary", "1|a1|b1", "2|a2|b2"), 98 }} 99 100 for _, tc := range testCases { 101 var sources []Primitive 102 for _, input := range tc.inputs { 103 // input is added twice, since the first one is used by execute and the next by stream execute 104 sources = append(sources, &fakePrimitive{results: []*sqltypes.Result{input, input}}) 105 } 106 107 concatenate := NewConcatenate(sources, tc.ignoreTypes) 108 109 t.Run(tc.testName+"-Execute", func(t *testing.T) { 110 qr, err := concatenate.TryExecute(context.Background(), &noopVCursor{}, nil, true) 111 if tc.expectedError == "" { 112 require.NoError(t, err) 113 require.Equal(t, tc.expectedResult, qr) 114 } else { 115 require.Error(t, err) 116 require.Contains(t, err.Error(), tc.expectedError) 117 } 118 }) 119 120 t.Run(tc.testName+"-StreamExecute", func(t *testing.T) { 121 qr, err := wrapStreamExecute(concatenate, &noopVCursor{}, nil, true) 122 if tc.expectedError == "" { 123 require.NoError(t, err) 124 require.Equal(t, utils.SortString(fmt.Sprintf("%v", tc.expectedResult.Rows)), utils.SortString(fmt.Sprintf("%v", qr.Rows))) 125 } else { 126 require.Error(t, err) 127 require.Contains(t, err.Error(), tc.expectedError) 128 } 129 }) 130 } 131 } 132 133 func TestConcatenate_WithErrors(t *testing.T) { 134 strFailed := "failed" 135 136 fake := r("id|col1|col2", "int64|varchar|varbinary", "1|a1|b1", "2|a2|b2") 137 concatenate := NewConcatenate( 138 []Primitive{ 139 &fakePrimitive{results: []*sqltypes.Result{fake, fake}}, 140 &fakePrimitive{results: []*sqltypes.Result{nil, nil}, sendErr: errors.New(strFailed)}, 141 &fakePrimitive{results: []*sqltypes.Result{fake, fake}}, 142 }, nil, 143 ) 144 _, err := concatenate.TryExecute(context.Background(), &noopVCursor{}, nil, true) 145 require.EqualError(t, err, strFailed) 146 147 _, err = wrapStreamExecute(concatenate, &noopVCursor{}, nil, true) 148 require.EqualError(t, err, strFailed) 149 150 concatenate = NewConcatenate( 151 []Primitive{ 152 &fakePrimitive{results: []*sqltypes.Result{fake, fake}}, 153 &fakePrimitive{results: []*sqltypes.Result{nil, nil}, sendErr: errors.New(strFailed)}, 154 &fakePrimitive{results: []*sqltypes.Result{fake, fake}}, 155 }, nil) 156 157 _, err = concatenate.TryExecute(context.Background(), &noopVCursor{}, nil, true) 158 require.EqualError(t, err, strFailed) 159 _, err = wrapStreamExecute(concatenate, &noopVCursor{}, nil, true) 160 require.EqualError(t, err, strFailed) 161 }