vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/lookup_hash_test.go (about) 1 /* 2 Copyright 2019 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 vindexes 18 19 import ( 20 "context" 21 "reflect" 22 "testing" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 27 "vitess.io/vitess/go/sqltypes" 28 "vitess.io/vitess/go/vt/key" 29 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 30 ) 31 32 func TestLookupHashNew(t *testing.T) { 33 l := createLookup(t, "lookup_hash", false /* writeOnly */) 34 if want, got := l.(*LookupHash).writeOnly, false; got != want { 35 t.Errorf("Create(lookup, false): %v, want %v", got, want) 36 } 37 38 l = createLookup(t, "lookup_hash", true) 39 if want, got := l.(*LookupHash).writeOnly, true; got != want { 40 t.Errorf("Create(lookup, false): %v, want %v", got, want) 41 } 42 43 _, err := CreateVindex("lookup_hash", "lookup_hash", map[string]string{ 44 "table": "t", 45 "from": "fromc", 46 "to": "toc", 47 "write_only": "invalid", 48 }) 49 want := "write_only value must be 'true' or 'false': 'invalid'" 50 if err == nil || err.Error() != want { 51 t.Errorf("Create(bad_scatter): %v, want %s", err, want) 52 } 53 } 54 55 func TestLookupHashInfo(t *testing.T) { 56 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 57 assert.Equal(t, 20, lookuphash.Cost()) 58 assert.Equal(t, "lookup_hash", lookuphash.String()) 59 assert.False(t, lookuphash.IsUnique()) 60 assert.True(t, lookuphash.NeedsVCursor()) 61 62 lookuphashunique := createLookup(t, "lookup_hash_unique", false /* writeOnly */) 63 assert.Equal(t, 10, lookuphashunique.Cost()) 64 assert.Equal(t, "lookup_hash_unique", lookuphashunique.String()) 65 assert.True(t, lookuphashunique.IsUnique()) 66 assert.True(t, lookuphashunique.NeedsVCursor()) 67 } 68 69 func TestLookupHashMap(t *testing.T) { 70 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 71 vc := &vcursor{numRows: 2} 72 73 got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) 74 require.NoError(t, err) 75 want := []key.Destination{ 76 key.DestinationKeyspaceIDs([][]byte{ 77 []byte("\x16k@\xb4J\xbaK\xd6"), 78 []byte("\x06\xe7\xea\"Βp\x8f"), 79 }), 80 key.DestinationKeyspaceIDs([][]byte{ 81 []byte("\x16k@\xb4J\xbaK\xd6"), 82 []byte("\x06\xe7\xea\"Βp\x8f"), 83 }), 84 } 85 if !reflect.DeepEqual(got, want) { 86 t.Errorf("Map(): %#v, want %+v", got, want) 87 } 88 89 // Test conversion fail. 90 vc.result = sqltypes.MakeTestResult( 91 sqltypes.MakeTestFields("b|a", "int64|varbinary"), 92 "1|notint", 93 ) 94 got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}) 95 require.NoError(t, err) 96 want = []key.Destination{key.DestinationKeyspaceIDs([][]byte{})} 97 if !reflect.DeepEqual(got, want) { 98 t.Errorf("Map(): %#v, want %#v", got, want) 99 } 100 101 // Test query fail. 102 vc.mustFail = true 103 _, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}) 104 wantErr := "lookup.Map: execute failed" 105 if err == nil || err.Error() != wantErr { 106 t.Errorf("lookuphash(query fail) err: %v, want %s", err, wantErr) 107 } 108 vc.mustFail = false 109 } 110 111 func TestLookupHashMapAbsent(t *testing.T) { 112 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 113 vc := &vcursor{numRows: 0} 114 115 got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) 116 require.NoError(t, err) 117 want := []key.Destination{ 118 key.DestinationNone{}, 119 key.DestinationNone{}, 120 } 121 if !reflect.DeepEqual(got, want) { 122 t.Errorf("Map(): %#v, want %+v", got, want) 123 } 124 125 // writeOnly true should return full keyranges. 126 lookuphash = createLookup(t, "lookup_hash", true) 127 got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}) 128 require.NoError(t, err) 129 want = []key.Destination{ 130 key.DestinationKeyRange{ 131 KeyRange: &topodatapb.KeyRange{}, 132 }, 133 key.DestinationKeyRange{ 134 KeyRange: &topodatapb.KeyRange{}, 135 }, 136 } 137 if !reflect.DeepEqual(got, want) { 138 t.Errorf("Map(): %#v, want %+v", got, want) 139 } 140 } 141 142 func TestLookupHashMapNull(t *testing.T) { 143 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 144 vc := &vcursor{numRows: 1, keys: []sqltypes.Value{sqltypes.NULL}} 145 146 got, err := lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NULL}) 147 require.NoError(t, err) 148 want := []key.Destination{ 149 key.DestinationKeyspaceIDs([][]byte{ 150 []byte("\x16k@\xb4J\xbaK\xd6"), 151 }), 152 } 153 if !reflect.DeepEqual(got, want) { 154 t.Errorf("Map(): %#v, want %+v", got, want) 155 } 156 157 // writeOnly true should return full keyranges. 158 lookuphash = createLookup(t, "lookup_hash", true) 159 got, err = lookuphash.Map(context.Background(), vc, []sqltypes.Value{sqltypes.NULL}) 160 require.NoError(t, err) 161 want = []key.Destination{ 162 key.DestinationKeyRange{ 163 KeyRange: &topodatapb.KeyRange{}, 164 }, 165 } 166 if !reflect.DeepEqual(got, want) { 167 t.Errorf("Map(): %#v, want %+v", got, want) 168 } 169 } 170 171 func TestLookupHashVerify(t *testing.T) { 172 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 173 vc := &vcursor{numRows: 1} 174 175 // The check doesn't actually happen. But we give correct values 176 // to avoid confusion. 177 got, err := lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6"), []byte("\x06\xe7\xea\"Βp\x8f")}) 178 require.NoError(t, err) 179 want := []bool{true, true} 180 if !reflect.DeepEqual(got, want) { 181 t.Errorf("lookuphash.Verify(match): %v, want %v", got, want) 182 } 183 184 vc.numRows = 0 185 got, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}) 186 require.NoError(t, err) 187 want = []bool{false} 188 if !reflect.DeepEqual(got, want) { 189 t.Errorf("lookuphash.Verify(mismatch): %v, want %v", got, want) 190 } 191 192 _, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, [][]byte{[]byte("bogus")}) 193 wantErr := "lookup.Verify.vunhash: invalid keyspace id: 626f677573" 194 if err == nil || err.Error() != wantErr { 195 t.Errorf("lookuphash.Verify(bogus) err: %v, want %s", err, wantErr) 196 } 197 198 // writeOnly true should always yield true. 199 lookuphash = createLookup(t, "lookup_hash", true) 200 vc.queries = nil 201 202 got, err = lookuphash.Verify(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, [][]byte{[]byte(""), []byte("")}) 203 require.NoError(t, err) 204 if vc.queries != nil { 205 t.Errorf("lookuphash.Verify(scatter), queries: %v, want nil", vc.queries) 206 } 207 wantBools := []bool{true, true} 208 if !reflect.DeepEqual(got, wantBools) { 209 t.Errorf("lookuphash.Verify(scatter): %v, want %v", got, wantBools) 210 } 211 } 212 213 func TestLookupHashCreate(t *testing.T) { 214 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 215 vc := &vcursor{} 216 217 err := lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) 218 require.NoError(t, err) 219 if got, want := len(vc.queries), 1; got != want { 220 t.Errorf("vc.queries length: %v, want %v", got, want) 221 } 222 223 err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) 224 want := "lookup.Create: input has null values: row: 0, col: 0" 225 if err == nil || err.Error() != want { 226 t.Errorf("lookuphash.Create(NULL) err: %v, want %s", err, want) 227 } 228 229 vc.queries = nil 230 lookuphash.(*LookupHash).lkp.IgnoreNulls = true 231 err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, [][]byte{[]byte("\x16k@\xb4J\xbaK\xd6")}, false /* ignoreMode */) 232 require.NoError(t, err) 233 if got, want := len(vc.queries), 0; got != want { 234 t.Errorf("vc.queries length: %v, want %v", got, want) 235 } 236 237 err = lookuphash.(Lookup).Create(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, [][]byte{[]byte("bogus")}, false /* ignoreMode */) 238 want = "lookup.Create.vunhash: invalid keyspace id: 626f677573" 239 if err == nil || err.Error() != want { 240 t.Errorf("lookuphash.Create(bogus) err: %v, want %s", err, want) 241 } 242 } 243 244 func TestLookupHashDelete(t *testing.T) { 245 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 246 vc := &vcursor{} 247 248 err := lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("\x16k@\xb4J\xbaK\xd6")) 249 require.NoError(t, err) 250 if got, want := len(vc.queries), 1; got != want { 251 t.Errorf("vc.queries length: %v, want %v", got, want) 252 } 253 254 vc.queries = nil 255 err = lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NULL}}, []byte("\x16k@\xb4J\xbaK\xd6")) 256 require.NoError(t, err) 257 if got, want := len(vc.queries), 1; got != want { 258 t.Errorf("vc.queries length: %v, want %v", got, want) 259 } 260 261 err = lookuphash.(Lookup).Delete(context.Background(), vc, [][]sqltypes.Value{{sqltypes.NewInt64(1)}}, []byte("bogus")) 262 want := "lookup.Delete.vunhash: invalid keyspace id: 626f677573" 263 if err == nil || err.Error() != want { 264 t.Errorf("lookuphash.Delete(bogus) err: %v, want %s", err, want) 265 } 266 } 267 268 func TestLookupHashUpdate(t *testing.T) { 269 lookuphash := createLookup(t, "lookup_hash", false /* writeOnly */) 270 vc := &vcursor{} 271 272 err := lookuphash.(Lookup).Update(context.Background(), vc, []sqltypes.Value{sqltypes.NewInt64(1)}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)}) 273 require.NoError(t, err) 274 if got, want := len(vc.queries), 2; got != want { 275 t.Errorf("vc.queries length: %v, want %v", got, want) 276 } 277 278 vc.queries = nil 279 err = lookuphash.(Lookup).Update(context.Background(), vc, []sqltypes.Value{sqltypes.NULL}, []byte("\x16k@\xb4J\xbaK\xd6"), []sqltypes.Value{sqltypes.NewInt64(2)}) 280 require.NoError(t, err) 281 if got, want := len(vc.queries), 2; got != want { 282 t.Errorf("vc.queries length: %v, want %v", got, want) 283 } 284 }