vitess.io/vitess@v0.16.2/go/vt/vtctl/workflow/stream_migrator_test.go (about) 1 /* 2 Copyright 2021 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 workflow 18 19 import ( 20 "context" 21 "encoding/json" 22 "testing" 23 24 "github.com/stretchr/testify/assert" 25 "github.com/stretchr/testify/require" 26 27 "vitess.io/vitess/go/vt/proto/vschema" 28 "vitess.io/vitess/go/vt/vtgate/vindexes" 29 "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" 30 31 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 32 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 33 ) 34 35 func TestTemplatize(t *testing.T) { 36 tests := []struct { 37 in []*VReplicationStream 38 out string 39 err string 40 }{{ 41 // First test contains all fields. 42 in: []*VReplicationStream{{ 43 ID: 1, 44 Workflow: "test", 45 BinlogSource: &binlogdatapb.BinlogSource{ 46 Keyspace: "ks", 47 Shard: "80-", 48 Filter: &binlogdatapb.Filter{ 49 Rules: []*binlogdatapb.Rule{{ 50 Match: "t1", 51 Filter: "select * from t1 where in_keyrange('-80')", 52 }}, 53 }, 54 }, 55 }}, 56 out: `[{"ID":1,"Workflow":"test","BinlogSource":{"keyspace":"ks","shard":"80-","filter":{"rules":[{"match":"t1","filter":"select * from t1 where in_keyrange('{{.}}')"}]}}}]`, 57 }, { 58 // Reference table. 59 in: []*VReplicationStream{{ 60 BinlogSource: &binlogdatapb.BinlogSource{ 61 Filter: &binlogdatapb.Filter{ 62 Rules: []*binlogdatapb.Rule{{ 63 Match: "ref", 64 Filter: "", 65 }}, 66 }, 67 }, 68 }}, 69 out: "", 70 }, { 71 // Sharded table. 72 in: []*VReplicationStream{{ 73 BinlogSource: &binlogdatapb.BinlogSource{ 74 Filter: &binlogdatapb.Filter{ 75 Rules: []*binlogdatapb.Rule{{ 76 Match: "t1", 77 Filter: "-80", 78 }}, 79 }, 80 }, 81 }}, 82 out: `[{"ID":0,"Workflow":"","BinlogSource":{"filter":{"rules":[{"match":"t1","filter":"{{.}}"}]}}}]`, 83 }, { 84 // table not found 85 in: []*VReplicationStream{{ 86 BinlogSource: &binlogdatapb.BinlogSource{ 87 Filter: &binlogdatapb.Filter{ 88 Rules: []*binlogdatapb.Rule{{ 89 Match: "t3", 90 }}, 91 }, 92 }, 93 }}, 94 err: `table t3 not found in vschema`, 95 }, { 96 // sharded table with no filter 97 in: []*VReplicationStream{{ 98 BinlogSource: &binlogdatapb.BinlogSource{ 99 Filter: &binlogdatapb.Filter{ 100 Rules: []*binlogdatapb.Rule{{ 101 Match: "t1", 102 }}, 103 }, 104 }, 105 }}, 106 err: `rule match:"t1" does not have a select expression in vreplication`, 107 }, { 108 // Excluded table. 109 in: []*VReplicationStream{{ 110 BinlogSource: &binlogdatapb.BinlogSource{ 111 Filter: &binlogdatapb.Filter{ 112 Rules: []*binlogdatapb.Rule{{ 113 Match: "t1", 114 Filter: vreplication.ExcludeStr, 115 }}, 116 }, 117 }, 118 }}, 119 err: `unexpected rule in vreplication: match:"t1" filter:"exclude" `, 120 }, { 121 // Sharded table and ref table 122 in: []*VReplicationStream{{ 123 BinlogSource: &binlogdatapb.BinlogSource{ 124 Filter: &binlogdatapb.Filter{ 125 Rules: []*binlogdatapb.Rule{{ 126 Match: "t1", 127 Filter: "-80", 128 }, { 129 Match: "ref", 130 Filter: "", 131 }}, 132 }, 133 }, 134 }}, 135 err: `cannot migrate streams with a mix of reference and sharded tables: filter:<rules:<match:"t1" filter:"{{.}}" > rules:<match:"ref" > > `, 136 }, { 137 // Ref table and sharded table (different code path) 138 in: []*VReplicationStream{{ 139 BinlogSource: &binlogdatapb.BinlogSource{ 140 Filter: &binlogdatapb.Filter{ 141 Rules: []*binlogdatapb.Rule{{ 142 Match: "ref", 143 Filter: "", 144 }, { 145 Match: "t2", 146 Filter: "-80", 147 }}, 148 }, 149 }, 150 }}, 151 err: `cannot migrate streams with a mix of reference and sharded tables: filter:<rules:<match:"ref" > rules:<match:"t2" filter:"{{.}}" > > `, 152 }, { 153 // Ref table with select expression 154 in: []*VReplicationStream{{ 155 BinlogSource: &binlogdatapb.BinlogSource{ 156 Filter: &binlogdatapb.Filter{ 157 Rules: []*binlogdatapb.Rule{{ 158 Match: "ref", 159 Filter: "select * from t1", 160 }}, 161 }, 162 }, 163 }}, 164 out: "", 165 }, { 166 // Select expresstion with no keyrange value 167 in: []*VReplicationStream{{ 168 BinlogSource: &binlogdatapb.BinlogSource{ 169 Filter: &binlogdatapb.Filter{ 170 Rules: []*binlogdatapb.Rule{{ 171 Match: "t1", 172 Filter: "select * from t1", 173 }}, 174 }, 175 }, 176 }}, 177 out: `[{"ID":0,"Workflow":"","BinlogSource":{"filter":{"rules":[{"match":"t1","filter":"select * from t1 where in_keyrange(c1, 'hash', '{{.}}')"}]}}}]`, 178 }, { 179 // Select expresstion with one keyrange value 180 in: []*VReplicationStream{{ 181 BinlogSource: &binlogdatapb.BinlogSource{ 182 Filter: &binlogdatapb.Filter{ 183 Rules: []*binlogdatapb.Rule{{ 184 Match: "t1", 185 Filter: "select * from t1 where in_keyrange('-80')", 186 }}, 187 }, 188 }, 189 }}, 190 out: `[{"ID":0,"Workflow":"","BinlogSource":{"filter":{"rules":[{"match":"t1","filter":"select * from t1 where in_keyrange('{{.}}')"}]}}}]`, 191 }, { 192 // Select expresstion with three keyrange values 193 in: []*VReplicationStream{{ 194 BinlogSource: &binlogdatapb.BinlogSource{ 195 Filter: &binlogdatapb.Filter{ 196 Rules: []*binlogdatapb.Rule{{ 197 Match: "t1", 198 Filter: "select * from t1 where in_keyrange(col, vdx, '-80')", 199 }}, 200 }, 201 }, 202 }}, 203 out: `[{"ID":0,"Workflow":"","BinlogSource":{"filter":{"rules":[{"match":"t1","filter":"select * from t1 where in_keyrange(col, vdx, '{{.}}')"}]}}}]`, 204 }, { 205 // syntax error 206 in: []*VReplicationStream{{ 207 BinlogSource: &binlogdatapb.BinlogSource{ 208 Filter: &binlogdatapb.Filter{ 209 Rules: []*binlogdatapb.Rule{{ 210 Match: "t1", 211 Filter: "bad syntax", 212 }}, 213 }, 214 }, 215 }}, 216 err: "syntax error at position 4 near 'bad'", 217 }, { 218 // invalid statement 219 in: []*VReplicationStream{{ 220 BinlogSource: &binlogdatapb.BinlogSource{ 221 Filter: &binlogdatapb.Filter{ 222 Rules: []*binlogdatapb.Rule{{ 223 Match: "t1", 224 Filter: "update t set a=1", 225 }}, 226 }, 227 }, 228 }}, 229 err: "unexpected query: update t set a=1", 230 }, { 231 // invalid in_keyrange 232 in: []*VReplicationStream{{ 233 BinlogSource: &binlogdatapb.BinlogSource{ 234 Filter: &binlogdatapb.Filter{ 235 Rules: []*binlogdatapb.Rule{{ 236 Match: "t1", 237 Filter: "select * from t1 where in_keyrange(col, vdx, '-80', extra)", 238 }}, 239 }, 240 }, 241 }}, 242 err: "unexpected in_keyrange parameters: in_keyrange(col, vdx, '-80', extra)", 243 }, { 244 // * in_keyrange 245 in: []*VReplicationStream{{ 246 BinlogSource: &binlogdatapb.BinlogSource{ 247 Filter: &binlogdatapb.Filter{ 248 Rules: []*binlogdatapb.Rule{{ 249 Match: "t1", 250 Filter: "select * from t1 where in_keyrange(*)", 251 }}, 252 }, 253 }, 254 }}, 255 err: "unexpected in_keyrange parameters: in_keyrange(*)", 256 }, { 257 // non-string in_keyrange 258 in: []*VReplicationStream{{ 259 BinlogSource: &binlogdatapb.BinlogSource{ 260 Filter: &binlogdatapb.Filter{ 261 Rules: []*binlogdatapb.Rule{{ 262 Match: "t1", 263 Filter: "select * from t1 where in_keyrange(aa)", 264 }}, 265 }, 266 }, 267 }}, 268 err: "unexpected in_keyrange parameters: in_keyrange(aa)", 269 }, { 270 // '{{' in query 271 in: []*VReplicationStream{{ 272 BinlogSource: &binlogdatapb.BinlogSource{ 273 Filter: &binlogdatapb.Filter{ 274 Rules: []*binlogdatapb.Rule{{ 275 Match: "t1", 276 Filter: "select '{{' from t1 where in_keyrange('-80')", 277 }}, 278 }, 279 }, 280 }}, 281 err: "cannot migrate queries that contain '{{' in their string: select '{{' from t1 where in_keyrange('-80')", 282 }} 283 vs := &vschemapb.Keyspace{ 284 Sharded: true, 285 Vindexes: map[string]*vschema.Vindex{ 286 "thash": { 287 Type: "hash", 288 }, 289 }, 290 Tables: map[string]*vschema.Table{ 291 "t1": { 292 ColumnVindexes: []*vschema.ColumnVindex{{ 293 Columns: []string{"c1"}, 294 Name: "thash", 295 }}, 296 }, 297 "t2": { 298 ColumnVindexes: []*vschema.ColumnVindex{{ 299 Columns: []string{"c1"}, 300 Name: "thash", 301 }}, 302 }, 303 "ref": { 304 Type: vindexes.TypeReference, 305 }, 306 }, 307 } 308 ksschema, err := vindexes.BuildKeyspaceSchema(vs, "ks") 309 require.NoError(t, err, "could not create test keyspace %+v", vs) 310 311 ts := &testTrafficSwitcher{ 312 sourceKeyspaceSchema: ksschema, 313 } 314 for _, tt := range tests { 315 sm := &StreamMigrator{ts: ts} 316 out, err := sm.templatize(context.Background(), tt.in) 317 if tt.err != "" { 318 assert.Error(t, err, "templatize(%v) expected to get err=%s, got %+v", stringifyVRS(tt.in), tt.err, err) 319 } 320 321 got := stringifyVRS(out) 322 assert.Equal(t, tt.out, got, "templatize(%v) mismatch", stringifyVRS(tt.in)) 323 } 324 } 325 326 func stringifyVRS(streams []*VReplicationStream) string { 327 if len(streams) == 0 { 328 return "" 329 } 330 331 type testVRS struct { 332 ID uint32 333 Workflow string 334 BinlogSource *binlogdatapb.BinlogSource 335 } 336 337 converted := make([]*testVRS, len(streams)) 338 for i, stream := range streams { 339 converted[i] = &testVRS{ 340 ID: stream.ID, 341 Workflow: stream.Workflow, 342 BinlogSource: stream.BinlogSource, 343 } 344 } 345 346 b, _ := json.Marshal(converted) 347 return string(b) 348 }