vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_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 vstreamer 18 19 import ( 20 "context" 21 "fmt" 22 "regexp" 23 "testing" 24 "time" 25 26 "vitess.io/vitess/go/vt/log" 27 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/mysql" 31 "vitess.io/vitess/go/sqltypes" 32 33 binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" 34 ) 35 36 func TestStreamRowsScan(t *testing.T) { 37 if testing.Short() { 38 t.Skip() 39 } 40 41 execStatements(t, []string{ 42 // Single PK 43 "create table t1(id int, val varbinary(128), primary key(id))", 44 "insert into t1 values (1, 'aaa'), (2, 'bbb')", 45 // Composite PK 46 "create table t2(id1 int, id2 int, val varbinary(128), primary key(id1, id2))", 47 "insert into t2 values (1, 2, 'aaa'), (1, 3, 'bbb')", 48 // No PK 49 "create table t3(id int, val varbinary(128))", 50 "insert into t3 values (1, 'aaa'), (2, 'bbb')", 51 // Three-column PK 52 "create table t4(id1 int, id2 int, id3 int, val varbinary(128), primary key(id1, id2, id3))", 53 "insert into t4 values (1, 2, 3, 'aaa'), (2, 3, 4, 'bbb')", 54 }) 55 56 defer execStatements(t, []string{ 57 "drop table t1", 58 "drop table t2", 59 "drop table t3", 60 "drop table t4", 61 }) 62 63 engine.se.Reload(context.Background()) 64 65 // t1: simulates rollup 66 wantStream := []string{ 67 `fields:{name:"1" type:INT64} pkfields:{name:"id" type:INT32}`, 68 `rows:{lengths:1 values:"1"} rows:{lengths:1 values:"1"} lastpk:{lengths:1 values:"2"}`, 69 } 70 wantQuery := "select id, val from t1 order by id" 71 checkStream(t, "select 1 from t1", nil, wantQuery, wantStream) 72 73 // t1: simulates rollup, with non-pk column 74 wantStream = []string{ 75 `fields:{name:"1" type:INT64} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 76 `rows:{lengths:1 lengths:3 values:"1aaa"} rows:{lengths:1 lengths:3 values:"1bbb"} lastpk:{lengths:1 values:"2"}`, 77 } 78 wantQuery = "select id, val from t1 order by id" 79 checkStream(t, "select 1, val from t1", nil, wantQuery, wantStream) 80 81 // t1: simulates rollup, with pk and non-pk column 82 wantStream = []string{ 83 `fields:{name:"1" type:INT64} fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 84 `rows:{lengths:1 lengths:1 lengths:3 values:"11aaa"} rows:{lengths:1 lengths:1 lengths:3 values:"12bbb"} lastpk:{lengths:1 values:"2"}`, 85 } 86 wantQuery = "select id, val from t1 order by id" 87 checkStream(t, "select 1, id, val from t1", nil, wantQuery, wantStream) 88 89 // t1: no pk in select list 90 wantStream = []string{ 91 `fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 92 `rows:{lengths:3 values:"aaa"} rows:{lengths:3 values:"bbb"} lastpk:{lengths:1 values:"2"}`, 93 } 94 wantQuery = "select id, val from t1 order by id" 95 checkStream(t, "select val from t1", nil, wantQuery, wantStream) 96 97 // t1: all rows 98 wantStream = []string{ 99 `fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 100 `rows:{lengths:1 lengths:3 values:"1aaa"} rows:{lengths:1 lengths:3 values:"2bbb"} lastpk:{lengths:1 values:"2"}`, 101 } 102 wantQuery = "select id, val from t1 order by id" 103 checkStream(t, "select * from t1", nil, wantQuery, wantStream) 104 105 // t1: lastpk=1 106 wantStream = []string{ 107 `fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 108 `rows:{lengths:1 lengths:3 values:"2bbb"} lastpk:{lengths:1 values:"2"}`, 109 } 110 wantQuery = "select id, val from t1 where (id > 1) order by id" 111 checkStream(t, "select * from t1", []sqltypes.Value{sqltypes.NewInt64(1)}, wantQuery, wantStream) 112 113 // t1: different column ordering 114 wantStream = []string{ 115 `fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63} pkfields:{name:"id" type:INT32}`, 116 `rows:{lengths:3 lengths:1 values:"aaa1"} rows:{lengths:3 lengths:1 values:"bbb2"} lastpk:{lengths:1 values:"2"}`, 117 } 118 wantQuery = "select id, val from t1 order by id" 119 checkStream(t, "select val, id from t1", nil, wantQuery, wantStream) 120 121 // t2: all rows 122 wantStream = []string{ 123 `fields:{name:"id1" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"id2" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id2" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t2" org_table:"t2" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32} pkfields:{name:"id2" type:INT32}`, 124 `rows:{lengths:1 lengths:1 lengths:3 values:"12aaa"} rows:{lengths:1 lengths:1 lengths:3 values:"13bbb"} lastpk:{lengths:1 lengths:1 values:"13"}`, 125 } 126 wantQuery = "select id1, id2, val from t2 order by id1, id2" 127 checkStream(t, "select * from t2", nil, wantQuery, wantStream) 128 129 // t2: lastpk=1,2 130 wantStream = []string{ 131 `fields:{name:"id1" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"id2" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id2" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t2" org_table:"t2" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32} pkfields:{name:"id2" type:INT32}`, 132 `rows:{lengths:1 lengths:1 lengths:3 values:"13bbb"} lastpk:{lengths:1 lengths:1 values:"13"}`, 133 } 134 wantQuery = "select id1, id2, val from t2 where (id1 = 1 and id2 > 2) or (id1 > 1) order by id1, id2" 135 checkStream(t, "select * from t2", []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2)}, wantQuery, wantStream) 136 137 // t3: all rows 138 wantStream = []string{ 139 `fields:{name:"id" type:INT32 table:"t3" org_table:"t3" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t3" org_table:"t3" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32} pkfields:{name:"val" type:VARBINARY}`, 140 `rows:{lengths:1 lengths:3 values:"1aaa"} rows:{lengths:1 lengths:3 values:"2bbb"} lastpk:{lengths:1 lengths:3 values:"2bbb"}`, 141 } 142 wantQuery = "select id, val from t3 order by id, val" 143 checkStream(t, "select * from t3", nil, wantQuery, wantStream) 144 145 // t3: lastpk: 1,'aaa' 146 wantStream = []string{ 147 `fields:{name:"id" type:INT32 table:"t3" org_table:"t3" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t3" org_table:"t3" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32} pkfields:{name:"val" type:VARBINARY}`, 148 `rows:{lengths:1 lengths:3 values:"2bbb"} lastpk:{lengths:1 lengths:3 values:"2bbb"}`, 149 } 150 wantQuery = "select id, val from t3 where (id = 1 and val > 'aaa') or (id > 1) order by id, val" 151 checkStream(t, "select * from t3", []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewVarBinary("aaa")}, wantQuery, wantStream) 152 153 // t4: all rows 154 wantStream = []string{ 155 `fields:{name:"id1" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"id2" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id2" column_length:11 charset:63} fields:{name:"id3" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id3" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t4" org_table:"t4" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32} pkfields:{name:"id2" type:INT32} pkfields:{name:"id3" type:INT32}`, 156 `rows:{lengths:1 lengths:1 lengths:1 lengths:3 values:"123aaa"} rows:{lengths:1 lengths:1 lengths:1 lengths:3 values:"234bbb"} lastpk:{lengths:1 lengths:1 lengths:1 values:"234"}`, 157 } 158 wantQuery = "select id1, id2, id3, val from t4 order by id1, id2, id3" 159 checkStream(t, "select * from t4", nil, wantQuery, wantStream) 160 161 // t4: lastpk: 1,2,3 162 wantStream = []string{ 163 `fields:{name:"id1" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"id2" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id2" column_length:11 charset:63} fields:{name:"id3" type:INT32 table:"t4" org_table:"t4" database:"vttest" org_name:"id3" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t4" org_table:"t4" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32} pkfields:{name:"id2" type:INT32} pkfields:{name:"id3" type:INT32}`, 164 `rows:{lengths:1 lengths:1 lengths:1 lengths:3 values:"234bbb"} lastpk:{lengths:1 lengths:1 lengths:1 values:"234"}`, 165 } 166 wantQuery = "select id1, id2, id3, val from t4 where (id1 = 1 and id2 = 2 and id3 > 3) or (id1 = 1 and id2 > 2) or (id1 > 1) order by id1, id2, id3" 167 checkStream(t, "select * from t4", []sqltypes.Value{sqltypes.NewInt64(1), sqltypes.NewInt64(2), sqltypes.NewInt64(3)}, wantQuery, wantStream) 168 169 // t1: test for unsupported integer literal 170 wantError := "only the integer literal 1 is supported" 171 expectStreamError(t, "select 2 from t1", wantError) 172 173 // t1: test for unsupported literal type 174 wantError = "only integer literals are supported" 175 expectStreamError(t, "select 'a' from t1", wantError) 176 } 177 178 func TestStreamRowsUnicode(t *testing.T) { 179 if testing.Short() { 180 t.Skip() 181 } 182 183 execStatements(t, []string{ 184 "create table t1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id))", 185 }) 186 187 defer execStatements(t, []string{ 188 "drop table t1", 189 }) 190 191 // Use an engine with latin1 charset. 192 savedEngine := engine 193 defer func() { 194 engine = savedEngine 195 }() 196 engine = customEngine(t, func(in mysql.ConnParams) mysql.ConnParams { 197 in.Charset = "latin1" 198 return in 199 }) 200 defer engine.Close() 201 engine.se.Reload(context.Background()) 202 // We need a latin1 connection. 203 conn, err := env.Mysqld.GetDbaConnection(context.Background()) 204 if err != nil { 205 t.Fatal(err) 206 } 207 defer conn.Close() 208 209 if _, err := conn.ExecuteFetch("set names latin1", 10000, false); err != nil { 210 t.Fatal(err) 211 } 212 // This will get "Mojibaked" into the utf8 column. 213 if _, err := conn.ExecuteFetch("insert into t1 values(1, '👍')", 10000, false); err != nil { 214 t.Fatal(err) 215 } 216 217 err = engine.StreamRows(context.Background(), "select * from t1", nil, func(rows *binlogdatapb.VStreamRowsResponse) error { 218 // Skip fields. 219 if len(rows.Rows) == 0 { 220 return nil 221 } 222 got := fmt.Sprintf("%q", rows.Rows[0].Values) 223 // We should expect a "Mojibaked" version of the string. 224 want := `"1ðŸ‘\u008d"` 225 if got != want { 226 t.Errorf("rows.Rows[0].Values: %s, want %s", got, want) 227 } 228 return nil 229 }) 230 require.NoError(t, err) 231 } 232 233 func TestStreamRowsKeyRange(t *testing.T) { 234 if testing.Short() { 235 t.Skip() 236 } 237 engine.se.Reload(context.Background()) 238 239 if err := env.SetVSchema(shardedVSchema); err != nil { 240 t.Fatal(err) 241 } 242 defer env.SetVSchema("{}") 243 244 execStatements(t, []string{ 245 "create table t1(id1 int, val varbinary(128), primary key(id1))", 246 "insert into t1 values (1, 'aaa'), (6, 'bbb')", 247 }) 248 249 defer execStatements(t, []string{ 250 "drop table t1", 251 }) 252 engine.se.Reload(context.Background()) 253 254 time.Sleep(1 * time.Second) 255 256 // Only the first row should be returned, but lastpk should be 6. 257 wantStream := []string{ 258 `fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32}`, 259 `rows:{lengths:1 lengths:3 values:"1aaa"} lastpk:{lengths:1 values:"6"}`, 260 } 261 wantQuery := "select id1, val from t1 order by id1" 262 checkStream(t, "select * from t1 where in_keyrange('-80')", nil, wantQuery, wantStream) 263 } 264 265 func TestStreamRowsFilterInt(t *testing.T) { 266 if testing.Short() { 267 t.Skip() 268 } 269 engine.rowStreamerNumPackets.Reset() 270 engine.rowStreamerNumRows.Reset() 271 272 if err := env.SetVSchema(shardedVSchema); err != nil { 273 t.Fatal(err) 274 } 275 defer env.SetVSchema("{}") 276 277 execStatements(t, []string{ 278 "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", 279 "insert into t1 values (1, 100, 'aaa'), (2, 200, 'bbb'), (3, 200, 'ccc'), (4, 100, 'ddd'), (5, 200, 'eee')", 280 }) 281 282 defer execStatements(t, []string{ 283 "drop table t1", 284 }) 285 engine.se.Reload(context.Background()) 286 287 time.Sleep(1 * time.Second) 288 289 wantStream := []string{ 290 `fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32}`, 291 `rows:{lengths:1 lengths:3 values:"1aaa"} rows:{lengths:1 lengths:3 values:"4ddd"} lastpk:{lengths:1 values:"5"}`, 292 } 293 wantQuery := "select id1, id2, val from t1 order by id1" 294 checkStream(t, "select id1, val from t1 where id2 = 100", nil, wantQuery, wantStream) 295 require.Equal(t, int64(0), engine.rowStreamerNumPackets.Get()) 296 require.Equal(t, int64(2), engine.rowStreamerNumRows.Get()) 297 require.Less(t, int64(0), engine.vstreamerPacketSize.Get()) 298 } 299 300 func TestStreamRowsFilterVarBinary(t *testing.T) { 301 if testing.Short() { 302 t.Skip() 303 } 304 305 if err := env.SetVSchema(shardedVSchema); err != nil { 306 t.Fatal(err) 307 } 308 defer env.SetVSchema("{}") 309 310 execStatements(t, []string{ 311 "create table t1(id1 int, val varbinary(128), primary key(id1))", 312 "insert into t1 values (1,'kepler'), (2, 'newton'), (3, 'newton'), (4, 'kepler'), (5, 'newton'), (6, 'kepler')", 313 }) 314 315 defer execStatements(t, []string{ 316 "drop table t1", 317 }) 318 engine.se.Reload(context.Background()) 319 320 time.Sleep(1 * time.Second) 321 322 wantStream := []string{ 323 `fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id1" type:INT32}`, 324 `rows:{lengths:1 lengths:6 values:"2newton"} rows:{lengths:1 lengths:6 values:"3newton"} rows:{lengths:1 lengths:6 values:"5newton"} lastpk:{lengths:1 values:"6"}`, 325 } 326 wantQuery := "select id1, val from t1 order by id1" 327 checkStream(t, "select id1, val from t1 where val = 'newton'", nil, wantQuery, wantStream) 328 } 329 330 func TestStreamRowsMultiPacket(t *testing.T) { 331 if testing.Short() { 332 t.Skip() 333 } 334 335 reset := AdjustPacketSize(10) 336 defer reset() 337 338 execStatements(t, []string{ 339 "create table t1(id int, val varbinary(128), primary key(id))", 340 "insert into t1 values (1, '234'), (2, '6789'), (3, '1'), (4, '2345678901'), (5, '2')", 341 }) 342 343 defer execStatements(t, []string{ 344 "drop table t1", 345 }) 346 engine.se.Reload(context.Background()) 347 348 wantStream := []string{ 349 `fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63} pkfields:{name:"id" type:INT32}`, 350 `rows:{lengths:1 lengths:3 values:"1234"} rows:{lengths:1 lengths:4 values:"26789"} rows:{lengths:1 lengths:1 values:"31"} lastpk:{lengths:1 values:"3"}`, 351 `rows:{lengths:1 lengths:10 values:"42345678901"} lastpk:{lengths:1 values:"4"}`, 352 `rows:{lengths:1 lengths:1 values:"52"} lastpk:{lengths:1 values:"5"}`, 353 } 354 wantQuery := "select id, val from t1 order by id" 355 checkStream(t, "select * from t1", nil, wantQuery, wantStream) 356 } 357 358 func TestStreamRowsCancel(t *testing.T) { 359 if testing.Short() { 360 t.Skip() 361 } 362 363 reset := AdjustPacketSize(10) 364 defer reset() 365 366 execStatements(t, []string{ 367 "create table t1(id int, val varbinary(128), primary key(id))", 368 "insert into t1 values (1, '234567890'), (2, '234')", 369 }) 370 371 defer execStatements(t, []string{ 372 "drop table t1", 373 }) 374 engine.se.Reload(context.Background()) 375 376 ctx, cancel := context.WithCancel(context.Background()) 377 defer cancel() 378 379 err := engine.StreamRows(ctx, "select * from t1", nil, func(rows *binlogdatapb.VStreamRowsResponse) error { 380 cancel() 381 return nil 382 }) 383 if got, want := err.Error(), "stream ended: context canceled"; got != want { 384 t.Errorf("err: %v, want %s", err, want) 385 } 386 } 387 388 func checkStream(t *testing.T, query string, lastpk []sqltypes.Value, wantQuery string, wantStream []string) { 389 t.Helper() 390 391 i := 0 392 ch := make(chan error) 393 // We don't want to report errors inside callback functions because 394 // line numbers come out wrong. 395 go func() { 396 first := true 397 defer close(ch) 398 err := engine.StreamRows(context.Background(), query, lastpk, func(rows *binlogdatapb.VStreamRowsResponse) error { 399 if first { 400 if rows.Gtid == "" { 401 ch <- fmt.Errorf("stream gtid is empty") 402 } 403 if got := engine.rowStreamers[engine.streamIdx-1].sendQuery; got != wantQuery { 404 log.Infof("Got: %v", got) 405 ch <- fmt.Errorf("query sent:\n%v, want\n%v", got, wantQuery) 406 } 407 } 408 first = false 409 rows.Gtid = "" 410 if i >= len(wantStream) { 411 ch <- fmt.Errorf("unexpected stream rows: %v", rows) 412 return nil 413 } 414 srows := fmt.Sprintf("%v", rows) 415 re, _ := regexp.Compile(` flags:[\d]+`) 416 srows = re.ReplaceAllString(srows, "") 417 418 if srows != wantStream[i] { 419 ch <- fmt.Errorf("stream %d:\n%s, want\n%s", i, srows, wantStream[i]) 420 } 421 i++ 422 return nil 423 }) 424 if err != nil { 425 ch <- err 426 } 427 }() 428 for err := range ch { 429 t.Error(err) 430 } 431 } 432 433 func expectStreamError(t *testing.T, query string, want string) { 434 t.Helper() 435 ch := make(chan error) 436 go func() { 437 defer close(ch) 438 err := engine.StreamRows(context.Background(), query, nil, func(rows *binlogdatapb.VStreamRowsResponse) error { 439 return nil 440 }) 441 require.EqualError(t, err, want, "Got incorrect error") 442 }() 443 }