vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_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  	"io"
    23  	"strconv"
    24  	"strings"
    25  	"sync"
    26  	"testing"
    27  	"time"
    28  
    29  	"google.golang.org/protobuf/proto"
    30  
    31  	"vitess.io/vitess/go/vt/log"
    32  	"vitess.io/vitess/go/vt/sqlparser"
    33  
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  
    37  	"vitess.io/vitess/go/mysql"
    38  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    39  )
    40  
    41  type testcase struct {
    42  	input  any
    43  	output [][]string
    44  }
    45  
    46  func checkIfOptionIsSupported(t *testing.T, variable string) bool {
    47  	qr, err := env.Mysqld.FetchSuperQuery(context.Background(), fmt.Sprintf("show variables like '%s'", variable))
    48  	require.NoError(t, err)
    49  	require.NotNil(t, qr)
    50  	if qr.Rows != nil && len(qr.Rows) == 1 {
    51  		return true
    52  	}
    53  	return false
    54  }
    55  
    56  type TestColumn struct {
    57  	name, dataType, colType string
    58  	len, charset            int64
    59  }
    60  
    61  type TestFieldEvent struct {
    62  	table, db string
    63  	cols      []*TestColumn
    64  }
    65  
    66  func (tfe *TestFieldEvent) String() string {
    67  	s := fmt.Sprintf("type:FIELD field_event:{table_name:\"%s\"", tfe.table)
    68  	fld := ""
    69  	for _, col := range tfe.cols {
    70  		fld += fmt.Sprintf(" fields:{name:\"%s\" type:%s table:\"%s\" org_table:\"%s\" database:\"%s\" org_name:\"%s\" column_length:%d charset:%d",
    71  			col.name, col.dataType, tfe.table, tfe.table, tfe.db, col.name, col.len, col.charset)
    72  		if col.colType != "" {
    73  			fld += fmt.Sprintf(" column_type:\"%s\"", col.colType)
    74  		}
    75  		fld += "}"
    76  	}
    77  	s += fld
    78  	s += "}"
    79  	return s
    80  }
    81  
    82  func TestSetAndEnum(t *testing.T) {
    83  	execStatements(t, []string{
    84  		"create table t1(id int, val binary(4), color set('red','green','blue'), size enum('S','M','L'), primary key(id))",
    85  	})
    86  	defer execStatements(t, []string{
    87  		"drop table t1",
    88  	})
    89  	engine.se.Reload(context.Background())
    90  	queries := []string{
    91  		"begin",
    92  		"insert into t1 values (1, 'aaa', 'red,blue', 'S')",
    93  		"insert into t1 values (2, 'bbb', 'green', 'M')",
    94  		"insert into t1 values (3, 'ccc', 'red,blue,green', 'L')",
    95  		"commit",
    96  	}
    97  
    98  	fe := &TestFieldEvent{
    99  		table: "t1",
   100  		db:    "vttest",
   101  		cols: []*TestColumn{
   102  			{name: "id", dataType: "INT32", colType: "int(11)", len: 11, charset: 63},
   103  			{name: "val", dataType: "BINARY", colType: "binary(4)", len: 4, charset: 63},
   104  			{name: "color", dataType: "SET", colType: "set('red','green','blue')", len: 56, charset: 45},
   105  			{name: "size", dataType: "ENUM", colType: "enum('S','M','L')", len: 4, charset: 45},
   106  		},
   107  	}
   108  
   109  	testcases := []testcase{{
   110  		input: queries,
   111  		output: [][]string{{
   112  			`begin`,
   113  			fe.String(),
   114  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"1aaa\x0051"}}}`,
   115  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"2bbb\x0022"}}}`,
   116  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 lengths:1 lengths:1 values:"3ccc\x0073"}}}`,
   117  			`gtid`,
   118  			`commit`,
   119  		}},
   120  	}}
   121  	runCases(t, nil, testcases, "current", nil)
   122  }
   123  
   124  func TestCellValuePadding(t *testing.T) {
   125  
   126  	execStatements(t, []string{
   127  		"create table t1(id int, val binary(4), primary key(val))",
   128  		"create table t2(id int, val char(4), primary key(val))",
   129  		"create table t3(id int, val char(4) collate utf8mb4_bin, primary key(val))",
   130  	})
   131  	defer execStatements(t, []string{
   132  		"drop table t1",
   133  		"drop table t2",
   134  		"drop table t3",
   135  	})
   136  	engine.se.Reload(context.Background())
   137  	queries := []string{
   138  		"begin",
   139  		"insert into t1 values (1, 'aaa\000')",
   140  		"insert into t1 values (2, 'bbb\000')",
   141  		"update t1 set id = 11 where val = 'aaa\000'",
   142  		"insert into t2 values (1, 'aaa')",
   143  		"insert into t2 values (2, 'bbb')",
   144  		"update t2 set id = 11 where val = 'aaa'",
   145  		"insert into t3 values (1, 'aaa')",
   146  		"insert into t3 values (2, 'bb')",
   147  		"update t3 set id = 11 where val = 'aaa'",
   148  		"commit",
   149  	}
   150  
   151  	testcases := []testcase{{
   152  		input: queries,
   153  		output: [][]string{{
   154  			`begin`,
   155  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:BINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:4 charset:63 column_type:"binary(4)"}}`,
   156  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"1aaa\x00"}}}`,
   157  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:4 values:"2bbb\x00"}}}`,
   158  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:4 values:"1aaa\x00"} after:{lengths:2 lengths:4 values:"11aaa\x00"}}}`,
   159  			`type:FIELD field_event:{table_name:"t2" fields:{name:"id" type:INT32 table:"t2" org_table:"t2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:CHAR table:"t2" org_table:"t2" database:"vttest" org_name:"val" column_length:16 charset:45 column_type:"char(4)"}}`,
   160  			`type:ROW row_event:{table_name:"t2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   161  			`type:ROW row_event:{table_name:"t2" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`,
   162  			`type:ROW row_event:{table_name:"t2" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:2 lengths:3 values:"11aaa"}}}`,
   163  			`type:FIELD field_event:{table_name:"t3" fields:{name:"id" type:INT32 table:"t3" org_table:"t3" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:BINARY table:"t3" org_table:"t3" database:"vttest" org_name:"val" column_length:16 charset:45 column_type:"char(4)"}}`,
   164  			`type:ROW row_event:{table_name:"t3" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   165  			`type:ROW row_event:{table_name:"t3" row_changes:{after:{lengths:1 lengths:2 values:"2bb"}}}`,
   166  			`type:ROW row_event:{table_name:"t3" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:2 lengths:3 values:"11aaa"}}}`,
   167  			`gtid`,
   168  			`commit`,
   169  		}},
   170  	}}
   171  	runCases(t, nil, testcases, "current", nil)
   172  }
   173  
   174  func TestSetStatement(t *testing.T) {
   175  
   176  	if testing.Short() {
   177  		t.Skip()
   178  	}
   179  	if !checkIfOptionIsSupported(t, "log_builtin_as_identified_by_password") {
   180  		// the combination of setting this option and support for "set password" only works on a few flavors
   181  		log.Info("Cannot test SetStatement on this flavor")
   182  		return
   183  	}
   184  	engine.se.Reload(context.Background())
   185  
   186  	execStatements(t, []string{
   187  		"create table t1(id int, val varbinary(128), primary key(id))",
   188  	})
   189  	defer execStatements(t, []string{
   190  		"drop table t1",
   191  	})
   192  	engine.se.Reload(context.Background())
   193  	queries := []string{
   194  		"begin",
   195  		"insert into t1 values (1, 'aaa')",
   196  		"commit",
   197  		"set global log_builtin_as_identified_by_password=1",
   198  		"SET PASSWORD FOR 'vt_appdebug'@'localhost'='*AA17DA66C7C714557F5485E84BCAFF2C209F2F53'", //select password('vtappdebug_password');
   199  	}
   200  	testcases := []testcase{{
   201  		input: queries,
   202  		output: [][]string{{
   203  			`begin`,
   204  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   205  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   206  			`gtid`,
   207  			`commit`,
   208  		}, {
   209  			`gtid`,
   210  			`other`,
   211  		}},
   212  	}}
   213  	runCases(t, nil, testcases, "current", nil)
   214  }
   215  
   216  func TestStmtComment(t *testing.T) {
   217  
   218  	if testing.Short() {
   219  		t.Skip()
   220  	}
   221  
   222  	execStatements(t, []string{
   223  		"create table t1(id int, val varbinary(128), primary key(id))",
   224  	})
   225  	defer execStatements(t, []string{
   226  		"drop table t1",
   227  	})
   228  	engine.se.Reload(context.Background())
   229  	queries := []string{
   230  		"begin",
   231  		"insert into t1 values (1, 'aaa')",
   232  		"commit",
   233  		"/*!40000 ALTER TABLE `t1` DISABLE KEYS */",
   234  	}
   235  	testcases := []testcase{{
   236  		input: queries,
   237  		output: [][]string{{
   238  			`begin`,
   239  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   240  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   241  			`gtid`,
   242  			`commit`,
   243  		}, {
   244  			`gtid`,
   245  			`other`,
   246  		}},
   247  	}}
   248  	runCases(t, nil, testcases, "current", nil)
   249  }
   250  
   251  func TestVersion(t *testing.T) {
   252  	if testing.Short() {
   253  		t.Skip()
   254  	}
   255  
   256  	oldEngine := engine
   257  	defer func() {
   258  		engine = oldEngine
   259  	}()
   260  
   261  	err := env.SchemaEngine.EnableHistorian(true)
   262  	require.NoError(t, err)
   263  	defer env.SchemaEngine.EnableHistorian(false)
   264  
   265  	engine = NewEngine(engine.env, env.SrvTopo, env.SchemaEngine, nil, env.Cells[0])
   266  	engine.InitDBConfig(env.KeyspaceName, env.ShardName)
   267  	engine.Open()
   268  	defer engine.Close()
   269  
   270  	execStatements(t, []string{
   271  		"create database if not exists _vt",
   272  		"create table if not exists _vt.schema_version(id int, pos varbinary(10000), time_updated bigint(20), ddl varchar(10000), schemax blob, primary key(id))",
   273  	})
   274  	defer execStatements(t, []string{
   275  		"drop table _vt.schema_version",
   276  	})
   277  	dbSchema := &binlogdatapb.MinimalSchema{
   278  		Tables: []*binlogdatapb.MinimalTable{{
   279  			Name: "t1",
   280  		}},
   281  	}
   282  	blob, _ := proto.Marshal(dbSchema)
   283  	engine.se.Reload(context.Background())
   284  	gtid := "MariaDB/0-41983-20"
   285  	testcases := []testcase{{
   286  		input: []string{
   287  			fmt.Sprintf("insert into _vt.schema_version values(1, '%s', 123, 'create table t1', %v)", gtid, encodeString(string(blob))),
   288  		},
   289  		// External table events don't get sent.
   290  		output: [][]string{{
   291  			`begin`,
   292  			`type:VERSION`}, {
   293  			`gtid`,
   294  			`commit`}},
   295  	}}
   296  	runCases(t, nil, testcases, "", nil)
   297  	mt, err := env.SchemaEngine.GetTableForPos(sqlparser.NewIdentifierCS("t1"), gtid)
   298  	require.NoError(t, err)
   299  	assert.True(t, proto.Equal(mt, dbSchema.Tables[0]))
   300  }
   301  
   302  func insertLotsOfData(t *testing.T, numRows int) {
   303  	query1 := "insert into t1 (id11, id12) values"
   304  	s := ""
   305  	for i := 1; i <= numRows; i++ {
   306  		if s != "" {
   307  			s += ","
   308  		}
   309  		s += fmt.Sprintf("(%d,%d)", i, i*10)
   310  	}
   311  	query1 += s
   312  	query2 := "insert into t2 (id21, id22) values"
   313  	s = ""
   314  	for i := 1; i <= numRows; i++ {
   315  		if s != "" {
   316  			s += ","
   317  		}
   318  		s += fmt.Sprintf("(%d,%d)", i, i*20)
   319  	}
   320  	query2 += s
   321  	execStatements(t, []string{
   322  		query1,
   323  		query2,
   324  	})
   325  }
   326  
   327  func TestMissingTables(t *testing.T) {
   328  	if testing.Short() {
   329  		t.Skip()
   330  	}
   331  	engine.se.Reload(context.Background())
   332  	execStatements(t, []string{
   333  		"create table t1(id11 int, id12 int, primary key(id11))",
   334  		"create table shortlived(id31 int, id32 int, primary key(id31))",
   335  	})
   336  	defer execStatements(t, []string{
   337  		"drop table t1",
   338  		"drop table _shortlived",
   339  	})
   340  	startPos := primaryPosition(t)
   341  	execStatements(t, []string{
   342  		"insert into shortlived values (1,1), (2,2)",
   343  		"alter table shortlived rename to _shortlived",
   344  	})
   345  	engine.se.Reload(context.Background())
   346  	filter := &binlogdatapb.Filter{
   347  		Rules: []*binlogdatapb.Rule{{
   348  			Match:  "t1",
   349  			Filter: "select * from t1",
   350  		}},
   351  	}
   352  	testcases := []testcase{
   353  		{
   354  			input:  []string{},
   355  			output: [][]string{},
   356  		},
   357  
   358  		{
   359  			input: []string{
   360  				"insert into t1 values (101, 1010)",
   361  			},
   362  			output: [][]string{
   363  				{
   364  					"begin",
   365  					"gtid",
   366  					"commit",
   367  				},
   368  				{
   369  					"gtid",
   370  					"type:OTHER",
   371  				},
   372  				{
   373  					"begin",
   374  					"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   375  					"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:3 lengths:4 values:\"1011010\"}}}",
   376  					"gtid",
   377  					"commit",
   378  				},
   379  			},
   380  		},
   381  	}
   382  	runCases(t, filter, testcases, startPos, nil)
   383  }
   384  
   385  func TestVStreamCopySimpleFlow(t *testing.T) {
   386  	if testing.Short() {
   387  		t.Skip()
   388  	}
   389  	execStatements(t, []string{
   390  		"create table t1(id11 int, id12 int, primary key(id11))",
   391  		"create table t2(id21 int, id22 int, primary key(id21))",
   392  	})
   393  	log.Infof("Pos before bulk insert: %s", primaryPosition(t))
   394  	insertLotsOfData(t, 10)
   395  	log.Infof("Pos after bulk insert: %s", primaryPosition(t))
   396  	defer execStatements(t, []string{
   397  		"drop table t1",
   398  		"drop table t2",
   399  	})
   400  	engine.se.Reload(context.Background())
   401  	ctx := context.Background()
   402  	qr, err := env.Mysqld.FetchSuperQuery(ctx, "SELECT count(*) as cnt from t1, t2 where t1.id11 = t2.id21")
   403  	if err != nil {
   404  		t.Fatal("Query failed")
   405  	}
   406  	require.Equal(t, "[[INT64(10)]]", fmt.Sprintf("%v", qr.Rows))
   407  
   408  	filter := &binlogdatapb.Filter{
   409  		Rules: []*binlogdatapb.Rule{{
   410  			Match:  "t1",
   411  			Filter: "select * from t1",
   412  		}, {
   413  			Match:  "t2",
   414  			Filter: "select * from t2",
   415  		}},
   416  	}
   417  
   418  	var tablePKs []*binlogdatapb.TableLastPK
   419  	tablePKs = append(tablePKs, getTablePK("t1", 1))
   420  	tablePKs = append(tablePKs, getTablePK("t2", 2))
   421  
   422  	t1FieldEvent := []string{"begin", "type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63}}"}
   423  	t2FieldEvent := []string{"begin", "type:FIELD field_event:{table_name:\"t2\" fields:{name:\"id21\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id21\" column_length:11 charset:63} fields:{name:\"id22\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id22\" column_length:11 charset:63}}"}
   424  	t1Events := []string{}
   425  	t2Events := []string{}
   426  	for i := 1; i <= 10; i++ {
   427  		t1Events = append(t1Events,
   428  			fmt.Sprintf("type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:%d lengths:%d values:\"%d%d\"}}}", len(strconv.Itoa(i)), len(strconv.Itoa(i*10)), i, i*10))
   429  		t2Events = append(t2Events,
   430  			fmt.Sprintf("type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:%d lengths:%d values:\"%d%d\"}}}", len(strconv.Itoa(i)), len(strconv.Itoa(i*20)), i, i*20))
   431  	}
   432  	t1Events = append(t1Events, "lastpk", "commit")
   433  	t2Events = append(t2Events, "lastpk", "commit")
   434  
   435  	insertEvents1 := []string{
   436  		"begin",
   437  		"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id11\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id11\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id12\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id12\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   438  		"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:3 lengths:4 values:\"1011010\"}}}",
   439  		"gtid",
   440  		"commit"}
   441  	insertEvents2 := []string{
   442  		"begin",
   443  		"type:FIELD field_event:{table_name:\"t2\" fields:{name:\"id21\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id21\" column_length:11 charset:63 column_type:\"int(11)\"} fields:{name:\"id22\" type:INT32 table:\"t2\" org_table:\"t2\" database:\"vttest\" org_name:\"id22\" column_length:11 charset:63 column_type:\"int(11)\"}}",
   444  		"type:ROW row_event:{table_name:\"t2\" row_changes:{after:{lengths:3 lengths:4 values:\"2022020\"}}}",
   445  		"gtid",
   446  		"commit"}
   447  
   448  	testcases := []testcase{
   449  		{
   450  			input:  []string{},
   451  			output: [][]string{t1FieldEvent, {"gtid"}, t1Events, {"begin", "lastpk", "commit"}, t2FieldEvent, t2Events, {"begin", "lastpk", "commit"}, {"copy_completed"}},
   452  		},
   453  
   454  		{
   455  			input: []string{
   456  				"insert into t1 values (101, 1010)",
   457  			},
   458  			output: [][]string{insertEvents1},
   459  		},
   460  		{
   461  			input: []string{
   462  				"insert into t2 values (202, 2020)",
   463  			},
   464  			output: [][]string{insertEvents2},
   465  		},
   466  	}
   467  
   468  	runCases(t, filter, testcases, "vscopy", tablePKs)
   469  	log.Infof("Pos at end of test: %s", primaryPosition(t))
   470  }
   471  
   472  func TestVStreamCopyWithDifferentFilters(t *testing.T) {
   473  	if testing.Short() {
   474  		t.Skip()
   475  	}
   476  	execStatements(t, []string{
   477  		"create table t1(id1 int, id2 int, id3 int, primary key(id1))",
   478  		"create table t2a(id1 int, id2 int, primary key(id1))",
   479  		"create table t2b(id1 varchar(20), id2 int, primary key(id1))",
   480  	})
   481  	defer execStatements(t, []string{
   482  		"drop table t1",
   483  		"drop table t2a",
   484  		"drop table t2b",
   485  	})
   486  	engine.se.Reload(context.Background())
   487  	ctx, cancel := context.WithCancel(context.Background())
   488  	defer cancel()
   489  	filter := &binlogdatapb.Filter{
   490  		Rules: []*binlogdatapb.Rule{{
   491  			Match: "/t2.*",
   492  		}, {
   493  			Match:  "t1",
   494  			Filter: "select id1, id2 from t1",
   495  		}},
   496  	}
   497  
   498  	execStatements(t, []string{
   499  		"insert into t1(id1, id2, id3) values (1, 2, 3)",
   500  		"insert into t2a(id1, id2) values (1, 4)",
   501  		"insert into t2b(id1, id2) values ('b', 6)",
   502  		"insert into t2b(id1, id2) values ('a', 5)",
   503  	})
   504  
   505  	var expectedEvents = []string{
   506  		"type:BEGIN",
   507  		"type:FIELD field_event:{table_name:\"t1\" fields:{name:\"id1\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id1\" column_length:11 charset:63} fields:{name:\"id2\" type:INT32 table:\"t1\" org_table:\"t1\" database:\"vttest\" org_name:\"id2\" column_length:11 charset:63}}",
   508  		"type:GTID",
   509  		"type:ROW row_event:{table_name:\"t1\" row_changes:{after:{lengths:1 lengths:1 values:\"12\"}}}",
   510  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t1\" lastpk:{rows:{lengths:1 values:\"1\"}}}}",
   511  		"type:COMMIT",
   512  		"type:BEGIN",
   513  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t1\"} completed:true}",
   514  		"type:COMMIT",
   515  		"type:BEGIN",
   516  		"type:FIELD field_event:{table_name:\"t2a\" fields:{name:\"id1\" type:INT32 table:\"t2a\" org_table:\"t2a\" database:\"vttest\" org_name:\"id1\" column_length:11 charset:63} fields:{name:\"id2\" type:INT32 table:\"t2a\" org_table:\"t2a\" database:\"vttest\" org_name:\"id2\" column_length:11 charset:63}}",
   517  		"type:ROW row_event:{table_name:\"t2a\" row_changes:{after:{lengths:1 lengths:1 values:\"14\"}}}",
   518  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2a\" lastpk:{rows:{lengths:1 values:\"1\"}}}}",
   519  		"type:COMMIT",
   520  		"type:BEGIN",
   521  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2a\"} completed:true}",
   522  		"type:COMMIT",
   523  		"type:BEGIN",
   524  		"type:FIELD field_event:{table_name:\"t2b\" fields:{name:\"id1\" type:VARCHAR table:\"t2b\" org_table:\"t2b\" database:\"vttest\" org_name:\"id1\" column_length:80 charset:45} fields:{name:\"id2\" type:INT32 table:\"t2b\" org_table:\"t2b\" database:\"vttest\" org_name:\"id2\" column_length:11 charset:63}}",
   525  		"type:ROW row_event:{table_name:\"t2b\" row_changes:{after:{lengths:1 lengths:1 values:\"a5\"}}}",
   526  		"type:ROW row_event:{table_name:\"t2b\" row_changes:{after:{lengths:1 lengths:1 values:\"b6\"}}}",
   527  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2b\" lastpk:{rows:{lengths:1 values:\"b\"}}}}",
   528  		"type:COMMIT",
   529  		"type:BEGIN",
   530  		"type:LASTPK last_p_k_event:{table_last_p_k:{table_name:\"t2b\"} completed:true}",
   531  		"type:COMMIT",
   532  	}
   533  
   534  	var allEvents []*binlogdatapb.VEvent
   535  	var wg sync.WaitGroup
   536  	wg.Add(1)
   537  	ctx2, cancel2 := context.WithDeadline(ctx, time.Now().Add(10*time.Second))
   538  	defer cancel2()
   539  
   540  	var errGoroutine error
   541  	go func() {
   542  		defer wg.Done()
   543  		engine.Stream(ctx2, "", nil, filter, func(evs []*binlogdatapb.VEvent) error {
   544  			for _, ev := range evs {
   545  				if ev.Type == binlogdatapb.VEventType_HEARTBEAT {
   546  					continue
   547  				}
   548  				if ev.Throttled {
   549  					continue
   550  				}
   551  				allEvents = append(allEvents, ev)
   552  			}
   553  			if len(allEvents) == len(expectedEvents) {
   554  				log.Infof("Got %d events as expected", len(allEvents))
   555  				for i, ev := range allEvents {
   556  					ev.Timestamp = 0
   557  					if ev.Type == binlogdatapb.VEventType_FIELD {
   558  						for j := range ev.FieldEvent.Fields {
   559  							ev.FieldEvent.Fields[j].Flags = 0
   560  						}
   561  						ev.FieldEvent.Keyspace = ""
   562  						ev.FieldEvent.Shard = ""
   563  					}
   564  					if ev.Type == binlogdatapb.VEventType_ROW {
   565  						ev.RowEvent.Keyspace = ""
   566  						ev.RowEvent.Shard = ""
   567  					}
   568  					got := ev.String()
   569  					want := expectedEvents[i]
   570  					if !strings.HasPrefix(got, want) {
   571  						errGoroutine = fmt.Errorf("Event %d did not match, want %s, got %s", i, want, got)
   572  						return errGoroutine
   573  					}
   574  				}
   575  
   576  				return io.EOF
   577  			}
   578  			return nil
   579  		})
   580  	}()
   581  	wg.Wait()
   582  	if errGoroutine != nil {
   583  		t.Fatalf(errGoroutine.Error())
   584  	}
   585  }
   586  
   587  func TestFilteredVarBinary(t *testing.T) {
   588  	if testing.Short() {
   589  		t.Skip()
   590  	}
   591  
   592  	execStatements(t, []string{
   593  		"create table t1(id1 int, val varbinary(128), primary key(id1))",
   594  	})
   595  	defer execStatements(t, []string{
   596  		"drop table t1",
   597  	})
   598  	engine.se.Reload(context.Background())
   599  
   600  	filter := &binlogdatapb.Filter{
   601  		Rules: []*binlogdatapb.Rule{{
   602  			Match:  "t1",
   603  			Filter: "select id1, val from t1 where val = 'newton'",
   604  		}},
   605  	}
   606  
   607  	testcases := []testcase{{
   608  		input: []string{
   609  			"begin",
   610  			"insert into t1 values (1, 'kepler')",
   611  			"insert into t1 values (2, 'newton')",
   612  			"insert into t1 values (3, 'newton')",
   613  			"insert into t1 values (4, 'kepler')",
   614  			"insert into t1 values (5, 'newton')",
   615  			"update t1 set val = 'newton' where id1 = 1",
   616  			"update t1 set val = 'kepler' where id1 = 2",
   617  			"update t1 set val = 'newton' where id1 = 2",
   618  			"update t1 set val = 'kepler' where id1 = 1",
   619  			"delete from t1 where id1 in (2,3)",
   620  			"commit",
   621  		},
   622  		output: [][]string{{
   623  			`begin`,
   624  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   625  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"2newton"}}}`,
   626  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"3newton"}}}`,
   627  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"5newton"}}}`,
   628  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"1newton"}}}`,
   629  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"2newton"}}}`,
   630  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"2newton"}}}`,
   631  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"1newton"}}}`,
   632  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:6 values:"2newton"}} row_changes:{before:{lengths:1 lengths:6 values:"3newton"}}}`,
   633  			`gtid`,
   634  			`commit`,
   635  		}},
   636  	}}
   637  	runCases(t, filter, testcases, "", nil)
   638  }
   639  
   640  func TestFilteredInt(t *testing.T) {
   641  	if testing.Short() {
   642  		t.Skip()
   643  	}
   644  	engine.se.Reload(context.Background())
   645  
   646  	execStatements(t, []string{
   647  		"create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))",
   648  	})
   649  	defer execStatements(t, []string{
   650  		"drop table t1",
   651  	})
   652  	engine.se.Reload(context.Background())
   653  
   654  	filter := &binlogdatapb.Filter{
   655  		Rules: []*binlogdatapb.Rule{{
   656  			Match:  "t1",
   657  			Filter: "select id1, val from t1 where id2 = 200",
   658  		}},
   659  	}
   660  
   661  	testcases := []testcase{{
   662  		input: []string{
   663  			"begin",
   664  			"insert into t1 values (1, 100, 'aaa')",
   665  			"insert into t1 values (2, 200, 'bbb')",
   666  			"insert into t1 values (3, 100, 'ccc')",
   667  			"insert into t1 values (4, 200, 'ddd')",
   668  			"insert into t1 values (5, 200, 'eee')",
   669  			"update t1 set val = 'newddd' where id1 = 4",
   670  			"update t1 set id2 = 200 where id1 = 1",
   671  			"update t1 set id2 = 100 where id1 = 2",
   672  			"update t1 set id2 = 100 where id1 = 1",
   673  			"update t1 set id2 = 200 where id1 = 2",
   674  			"commit",
   675  		},
   676  		output: [][]string{{
   677  			`begin`,
   678  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   679  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`,
   680  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"4ddd"}}}`,
   681  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"5eee"}}}`,
   682  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"4ddd"} after:{lengths:1 lengths:6 values:"4newddd"}}}`,
   683  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   684  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"2bbb"}}}`,
   685  			`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"}}}`,
   686  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`,
   687  			`gtid`,
   688  			`commit`,
   689  		}},
   690  	}}
   691  	runCases(t, filter, testcases, "", nil)
   692  }
   693  
   694  func TestSavepoint(t *testing.T) {
   695  	if testing.Short() {
   696  		t.Skip()
   697  	}
   698  
   699  	execStatements(t, []string{
   700  		"create table stream1(id int, val varbinary(128), primary key(id))",
   701  		"create table stream2(id int, val varbinary(128), primary key(id))",
   702  	})
   703  	defer execStatements(t, []string{
   704  		"drop table stream1",
   705  		"drop table stream2",
   706  	})
   707  	engine.se.Reload(context.Background())
   708  	testcases := []testcase{{
   709  		input: []string{
   710  			"begin",
   711  			"insert into stream1 values (1, 'aaa')",
   712  			"savepoint a",
   713  			"insert into stream1 values (2, 'aaa')",
   714  			"rollback work to savepoint a",
   715  			"savepoint b",
   716  			"update stream1 set val='bbb' where id = 1",
   717  			"release savepoint b",
   718  			"commit",
   719  		},
   720  		output: [][]string{{
   721  			`begin`,
   722  			`type:FIELD field_event:{table_name:"stream1" fields:{name:"id" type:INT32 table:"stream1" org_table:"stream1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream1" org_table:"stream1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   723  			`type:ROW row_event:{table_name:"stream1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   724  			`type:ROW row_event:{table_name:"stream1" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:1 lengths:3 values:"1bbb"}}}`,
   725  			`gtid`,
   726  			`commit`,
   727  		}},
   728  	}}
   729  	runCases(t, nil, testcases, "current", nil)
   730  }
   731  
   732  func TestSavepointWithFilter(t *testing.T) {
   733  	if testing.Short() {
   734  		t.Skip()
   735  	}
   736  
   737  	execStatements(t, []string{
   738  		"create table stream1(id int, val varbinary(128), primary key(id))",
   739  		"create table stream2(id int, val varbinary(128), primary key(id))",
   740  	})
   741  	defer execStatements(t, []string{
   742  		"drop table stream1",
   743  		"drop table stream2",
   744  	})
   745  	engine.se.Reload(context.Background())
   746  	testcases := []testcase{{
   747  		input: []string{
   748  			"begin",
   749  			"insert into stream1 values (1, 'aaa')",
   750  			"savepoint a",
   751  			"insert into stream1 values (2, 'aaa')",
   752  			"savepoint b",
   753  			"insert into stream1 values (3, 'aaa')",
   754  			"savepoint c",
   755  			"insert into stream1 values (4, 'aaa')",
   756  			"savepoint d",
   757  			"commit",
   758  
   759  			"begin",
   760  			"insert into stream1 values (5, 'aaa')",
   761  			"savepoint d",
   762  			"insert into stream1 values (6, 'aaa')",
   763  			"savepoint c",
   764  			"insert into stream1 values (7, 'aaa')",
   765  			"savepoint b",
   766  			"insert into stream1 values (8, 'aaa')",
   767  			"savepoint a",
   768  			"commit",
   769  
   770  			"begin",
   771  			"insert into stream1 values (9, 'aaa')",
   772  			"savepoint a",
   773  			"insert into stream2 values (1, 'aaa')",
   774  			"savepoint b",
   775  			"insert into stream1 values (10, 'aaa')",
   776  			"savepoint c",
   777  			"insert into stream2 values (2, 'aaa')",
   778  			"savepoint d",
   779  			"commit",
   780  		},
   781  		output: [][]string{{
   782  			`begin`,
   783  			`gtid`,
   784  			`commit`,
   785  		}, {
   786  			`begin`,
   787  			`gtid`,
   788  			`commit`,
   789  		}, {
   790  			`begin`,
   791  			`type:FIELD field_event:{table_name:"stream2" fields:{name:"id" type:INT32 table:"stream2" org_table:"stream2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream2" org_table:"stream2" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   792  			`type:ROW row_event:{table_name:"stream2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   793  			`type:ROW row_event:{table_name:"stream2" row_changes:{after:{lengths:1 lengths:3 values:"2aaa"}}}`,
   794  			`gtid`,
   795  			`commit`,
   796  		}},
   797  	}}
   798  
   799  	filter := &binlogdatapb.Filter{
   800  		Rules: []*binlogdatapb.Rule{{
   801  			Match:  "stream2",
   802  			Filter: "select * from stream2",
   803  		}},
   804  	}
   805  	runCases(t, filter, testcases, "current", nil)
   806  }
   807  
   808  func TestStatements(t *testing.T) {
   809  	if testing.Short() {
   810  		t.Skip()
   811  	}
   812  
   813  	execStatements(t, []string{
   814  		"create table stream1(id int, val varbinary(128), primary key(id))",
   815  		"create table stream2(id int, val varbinary(128), primary key(id))",
   816  	})
   817  	defer execStatements(t, []string{
   818  		"drop table stream1",
   819  		"drop table stream2",
   820  	})
   821  	engine.se.Reload(context.Background())
   822  
   823  	testcases := []testcase{{
   824  		input: []string{
   825  			"begin",
   826  			"insert into stream1 values (1, 'aaa')",
   827  			"update stream1 set val='bbb' where id = 1",
   828  			"commit",
   829  		},
   830  		output: [][]string{{
   831  			`begin`,
   832  			`type:FIELD field_event:{table_name:"stream1" fields:{name:"id" type:INT32 table:"stream1" org_table:"stream1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream1" org_table:"stream1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   833  			`type:ROW row_event:{table_name:"stream1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   834  			`type:ROW row_event:{table_name:"stream1" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:1 lengths:3 values:"1bbb"}}}`,
   835  			`gtid`,
   836  			`commit`,
   837  		}},
   838  	}, {
   839  		// Normal DDL.
   840  		input: "alter table stream1 change column val val varbinary(128)",
   841  		output: [][]string{{
   842  			`gtid`,
   843  			`type:DDL statement:"alter table stream1 change column val val varbinary(128)"`,
   844  		}},
   845  	}, {
   846  		// DDL padded with comments.
   847  		input: " /* prefix */ alter table stream1 change column val val varbinary(256) /* suffix */ ",
   848  		output: [][]string{{
   849  			`gtid`,
   850  			`type:DDL statement:"/* prefix */ alter table stream1 change column val val varbinary(256) /* suffix */"`,
   851  		}},
   852  	}, {
   853  		// Multiple tables, and multiple rows changed per statement.
   854  		input: []string{
   855  			"begin",
   856  			"insert into stream1 values (2, 'bbb')",
   857  			"insert into stream2 values (1, 'aaa')",
   858  			"update stream1 set val='ccc'",
   859  			"delete from stream1",
   860  			"commit",
   861  		},
   862  		output: [][]string{{
   863  			`begin`,
   864  			`type:FIELD field_event:{table_name:"stream1" fields:{name:"id" type:INT32 table:"stream1" org_table:"stream1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream1" org_table:"stream1" database:"vttest" org_name:"val" column_length:256 charset:63 column_type:"varbinary(256)"}}`,
   865  			`type:ROW row_event:{table_name:"stream1" row_changes:{after:{lengths:1 lengths:3 values:"2bbb"}}}`,
   866  			`type:FIELD field_event:{table_name:"stream2" fields:{name:"id" type:INT32 table:"stream2" org_table:"stream2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"stream2" org_table:"stream2" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
   867  			`type:ROW row_event:{table_name:"stream2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
   868  			`type:ROW row_event:{table_name:"stream1" ` +
   869  				`row_changes:{before:{lengths:1 lengths:3 values:"1bbb"} after:{lengths:1 lengths:3 values:"1ccc"}} ` +
   870  				`row_changes:{before:{lengths:1 lengths:3 values:"2bbb"} after:{lengths:1 lengths:3 values:"2ccc"}}}`,
   871  			`type:ROW row_event:{table_name:"stream1" ` +
   872  				`row_changes:{before:{lengths:1 lengths:3 values:"1ccc"}} ` +
   873  				`row_changes:{before:{lengths:1 lengths:3 values:"2ccc"}}}`,
   874  			`gtid`,
   875  			`commit`,
   876  		}},
   877  	}, {
   878  		// truncate is a DDL
   879  		input: "truncate table stream2",
   880  		output: [][]string{{
   881  			`gtid`,
   882  			`type:DDL statement:"truncate table stream2"`,
   883  		}},
   884  	}, {
   885  		// Reverse alter table, else FilePos tests fail
   886  		input: " /* prefix */ alter table stream1 change column val val varbinary(128) /* suffix */ ",
   887  		output: [][]string{{
   888  			`gtid`,
   889  			`type:DDL statement:"/* prefix */ alter table stream1 change column val val varbinary(128) /* suffix */"`,
   890  		}},
   891  	}}
   892  	runCases(t, nil, testcases, "current", nil)
   893  	// Test FilePos flavor
   894  	savedEngine := engine
   895  	defer func() { engine = savedEngine }()
   896  	engine = customEngine(t, func(in mysql.ConnParams) mysql.ConnParams {
   897  		in.Flavor = "FilePos"
   898  		return in
   899  	})
   900  
   901  	defer engine.Close()
   902  	runCases(t, nil, testcases, "current", nil)
   903  }
   904  
   905  // TestOther tests "other" and "priv" statements. These statements can
   906  // produce very different events depending on the version of mysql or
   907  // mariadb. So, we just show that vreplication transmits "OTHER" events
   908  // if the binlog is affected by the statement.
   909  func TestOther(t *testing.T) {
   910  	if testing.Short() {
   911  		t.Skip()
   912  	}
   913  
   914  	execStatements(t, []string{
   915  		"create table stream1(id int, val varbinary(128), primary key(id))",
   916  		"create table stream2(id int, val varbinary(128), primary key(id))",
   917  	})
   918  	defer execStatements(t, []string{
   919  		"drop table stream1",
   920  		"drop table stream2",
   921  	})
   922  	engine.se.Reload(context.Background())
   923  
   924  	testcases := []string{
   925  		"repair table stream2",
   926  		"optimize table stream2",
   927  		"analyze table stream2",
   928  		"select * from stream1",
   929  		"set @val=1",
   930  		"show tables",
   931  		"describe stream1",
   932  		"grant select on stream1 to current_user()",
   933  		"revoke select on stream1 from current_user()",
   934  	}
   935  
   936  	// customRun is a modified version of runCases.
   937  	customRun := func(mode string) {
   938  		t.Logf("Run mode: %v", mode)
   939  		ctx, cancel := context.WithCancel(context.Background())
   940  		defer cancel()
   941  		wg, ch := startStream(ctx, t, nil, "", nil)
   942  		defer wg.Wait()
   943  		want := [][]string{{
   944  			`gtid`,
   945  			`type:OTHER`,
   946  		}}
   947  
   948  		for _, stmt := range testcases {
   949  			startPosition := primaryPosition(t)
   950  			execStatement(t, stmt)
   951  			endPosition := primaryPosition(t)
   952  			if startPosition == endPosition {
   953  				t.Logf("statement %s did not affect binlog", stmt)
   954  				continue
   955  			}
   956  			expectLog(ctx, t, stmt, ch, want)
   957  		}
   958  		cancel()
   959  		if evs, ok := <-ch; ok {
   960  			t.Fatalf("unexpected evs: %v", evs)
   961  		}
   962  	}
   963  	customRun("gtid")
   964  
   965  	// Test FilePos flavor
   966  	savedEngine := engine
   967  	defer func() { engine = savedEngine }()
   968  	engine = customEngine(t, func(in mysql.ConnParams) mysql.ConnParams {
   969  		in.Flavor = "FilePos"
   970  		return in
   971  	})
   972  	defer engine.Close()
   973  	customRun("filePos")
   974  }
   975  
   976  func TestRegexp(t *testing.T) {
   977  	if testing.Short() {
   978  		t.Skip()
   979  	}
   980  
   981  	execStatements(t, []string{
   982  		"create table yes_stream(id int, val varbinary(128), primary key(id))",
   983  		"create table no_stream(id int, val varbinary(128), primary key(id))",
   984  	})
   985  	defer execStatements(t, []string{
   986  		"drop table yes_stream",
   987  		"drop table no_stream",
   988  	})
   989  	engine.se.Reload(context.Background())
   990  
   991  	filter := &binlogdatapb.Filter{
   992  		Rules: []*binlogdatapb.Rule{{
   993  			Match: "/yes.*/",
   994  		}},
   995  	}
   996  
   997  	testcases := []testcase{{
   998  		input: []string{
   999  			"begin",
  1000  			"insert into yes_stream values (1, 'aaa')",
  1001  			"insert into no_stream values (2, 'bbb')",
  1002  			"update yes_stream set val='bbb' where id = 1",
  1003  			"update no_stream set val='bbb' where id = 2",
  1004  			"commit",
  1005  		},
  1006  		output: [][]string{{
  1007  			`begin`,
  1008  			`type:FIELD field_event:{table_name:"yes_stream" fields:{name:"id" type:INT32 table:"yes_stream" org_table:"yes_stream" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"yes_stream" org_table:"yes_stream" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1009  			`type:ROW row_event:{table_name:"yes_stream" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
  1010  			`type:ROW row_event:{table_name:"yes_stream" row_changes:{before:{lengths:1 lengths:3 values:"1aaa"} after:{lengths:1 lengths:3 values:"1bbb"}}}`,
  1011  			`gtid`,
  1012  			`commit`,
  1013  		}},
  1014  	}}
  1015  	runCases(t, filter, testcases, "", nil)
  1016  }
  1017  
  1018  func TestREKeyRange(t *testing.T) {
  1019  	if testing.Short() {
  1020  		t.Skip()
  1021  	}
  1022  	ignoreKeyspaceShardInFieldAndRowEvents = false
  1023  	defer func() {
  1024  		ignoreKeyspaceShardInFieldAndRowEvents = true
  1025  	}()
  1026  	// Needed for this test to run if run standalone
  1027  	engine.watcherOnce.Do(engine.setWatch)
  1028  
  1029  	execStatements(t, []string{
  1030  		"create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))",
  1031  	})
  1032  	defer execStatements(t, []string{
  1033  		"drop table t1",
  1034  	})
  1035  	engine.se.Reload(context.Background())
  1036  
  1037  	setVSchema(t, shardedVSchema)
  1038  	defer env.SetVSchema("{}")
  1039  
  1040  	ctx, cancel := context.WithCancel(context.Background())
  1041  	defer cancel()
  1042  
  1043  	filter := &binlogdatapb.Filter{
  1044  		Rules: []*binlogdatapb.Rule{{
  1045  			Match:  "/.*/",
  1046  			Filter: "-80",
  1047  		}},
  1048  	}
  1049  	wg, ch := startStream(ctx, t, filter, "", nil)
  1050  	defer wg.Wait()
  1051  	// 1, 2, 3 and 5 are in shard -80.
  1052  	// 4 and 6 are in shard 80-.
  1053  	input := []string{
  1054  		"begin",
  1055  		"insert into t1 values (1, 4, 'aaa')",
  1056  		"insert into t1 values (4, 1, 'bbb')",
  1057  		// Stay in shard.
  1058  		"update t1 set id1 = 2 where id1 = 1",
  1059  		// Move from -80 to 80-.
  1060  		"update t1 set id1 = 6 where id1 = 2",
  1061  		// Move from 80- to -80.
  1062  		"update t1 set id1 = 3 where id1 = 4",
  1063  		"commit",
  1064  	}
  1065  	execStatements(t, input)
  1066  	expectLog(ctx, t, input, ch, [][]string{{
  1067  		`begin`,
  1068  		`type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"id2" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id2" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"} keyspace:"vttest" shard:"0"}`,
  1069  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 values:"14aaa"}} keyspace:"vttest" shard:"0"}`,
  1070  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 values:"14aaa"} after:{lengths:1 lengths:1 lengths:3 values:"24aaa"}} keyspace:"vttest" shard:"0"}`,
  1071  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 values:"24aaa"}} keyspace:"vttest" shard:"0"}`,
  1072  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 values:"31bbb"}} keyspace:"vttest" shard:"0"}`,
  1073  		`gtid`,
  1074  		`commit`,
  1075  	}})
  1076  
  1077  	// Switch the vschema to make id2 the primary vindex.
  1078  	altVSchema := `{
  1079    "sharded": true,
  1080    "vindexes": {
  1081      "hash": {
  1082        "type": "hash"
  1083      }
  1084    },
  1085    "tables": {
  1086      "t1": {
  1087        "column_vindexes": [
  1088          {
  1089            "column": "id2",
  1090            "name": "hash"
  1091          }
  1092        ]
  1093      }
  1094    }
  1095  }`
  1096  	setVSchema(t, altVSchema)
  1097  
  1098  	// Only the first insert should be sent.
  1099  	input = []string{
  1100  		"begin",
  1101  		"insert into t1 values (4, 1, 'aaa')",
  1102  		"insert into t1 values (1, 4, 'aaa')",
  1103  		"commit",
  1104  	}
  1105  	execStatements(t, input)
  1106  	expectLog(ctx, t, input, ch, [][]string{{
  1107  		`begin`,
  1108  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 values:"41aaa"}} keyspace:"vttest" shard:"0"}`,
  1109  		`gtid`,
  1110  		`commit`,
  1111  	}})
  1112  	cancel()
  1113  }
  1114  
  1115  func TestInKeyRangeMultiColumn(t *testing.T) {
  1116  	if testing.Short() {
  1117  		t.Skip()
  1118  	}
  1119  	engine.watcherOnce.Do(engine.setWatch)
  1120  	engine.se.Reload(context.Background())
  1121  
  1122  	execStatements(t, []string{
  1123  		"create table t1(region int, id int, val varbinary(128), primary key(id))",
  1124  	})
  1125  	defer execStatements(t, []string{
  1126  		"drop table t1",
  1127  	})
  1128  	engine.se.Reload(context.Background())
  1129  
  1130  	setVSchema(t, multicolumnVSchema)
  1131  	defer env.SetVSchema("{}")
  1132  
  1133  	ctx, cancel := context.WithCancel(context.Background())
  1134  	defer cancel()
  1135  
  1136  	filter := &binlogdatapb.Filter{
  1137  		Rules: []*binlogdatapb.Rule{{
  1138  			Match:  "t1",
  1139  			Filter: "select id, region, val, keyspace_id() from t1 where in_keyrange('-80')",
  1140  		}},
  1141  	}
  1142  	wg, ch := startStream(ctx, t, filter, "", nil)
  1143  	defer wg.Wait()
  1144  
  1145  	// 1, 2, 3 and 5 are in shard -80.
  1146  	// 4 and 6 are in shard 80-.
  1147  	input := []string{
  1148  		"begin",
  1149  		"insert into t1 values (1, 1, 'aaa')",
  1150  		"insert into t1 values (128, 2, 'bbb')",
  1151  		// Stay in shard.
  1152  		"update t1 set region = 2 where id = 1",
  1153  		// Move from -80 to 80-.
  1154  		"update t1 set region = 128 where id = 1",
  1155  		// Move from 80- to -80.
  1156  		"update t1 set region = 1 where id = 2",
  1157  		"commit",
  1158  	}
  1159  	execStatements(t, input)
  1160  	expectLog(ctx, t, input, ch, [][]string{{
  1161  		`begin`,
  1162  		`type:FIELD field_event:{table_name:"t1" fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"region" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"region" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"} fields:{name:"keyspace_id" type:VARBINARY}}`,
  1163  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 lengths:9 values:"11aaa\x01\x16k@\xb4J\xbaK\xd6"}}}`,
  1164  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 lengths:9 values:"11aaa\x01\x16k@\xb4J\xbaK\xd6"} ` +
  1165  			`after:{lengths:1 lengths:1 lengths:3 lengths:9 values:"12aaa\x02\x16k@\xb4J\xbaK\xd6"}}}`,
  1166  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 lengths:9 values:"12aaa\x02\x16k@\xb4J\xbaK\xd6"}}}`,
  1167  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 lengths:9 values:"21bbb\x01\x06\xe7\xea\"Î’p\x8f"}}}`,
  1168  		`gtid`,
  1169  		`commit`,
  1170  	}})
  1171  	cancel()
  1172  }
  1173  
  1174  func TestREMultiColumnVindex(t *testing.T) {
  1175  	if testing.Short() {
  1176  		t.Skip()
  1177  	}
  1178  	engine.watcherOnce.Do(engine.setWatch)
  1179  
  1180  	execStatements(t, []string{
  1181  		"create table t1(region int, id int, val varbinary(128), primary key(id))",
  1182  	})
  1183  	defer execStatements(t, []string{
  1184  		"drop table t1",
  1185  	})
  1186  	engine.se.Reload(context.Background())
  1187  
  1188  	setVSchema(t, multicolumnVSchema)
  1189  	defer env.SetVSchema("{}")
  1190  
  1191  	ctx, cancel := context.WithCancel(context.Background())
  1192  	defer cancel()
  1193  
  1194  	filter := &binlogdatapb.Filter{
  1195  		Rules: []*binlogdatapb.Rule{{
  1196  			Match:  "/.*/",
  1197  			Filter: "-80",
  1198  		}},
  1199  	}
  1200  	wg, ch := startStream(ctx, t, filter, "", nil)
  1201  	defer wg.Wait()
  1202  
  1203  	// 1, 2, 3 and 5 are in shard -80.
  1204  	// 4 and 6 are in shard 80-.
  1205  	input := []string{
  1206  		"begin",
  1207  		"insert into t1 values (1, 1, 'aaa')",
  1208  		"insert into t1 values (128, 2, 'bbb')",
  1209  		// Stay in shard.
  1210  		"update t1 set region = 2 where id = 1",
  1211  		// Move from -80 to 80-.
  1212  		"update t1 set region = 128 where id = 1",
  1213  		// Move from 80- to -80.
  1214  		"update t1 set region = 1 where id = 2",
  1215  		"commit",
  1216  	}
  1217  	execStatements(t, input)
  1218  	expectLog(ctx, t, input, ch, [][]string{{
  1219  		`begin`,
  1220  		`type:FIELD field_event:{table_name:"t1" fields:{name:"region" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"region" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"id" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1221  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 values:"11aaa"}}}`,
  1222  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 values:"11aaa"} after:{lengths:1 lengths:1 lengths:3 values:"21aaa"}}}`,
  1223  		`type:ROW row_event:{table_name:"t1" row_changes:{before:{lengths:1 lengths:1 lengths:3 values:"21aaa"}}}`,
  1224  		`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:1 lengths:3 values:"12bbb"}}}`,
  1225  		`gtid`,
  1226  		`commit`,
  1227  	}})
  1228  	cancel()
  1229  }
  1230  
  1231  func TestSelectFilter(t *testing.T) {
  1232  	if testing.Short() {
  1233  		t.Skip()
  1234  	}
  1235  	engine.se.Reload(context.Background())
  1236  
  1237  	execStatements(t, []string{
  1238  		"create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))",
  1239  	})
  1240  	defer execStatements(t, []string{
  1241  		"drop table t1",
  1242  	})
  1243  	engine.se.Reload(context.Background())
  1244  
  1245  	filter := &binlogdatapb.Filter{
  1246  		Rules: []*binlogdatapb.Rule{{
  1247  			Match:  "t1",
  1248  			Filter: "select id2, val from t1 where in_keyrange(id2, 'hash', '-80')",
  1249  		}},
  1250  	}
  1251  
  1252  	testcases := []testcase{{
  1253  		input: []string{
  1254  			"begin",
  1255  			"insert into t1 values (4, 1, 'aaa')",
  1256  			"insert into t1 values (2, 4, 'aaa')",
  1257  			"commit",
  1258  		},
  1259  		output: [][]string{{
  1260  			`begin`,
  1261  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id2" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id2" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1262  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
  1263  			`gtid`,
  1264  			`commit`,
  1265  		}},
  1266  	}}
  1267  	runCases(t, filter, testcases, "", nil)
  1268  }
  1269  
  1270  func TestDDLAddColumn(t *testing.T) {
  1271  	if testing.Short() {
  1272  		t.Skip()
  1273  	}
  1274  
  1275  	execStatements(t, []string{
  1276  		"create table ddl_test1(id int, val1 varbinary(128), primary key(id))",
  1277  		"create table ddl_test2(id int, val1 varbinary(128), primary key(id))",
  1278  	})
  1279  	defer execStatements(t, []string{
  1280  		"drop table ddl_test1",
  1281  		"drop table ddl_test2",
  1282  	})
  1283  
  1284  	// Record position before the next few statements.
  1285  	pos := primaryPosition(t)
  1286  	execStatements(t, []string{
  1287  		"begin",
  1288  		"insert into ddl_test1 values(1, 'aaa')",
  1289  		"insert into ddl_test2 values(1, 'aaa')",
  1290  		"commit",
  1291  		// Adding columns is allowed.
  1292  		"alter table ddl_test1 add column val2 varbinary(128)",
  1293  		"alter table ddl_test2 add column val2 varbinary(128)",
  1294  		"begin",
  1295  		"insert into ddl_test1 values(2, 'bbb', 'ccc')",
  1296  		"insert into ddl_test2 values(2, 'bbb', 'ccc')",
  1297  		"commit",
  1298  	})
  1299  	engine.se.Reload(context.Background())
  1300  
  1301  	ctx, cancel := context.WithCancel(context.Background())
  1302  	defer cancel()
  1303  
  1304  	// Test RE as well as select-based filters.
  1305  	filter := &binlogdatapb.Filter{
  1306  		Rules: []*binlogdatapb.Rule{{
  1307  			Match:  "ddl_test2",
  1308  			Filter: "select * from ddl_test2",
  1309  		}, {
  1310  			Match: "/.*/",
  1311  		}},
  1312  	}
  1313  
  1314  	ch := make(chan []*binlogdatapb.VEvent)
  1315  	go func() {
  1316  		defer close(ch)
  1317  		if err := vstream(ctx, t, pos, nil, filter, ch); err != nil {
  1318  			t.Error(err)
  1319  		}
  1320  	}()
  1321  	expectLog(ctx, t, "ddls", ch, [][]string{{
  1322  		// Current schema has 3 columns, but they'll be truncated to match the two columns in the event.
  1323  		`begin`,
  1324  		`type:FIELD field_event:{table_name:"ddl_test1" fields:{name:"id" type:INT32 table:"ddl_test1" org_table:"ddl_test1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val1" type:VARBINARY table:"ddl_test1" org_table:"ddl_test1" database:"vttest" org_name:"val1" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1325  		`type:ROW row_event:{table_name:"ddl_test1" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
  1326  		`type:FIELD field_event:{table_name:"ddl_test2" fields:{name:"id" type:INT32 table:"ddl_test2" org_table:"ddl_test2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val1" type:VARBINARY table:"ddl_test2" org_table:"ddl_test2" database:"vttest" org_name:"val1" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1327  		`type:ROW row_event:{table_name:"ddl_test2" row_changes:{after:{lengths:1 lengths:3 values:"1aaa"}}}`,
  1328  		`gtid`,
  1329  		`commit`,
  1330  	}, {
  1331  		`gtid`,
  1332  		`type:DDL statement:"alter table ddl_test1 add column val2 varbinary(128)"`,
  1333  	}, {
  1334  		`gtid`,
  1335  		`type:DDL statement:"alter table ddl_test2 add column val2 varbinary(128)"`,
  1336  	}, {
  1337  		// The plan will be updated to now include the third column
  1338  		// because the new table map will have three columns.
  1339  		`begin`,
  1340  		`type:FIELD field_event:{table_name:"ddl_test1" fields:{name:"id" type:INT32 table:"ddl_test1" org_table:"ddl_test1" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val1" type:VARBINARY table:"ddl_test1" org_table:"ddl_test1" database:"vttest" org_name:"val1" column_length:128 charset:63 column_type:"varbinary(128)"} fields:{name:"val2" type:VARBINARY table:"ddl_test1" org_table:"ddl_test1" database:"vttest" org_name:"val2" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1341  		`type:ROW row_event:{table_name:"ddl_test1" row_changes:{after:{lengths:1 lengths:3 lengths:3 values:"2bbbccc"}}}`,
  1342  		`type:FIELD field_event:{table_name:"ddl_test2" fields:{name:"id" type:INT32 table:"ddl_test2" org_table:"ddl_test2" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val1" type:VARBINARY table:"ddl_test2" org_table:"ddl_test2" database:"vttest" org_name:"val1" column_length:128 charset:63 column_type:"varbinary(128)"} fields:{name:"val2" type:VARBINARY table:"ddl_test2" org_table:"ddl_test2" database:"vttest" org_name:"val2" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1343  		`type:ROW row_event:{table_name:"ddl_test2" row_changes:{after:{lengths:1 lengths:3 lengths:3 values:"2bbbccc"}}}`,
  1344  		`gtid`,
  1345  		`commit`,
  1346  	}})
  1347  }
  1348  
  1349  func TestDDLDropColumn(t *testing.T) {
  1350  	if testing.Short() {
  1351  		t.Skip()
  1352  	}
  1353  	env.SchemaEngine.Reload(context.Background())
  1354  	execStatement(t, "create table ddl_test2(id int, val1 varbinary(128), val2 varbinary(128), primary key(id))")
  1355  	defer execStatement(t, "drop table ddl_test2")
  1356  
  1357  	// Record position before the next few statements.
  1358  	pos := primaryPosition(t)
  1359  	execStatements(t, []string{
  1360  		"insert into ddl_test2 values(1, 'aaa', 'ccc')",
  1361  		// Adding columns is allowed.
  1362  		"alter table ddl_test2 drop column val2",
  1363  		"insert into ddl_test2 values(2, 'bbb')",
  1364  	})
  1365  	engine.se.Reload(context.Background())
  1366  	env.SchemaEngine.Reload(context.Background())
  1367  
  1368  	ctx, cancel := context.WithCancel(context.Background())
  1369  	defer cancel()
  1370  
  1371  	ch := make(chan []*binlogdatapb.VEvent)
  1372  	go func() {
  1373  		for range ch {
  1374  		}
  1375  	}()
  1376  	defer close(ch)
  1377  	err := vstream(ctx, t, pos, nil, nil, ch)
  1378  	want := "cannot determine table columns"
  1379  	if err == nil || !strings.Contains(err.Error(), want) {
  1380  		t.Errorf("err: %v, must contain %s", err, want)
  1381  	}
  1382  }
  1383  
  1384  func TestUnsentDDL(t *testing.T) {
  1385  	if testing.Short() {
  1386  		t.Skip()
  1387  	}
  1388  
  1389  	execStatement(t, "create table unsent(id int, val varbinary(128), primary key(id))")
  1390  
  1391  	testcases := []testcase{{
  1392  		input: []string{
  1393  			"drop table unsent",
  1394  		},
  1395  		// An unsent DDL is sent as an empty transaction.
  1396  		output: [][]string{{
  1397  			`gtid`,
  1398  			`type:OTHER`,
  1399  		}},
  1400  	}}
  1401  
  1402  	filter := &binlogdatapb.Filter{
  1403  		Rules: []*binlogdatapb.Rule{{
  1404  			Match: "/none/",
  1405  		}},
  1406  	}
  1407  	runCases(t, filter, testcases, "", nil)
  1408  }
  1409  
  1410  func TestBuffering(t *testing.T) {
  1411  	if testing.Short() {
  1412  		t.Skip()
  1413  	}
  1414  
  1415  	reset := AdjustPacketSize(10)
  1416  	defer reset()
  1417  
  1418  	execStatement(t, "create table packet_test(id int, val varbinary(128), primary key(id))")
  1419  	defer execStatement(t, "drop table packet_test")
  1420  	engine.se.Reload(context.Background())
  1421  
  1422  	testcases := []testcase{{
  1423  		// All rows in one packet.
  1424  		input: []string{
  1425  			"begin",
  1426  			"insert into packet_test values (1, '123')",
  1427  			"insert into packet_test values (2, '456')",
  1428  			"commit",
  1429  		},
  1430  		output: [][]string{{
  1431  			`begin`,
  1432  			`type:FIELD field_event:{table_name:"packet_test" fields:{name:"id" type:INT32 table:"packet_test" org_table:"packet_test" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"packet_test" org_table:"packet_test" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1433  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:3 values:"1123"}}}`,
  1434  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:3 values:"2456"}}}`,
  1435  			`gtid`,
  1436  			`commit`,
  1437  		}},
  1438  	}, {
  1439  		// A new row causes packet size to be exceeded.
  1440  		// Also test deletes
  1441  		input: []string{
  1442  			"begin",
  1443  			"insert into packet_test values (3, '123456')",
  1444  			"insert into packet_test values (4, '789012')",
  1445  			"delete from packet_test where id=3",
  1446  			"delete from packet_test where id=4",
  1447  			"commit",
  1448  		},
  1449  		output: [][]string{{
  1450  			`begin`,
  1451  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:6 values:"3123456"}}}`,
  1452  		}, {
  1453  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:6 values:"4789012"}}}`,
  1454  		}, {
  1455  			`type:ROW row_event:{table_name:"packet_test" row_changes:{before:{lengths:1 lengths:6 values:"3123456"}}}`,
  1456  		}, {
  1457  			`type:ROW row_event:{table_name:"packet_test" row_changes:{before:{lengths:1 lengths:6 values:"4789012"}}}`,
  1458  			`gtid`,
  1459  			`commit`,
  1460  		}},
  1461  	}, {
  1462  		// A single row is itself bigger than the packet size.
  1463  		input: []string{
  1464  			"begin",
  1465  			"insert into packet_test values (5, '123456')",
  1466  			"insert into packet_test values (6, '12345678901')",
  1467  			"insert into packet_test values (7, '23456')",
  1468  			"commit",
  1469  		},
  1470  		output: [][]string{{
  1471  			`begin`,
  1472  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:6 values:"5123456"}}}`,
  1473  		}, {
  1474  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:11 values:"612345678901"}}}`,
  1475  		}, {
  1476  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:5 values:"723456"}}}`,
  1477  			`gtid`,
  1478  			`commit`,
  1479  		}},
  1480  	}, {
  1481  		// An update packet is bigger because it has a before and after image.
  1482  		input: []string{
  1483  			"begin",
  1484  			"insert into packet_test values (8, '123')",
  1485  			"update packet_test set val='456' where id=8",
  1486  			"commit",
  1487  		},
  1488  		output: [][]string{{
  1489  			`begin`,
  1490  			`type:ROW row_event:{table_name:"packet_test" row_changes:{after:{lengths:1 lengths:3 values:"8123"}}}`,
  1491  		}, {
  1492  			`type:ROW row_event:{table_name:"packet_test" row_changes:{before:{lengths:1 lengths:3 values:"8123"} after:{lengths:1 lengths:3 values:"8456"}}}`,
  1493  			`gtid`,
  1494  			`commit`,
  1495  		}},
  1496  	}, {
  1497  		// DDL is in its own packet
  1498  		input: []string{
  1499  			"alter table packet_test change val val varchar(128)",
  1500  		},
  1501  		output: [][]string{{
  1502  			`gtid`,
  1503  			`type:DDL statement:"alter table packet_test change val val varchar(128)"`,
  1504  		}},
  1505  	}}
  1506  	runCases(t, nil, testcases, "", nil)
  1507  }
  1508  
  1509  func TestBestEffortNameInFieldEvent(t *testing.T) {
  1510  	if testing.Short() {
  1511  		t.Skip()
  1512  	}
  1513  	filter := &binlogdatapb.Filter{
  1514  		FieldEventMode: binlogdatapb.Filter_BEST_EFFORT,
  1515  		Rules: []*binlogdatapb.Rule{{
  1516  			Match: "/.*/",
  1517  		}},
  1518  	}
  1519  	// Modeled after vttablet endtoend compatibility tests.
  1520  	execStatements(t, []string{
  1521  		"create table vitess_test(id int, val varbinary(128), primary key(id))",
  1522  	})
  1523  	position := primaryPosition(t)
  1524  	execStatements(t, []string{
  1525  		"insert into vitess_test values(1, 'abc')",
  1526  		"rename table vitess_test to vitess_test_new",
  1527  	})
  1528  
  1529  	defer execStatements(t, []string{
  1530  		"drop table vitess_test_new",
  1531  	})
  1532  	engine.se.Reload(context.Background())
  1533  	testcases := []testcase{{
  1534  		input: []string{
  1535  			"insert into vitess_test_new values(2, 'abc')",
  1536  		},
  1537  		// In this case, we don't have information about vitess_test since it was renamed to vitess_test_test.
  1538  		// information returned by binlog for val column == varchar (rather than varbinary).
  1539  		output: [][]string{{
  1540  			`begin`,
  1541  			`type:FIELD field_event:{table_name:"vitess_test" fields:{name:"@1" type:INT32} fields:{name:"@2" type:VARCHAR}}`,
  1542  			`type:ROW row_event:{table_name:"vitess_test" row_changes:{after:{lengths:1 lengths:3 values:"1abc"}}}`,
  1543  			`gtid`,
  1544  			`commit`,
  1545  		}, {
  1546  			`gtid`,
  1547  			`type:DDL statement:"rename table vitess_test to vitess_test_new"`,
  1548  		}, {
  1549  			`begin`,
  1550  			`type:FIELD field_event:{table_name:"vitess_test_new" fields:{name:"id" type:INT32 table:"vitess_test_new" org_table:"vitess_test_new" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"vitess_test_new" org_table:"vitess_test_new" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1551  			`type:ROW row_event:{table_name:"vitess_test_new" row_changes:{after:{lengths:1 lengths:3 values:"2abc"}}}`,
  1552  			`gtid`,
  1553  			`commit`,
  1554  		}},
  1555  	}}
  1556  	runCases(t, filter, testcases, position, nil)
  1557  }
  1558  
  1559  // test that vstreamer ignores tables created by OnlineDDL
  1560  func TestInternalTables(t *testing.T) {
  1561  	if testing.Short() {
  1562  		t.Skip()
  1563  	}
  1564  	filter := &binlogdatapb.Filter{
  1565  		FieldEventMode: binlogdatapb.Filter_BEST_EFFORT,
  1566  		Rules: []*binlogdatapb.Rule{{
  1567  			Match: "/.*/",
  1568  		}},
  1569  	}
  1570  	// Modeled after vttablet endtoend compatibility tests.
  1571  	execStatements(t, []string{
  1572  		"create table vitess_test(id int, val varbinary(128), primary key(id))",
  1573  		"create table _1e275eef_3b20_11eb_a38f_04ed332e05c2_20201210204529_gho(id int, val varbinary(128), primary key(id))",
  1574  		"create table _vt_PURGE_1f9194b43b2011eb8a0104ed332e05c2_20201210194431(id int, val varbinary(128), primary key(id))",
  1575  		"create table _product_old(id int, val varbinary(128), primary key(id))",
  1576  	})
  1577  	position := primaryPosition(t)
  1578  	execStatements(t, []string{
  1579  		"insert into vitess_test values(1, 'abc')",
  1580  		"insert into _1e275eef_3b20_11eb_a38f_04ed332e05c2_20201210204529_gho values(1, 'abc')",
  1581  		"insert into _vt_PURGE_1f9194b43b2011eb8a0104ed332e05c2_20201210194431 values(1, 'abc')",
  1582  		"insert into _product_old values(1, 'abc')",
  1583  	})
  1584  
  1585  	defer execStatements(t, []string{
  1586  		"drop table vitess_test",
  1587  		"drop table _1e275eef_3b20_11eb_a38f_04ed332e05c2_20201210204529_gho",
  1588  		"drop table _vt_PURGE_1f9194b43b2011eb8a0104ed332e05c2_20201210194431",
  1589  		"drop table _product_old",
  1590  	})
  1591  	engine.se.Reload(context.Background())
  1592  	testcases := []testcase{{
  1593  		input: []string{
  1594  			"insert into vitess_test values(2, 'abc')",
  1595  		},
  1596  		// In this case, we don't have information about vitess_test since it was renamed to vitess_test_test.
  1597  		// information returned by binlog for val column == varchar (rather than varbinary).
  1598  		output: [][]string{{
  1599  			`begin`,
  1600  			`type:FIELD field_event:{table_name:"vitess_test" fields:{name:"id" type:INT32 table:"vitess_test" org_table:"vitess_test" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"vitess_test" org_table:"vitess_test" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1601  			`type:ROW row_event:{table_name:"vitess_test" row_changes:{after:{lengths:1 lengths:3 values:"1abc"}}}`,
  1602  			`gtid`,
  1603  			`commit`,
  1604  		}, {`begin`, `gtid`, `commit`}, {`begin`, `gtid`, `commit`}, {`begin`, `gtid`, `commit`}, // => inserts into the three internal comments
  1605  			{
  1606  				`begin`,
  1607  				`type:ROW row_event:{table_name:"vitess_test" row_changes:{after:{lengths:1 lengths:3 values:"2abc"}}}`,
  1608  				`gtid`,
  1609  				`commit`,
  1610  			}},
  1611  	}}
  1612  	runCases(t, filter, testcases, position, nil)
  1613  }
  1614  
  1615  func TestTypes(t *testing.T) {
  1616  	if testing.Short() {
  1617  		t.Skip()
  1618  	}
  1619  
  1620  	// Modeled after vttablet endtoend compatibility tests.
  1621  	execStatements(t, []string{
  1622  		"create table vitess_ints(tiny tinyint, tinyu tinyint unsigned, small smallint, smallu smallint unsigned, medium mediumint, mediumu mediumint unsigned, normal int, normalu int unsigned, big bigint, bigu bigint unsigned, y year, primary key(tiny))",
  1623  		"create table vitess_fracts(id int, deci decimal(5,2), num numeric(5,2), f float, d double, primary key(id))",
  1624  		"create table vitess_strings(vb varbinary(16), c char(16), vc varchar(16), b binary(4), tb tinyblob, bl blob, ttx tinytext, tx text, en enum('a','b'), s set('a','b'), primary key(vb))",
  1625  		"create table vitess_misc(id int, b bit(8), d date, dt datetime, t time, g geometry, primary key(id))",
  1626  		"create table vitess_null(id int, val varbinary(128), primary key(id))",
  1627  		"create table vitess_decimal(id int, dec1 decimal(12,4), dec2 decimal(13,4), primary key(id))",
  1628  	})
  1629  	defer execStatements(t, []string{
  1630  		"drop table vitess_ints",
  1631  		"drop table vitess_fracts",
  1632  		"drop table vitess_strings",
  1633  		"drop table vitess_misc",
  1634  		"drop table vitess_null",
  1635  		"drop table vitess_decimal",
  1636  	})
  1637  	engine.se.Reload(context.Background())
  1638  
  1639  	testcases := []testcase{{
  1640  		input: []string{
  1641  			"insert into vitess_ints values(-128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)",
  1642  		},
  1643  		output: [][]string{{
  1644  			`begin`,
  1645  			`type:FIELD field_event:{table_name:"vitess_ints" fields:{name:"tiny" type:INT8 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"tiny" column_length:4 charset:63 column_type:"tinyint(4)"} fields:{name:"tinyu" type:UINT8 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"tinyu" column_length:3 charset:63 column_type:"tinyint(3) unsigned"} fields:{name:"small" type:INT16 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"small" column_length:6 charset:63 column_type:"smallint(6)"} fields:{name:"smallu" type:UINT16 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"smallu" column_length:5 charset:63 column_type:"smallint(5) unsigned"} fields:{name:"medium" type:INT24 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"medium" column_length:9 charset:63 column_type:"mediumint(9)"} fields:{name:"mediumu" type:UINT24 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"mediumu" column_length:8 charset:63 column_type:"mediumint(8) unsigned"} fields:{name:"normal" type:INT32 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"normal" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"normalu" type:UINT32 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"normalu" column_length:10 charset:63 column_type:"int(10) unsigned"} fields:{name:"big" type:INT64 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"big" column_length:20 charset:63 column_type:"bigint(20)"} fields:{name:"bigu" type:UINT64 table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"bigu" column_length:20 charset:63 column_type:"bigint(20) unsigned"} fields:{name:"y" type:YEAR table:"vitess_ints" org_table:"vitess_ints" database:"vttest" org_name:"y" column_length:4 charset:63 column_type:"year(4)"}}`,
  1646  			`type:ROW row_event:{table_name:"vitess_ints" row_changes:{after:{lengths:4 lengths:3 lengths:6 lengths:5 lengths:8 lengths:8 lengths:11 lengths:10 lengths:20 lengths:20 lengths:4 values:"` +
  1647  				`-128` +
  1648  				`255` +
  1649  				`-32768` +
  1650  				`65535` +
  1651  				`-8388608` +
  1652  				`16777215` +
  1653  				`-2147483648` +
  1654  				`4294967295` +
  1655  				`-9223372036854775808` +
  1656  				`18446744073709551615` +
  1657  				`2012` +
  1658  				`"}}}`,
  1659  			`gtid`,
  1660  			`commit`,
  1661  		}},
  1662  	}, {
  1663  		input: []string{
  1664  			"insert into vitess_fracts values(1, 1.99, 2.99, 3.99, 4.99)",
  1665  		},
  1666  		output: [][]string{{
  1667  			`begin`,
  1668  			`type:FIELD field_event:{table_name:"vitess_fracts" fields:{name:"id" type:INT32 table:"vitess_fracts" org_table:"vitess_fracts" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"deci" type:DECIMAL table:"vitess_fracts" org_table:"vitess_fracts" database:"vttest" org_name:"deci" column_length:7 charset:63 decimals:2 column_type:"decimal(5,2)"} fields:{name:"num" type:DECIMAL table:"vitess_fracts" org_table:"vitess_fracts" database:"vttest" org_name:"num" column_length:7 charset:63 decimals:2 column_type:"decimal(5,2)"} fields:{name:"f" type:FLOAT32 table:"vitess_fracts" org_table:"vitess_fracts" database:"vttest" org_name:"f" column_length:12 charset:63 decimals:31 column_type:"float"} fields:{name:"d" type:FLOAT64 table:"vitess_fracts" org_table:"vitess_fracts" database:"vttest" org_name:"d" column_length:22 charset:63 decimals:31 column_type:"double"}}`,
  1669  			`type:ROW row_event:{table_name:"vitess_fracts" row_changes:{after:{lengths:1 lengths:4 lengths:4 lengths:8 lengths:8 values:"11.992.993.99E+004.99E+00"}}}`,
  1670  			`gtid`,
  1671  			`commit`,
  1672  		}},
  1673  	}, {
  1674  		// TODO(sougou): validate that binary and char data generate correct DMLs on the other end.
  1675  		input: []string{
  1676  			"insert into vitess_strings values('a', 'b', 'c', 'd\000\000\000', 'e', 'f', 'g', 'h', 'a', 'a,b')",
  1677  		},
  1678  		output: [][]string{{
  1679  			`begin`,
  1680  			`type:FIELD field_event:{table_name:"vitess_strings" fields:{name:"vb" type:VARBINARY table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"vb" column_length:16 charset:63 column_type:"varbinary(16)"} fields:{name:"c" type:CHAR table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"c" column_length:64 charset:45 column_type:"char(16)"} fields:{name:"vc" type:VARCHAR table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"vc" column_length:64 charset:45 column_type:"varchar(16)"} fields:{name:"b" type:BINARY table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"b" column_length:4 charset:63 column_type:"binary(4)"} fields:{name:"tb" type:BLOB table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"tb" column_length:255 charset:63 column_type:"tinyblob"} fields:{name:"bl" type:BLOB table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"bl" column_length:65535 charset:63 column_type:"blob"} fields:{name:"ttx" type:TEXT table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"ttx" column_length:1020 charset:45 column_type:"tinytext"} fields:{name:"tx" type:TEXT table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"tx" column_length:262140 charset:45 column_type:"text"} fields:{name:"en" type:ENUM table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"en" column_length:4 charset:45 column_type:"enum('a','b')"} fields:{name:"s" type:SET table:"vitess_strings" org_table:"vitess_strings" database:"vttest" org_name:"s" column_length:12 charset:45 column_type:"set('a','b')"}}`,
  1681  			`type:ROW row_event:{table_name:"vitess_strings" row_changes:{after:{lengths:1 lengths:1 lengths:1 lengths:4 lengths:1 lengths:1 lengths:1 lengths:1 lengths:1 lengths:1 ` +
  1682  				`values:"abcd\x00\x00\x00efgh13"}}}`,
  1683  			`gtid`,
  1684  			`commit`,
  1685  		}},
  1686  	}, {
  1687  		// TODO(sougou): validate that the geometry value generates the correct DMLs on the other end.
  1688  		input: []string{
  1689  			"insert into vitess_misc values(1, '\x01', '2012-01-01', '2012-01-01 15:45:45', '15:45:45', point(1, 2))",
  1690  		},
  1691  		output: [][]string{{
  1692  			`begin`,
  1693  			`type:FIELD field_event:{table_name:"vitess_misc" fields:{name:"id" type:INT32 table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"b" type:BIT table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"b" column_length:8 charset:63 column_type:"bit(8)"} fields:{name:"d" type:DATE table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"d" column_length:10 charset:63 column_type:"date"} fields:{name:"dt" type:DATETIME table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"dt" column_length:19 charset:63 column_type:"datetime"} fields:{name:"t" type:TIME table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"t" column_length:10 charset:63 column_type:"time"} fields:{name:"g" type:GEOMETRY table:"vitess_misc" org_table:"vitess_misc" database:"vttest" org_name:"g" column_length:4294967295 charset:63 column_type:"geometry"}}`,
  1694  			`type:ROW row_event:{table_name:"vitess_misc" row_changes:{after:{lengths:1 lengths:1 lengths:10 lengths:19 lengths:8 lengths:25 values:"1\x012012-01-012012-01-01 15:45:4515:45:45\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@"}}}`,
  1695  			`gtid`,
  1696  			`commit`,
  1697  		}},
  1698  	}, {
  1699  		input: []string{
  1700  			"insert into vitess_null values(1, null)",
  1701  		},
  1702  		output: [][]string{{
  1703  			`begin`,
  1704  			`type:FIELD field_event:{table_name:"vitess_null" fields:{name:"id" type:INT32 table:"vitess_null" org_table:"vitess_null" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"vitess_null" org_table:"vitess_null" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  1705  			`type:ROW row_event:{table_name:"vitess_null" row_changes:{after:{lengths:1 lengths:-1 values:"1"}}}`,
  1706  			`gtid`,
  1707  			`commit`,
  1708  		}},
  1709  	}, {
  1710  		input: []string{
  1711  			"insert into vitess_decimal values(1, 1.23, 1.23)",
  1712  			"insert into vitess_decimal values(2, -1.23, -1.23)",
  1713  			"insert into vitess_decimal values(3, 0000000001.23, 0000000001.23)",
  1714  			"insert into vitess_decimal values(4, -0000000001.23, -0000000001.23)",
  1715  		},
  1716  		output: [][]string{{
  1717  			`begin`,
  1718  			`type:FIELD field_event:{table_name:"vitess_decimal" fields:{name:"id" type:INT32 table:"vitess_decimal" org_table:"vitess_decimal" database:"vttest" org_name:"id" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"dec1" type:DECIMAL table:"vitess_decimal" org_table:"vitess_decimal" database:"vttest" org_name:"dec1" column_length:14 charset:63 decimals:4 column_type:"decimal(12,4)"} fields:{name:"dec2" type:DECIMAL table:"vitess_decimal" org_table:"vitess_decimal" database:"vttest" org_name:"dec2" column_length:15 charset:63 decimals:4 column_type:"decimal(13,4)"}}`,
  1719  			`type:ROW row_event:{table_name:"vitess_decimal" row_changes:{after:{lengths:1 lengths:6 lengths:6 values:"11.23001.2300"}}}`,
  1720  			`gtid`,
  1721  			`commit`,
  1722  		}, {
  1723  			`begin`,
  1724  			`type:ROW row_event:{table_name:"vitess_decimal" row_changes:{after:{lengths:1 lengths:7 lengths:7 values:"2-1.2300-1.2300"}}}`,
  1725  			`gtid`,
  1726  			`commit`,
  1727  		}, {
  1728  			`begin`,
  1729  			`type:ROW row_event:{table_name:"vitess_decimal" row_changes:{after:{lengths:1 lengths:6 lengths:6 values:"31.23001.2300"}}}`,
  1730  			`gtid`,
  1731  			`commit`,
  1732  		}, {
  1733  			`begin`,
  1734  			`type:ROW row_event:{table_name:"vitess_decimal" row_changes:{after:{lengths:1 lengths:7 lengths:7 values:"4-1.2300-1.2300"}}}`,
  1735  			`gtid`,
  1736  			`commit`,
  1737  		}},
  1738  	}}
  1739  	runCases(t, nil, testcases, "", nil)
  1740  }
  1741  
  1742  func TestJSON(t *testing.T) {
  1743  	log.Errorf("TestJSON: flavor is %s", env.Flavor)
  1744  	// JSON is supported only after mysql57.
  1745  	if !strings.Contains(env.Flavor, "mysql57") {
  1746  		return
  1747  	}
  1748  	if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "create table vitess_json(id int default 1, val json, primary key(id))"); err != nil {
  1749  		// If it's a syntax error, MySQL is an older version. Skip this test.
  1750  		if strings.Contains(err.Error(), "syntax") {
  1751  			return
  1752  		}
  1753  		t.Fatal(err)
  1754  	}
  1755  	defer execStatement(t, "drop table vitess_json")
  1756  	engine.se.Reload(context.Background())
  1757  	jsonValues := []string{"{}", "123456", `"vtTablet"`, `{"foo":"bar"}`, `["abc",3.14,true]`}
  1758  
  1759  	var inputs, outputs []string
  1760  	var outputsArray [][]string
  1761  	fieldAdded := false
  1762  	var expect = func(in string) string {
  1763  		return strings.ReplaceAll(in, "\"", "\\\"")
  1764  	}
  1765  	for i, val := range jsonValues {
  1766  		inputs = append(inputs, fmt.Sprintf("insert into vitess_json values(%d, %s)", i+1, encodeString(val)))
  1767  
  1768  		outputs = []string{}
  1769  		outputs = append(outputs, `begin`)
  1770  		if !fieldAdded {
  1771  			outputs = append(outputs, `type:FIELD field_event:{table_name:"vitess_json" fields:{name:"id" type:INT32 table:"vitess_json" org_table:"vitess_json" database:"vttest" org_name:"id" column_length:11 charset:63} fields:{name:"val" type:JSON table:"vitess_json" org_table:"vitess_json" database:"vttest" org_name:"val" column_length:4294967295 charset:63}}`)
  1772  			fieldAdded = true
  1773  		}
  1774  		out := expect(val)
  1775  
  1776  		outputs = append(outputs, fmt.Sprintf(`type:ROW row_event:{table_name:"vitess_json" row_changes:{after:{lengths:1 lengths:%d values:"%d%s"}}}`,
  1777  			len(val), i+1 /*id increments*/, out))
  1778  		outputs = append(outputs, `gtid`)
  1779  		outputs = append(outputs, `commit`)
  1780  		outputsArray = append(outputsArray, outputs)
  1781  	}
  1782  	testcases := []testcase{{
  1783  		input:  inputs,
  1784  		output: outputsArray,
  1785  	}}
  1786  	runCases(t, nil, testcases, "", nil)
  1787  }
  1788  
  1789  func TestExternalTable(t *testing.T) {
  1790  	if testing.Short() {
  1791  		t.Skip()
  1792  	}
  1793  
  1794  	execStatements(t, []string{
  1795  		"create database external",
  1796  		"create table external.ext(id int, val varbinary(128), primary key(id))",
  1797  	})
  1798  	defer execStatements(t, []string{
  1799  		"drop database external",
  1800  	})
  1801  	engine.se.Reload(context.Background())
  1802  
  1803  	testcases := []testcase{{
  1804  		input: []string{
  1805  			"begin",
  1806  			"insert into external.ext values (1, 'aaa')",
  1807  			"commit",
  1808  		},
  1809  		// External table events don't get sent.
  1810  		output: [][]string{{
  1811  			`begin`,
  1812  			`gtid`,
  1813  			`commit`,
  1814  		}},
  1815  	}}
  1816  	runCases(t, nil, testcases, "", nil)
  1817  }
  1818  
  1819  func TestJournal(t *testing.T) {
  1820  	if testing.Short() {
  1821  		t.Skip()
  1822  	}
  1823  
  1824  	execStatements(t, []string{
  1825  		"create table if not exists _vt.resharding_journal(id int, db_name varchar(128), val blob, primary key(id))",
  1826  	})
  1827  	defer execStatements(t, []string{
  1828  		"drop table _vt.resharding_journal",
  1829  	})
  1830  	engine.se.Reload(context.Background())
  1831  
  1832  	journal1 := &binlogdatapb.Journal{
  1833  		Id:            1,
  1834  		MigrationType: binlogdatapb.MigrationType_SHARDS,
  1835  	}
  1836  	journal2 := &binlogdatapb.Journal{
  1837  		Id:            2,
  1838  		MigrationType: binlogdatapb.MigrationType_SHARDS,
  1839  	}
  1840  	testcases := []testcase{{
  1841  		input: []string{
  1842  			"begin",
  1843  			fmt.Sprintf("insert into _vt.resharding_journal values(1, 'vttest', '%v')", journal1.String()),
  1844  			fmt.Sprintf("insert into _vt.resharding_journal values(2, 'nosend', '%v')", journal2.String()),
  1845  			"commit",
  1846  		},
  1847  		// External table events don't get sent.
  1848  		output: [][]string{{
  1849  			`begin`,
  1850  			`type:JOURNAL journal:{id:1 migration_type:SHARDS}`,
  1851  			`gtid`,
  1852  			`commit`,
  1853  		}},
  1854  	}}
  1855  	runCases(t, nil, testcases, "", nil)
  1856  }
  1857  
  1858  func TestMinimalMode(t *testing.T) {
  1859  	if testing.Short() {
  1860  		t.Skip()
  1861  	}
  1862  
  1863  	execStatements(t, []string{
  1864  		"create table t1(id int, val1 varbinary(128), val2 varbinary(128), primary key(id))",
  1865  		"insert into t1 values(1, 'aaa', 'bbb')",
  1866  	})
  1867  	defer execStatements(t, []string{
  1868  		"drop table t1",
  1869  	})
  1870  	engine.se.Reload(context.Background())
  1871  
  1872  	// Record position before the next few statements.
  1873  	pos := primaryPosition(t)
  1874  	execStatements(t, []string{
  1875  		"set @@session.binlog_row_image='minimal'",
  1876  		"update t1 set val1='bbb' where id=1",
  1877  		"set @@session.binlog_row_image='full'",
  1878  	})
  1879  
  1880  	ctx, cancel := context.WithCancel(context.Background())
  1881  	defer cancel()
  1882  
  1883  	ch := make(chan []*binlogdatapb.VEvent)
  1884  	go func() {
  1885  		for evs := range ch {
  1886  			t.Errorf("received: %v", evs)
  1887  		}
  1888  	}()
  1889  	defer close(ch)
  1890  	err := vstream(ctx, t, pos, nil, nil, ch)
  1891  	want := "partial row image encountered"
  1892  	if err == nil || !strings.Contains(err.Error(), want) {
  1893  		t.Errorf("err: %v, must contain '%s'", err, want)
  1894  	}
  1895  }
  1896  
  1897  func TestStatementMode(t *testing.T) {
  1898  	if testing.Short() {
  1899  		t.Skip()
  1900  	}
  1901  	execStatements(t, []string{
  1902  		"create table stream1(id int, val varbinary(128), primary key(id))",
  1903  		"create table stream2(id int, val varbinary(128), primary key(id))",
  1904  	})
  1905  
  1906  	engine.se.Reload(context.Background())
  1907  
  1908  	defer execStatements(t, []string{
  1909  		"drop table stream1",
  1910  		"drop table stream2",
  1911  	})
  1912  
  1913  	testcases := []testcase{{
  1914  		input: []string{
  1915  			"set @@session.binlog_format='STATEMENT'",
  1916  			"begin",
  1917  			"insert into stream1 values (1, 'aaa')",
  1918  			"update stream1 set val='bbb' where id = 1",
  1919  			"delete from stream1 where id = 1",
  1920  			"commit",
  1921  			"set @@session.binlog_format='ROW'",
  1922  		},
  1923  		output: [][]string{{
  1924  			`begin`,
  1925  			`type:INSERT dml:"insert into stream1 values (1, 'aaa')"`,
  1926  			`type:UPDATE dml:"update stream1 set val='bbb' where id = 1"`,
  1927  			`type:DELETE dml:"delete from stream1 where id = 1"`,
  1928  			`gtid`,
  1929  			`commit`,
  1930  		}},
  1931  	}}
  1932  	runCases(t, nil, testcases, "", nil)
  1933  }
  1934  
  1935  func TestHeartbeat(t *testing.T) {
  1936  	if testing.Short() {
  1937  		t.Skip()
  1938  	}
  1939  
  1940  	ctx, cancel := context.WithCancel(context.Background())
  1941  	defer cancel()
  1942  
  1943  	wg, ch := startStream(ctx, t, nil, "", nil)
  1944  	defer wg.Wait()
  1945  	evs := <-ch
  1946  	require.Equal(t, 1, len(evs))
  1947  	assert.Equal(t, binlogdatapb.VEventType_HEARTBEAT, evs[0].Type)
  1948  	cancel()
  1949  }
  1950  
  1951  func TestNoFutureGTID(t *testing.T) {
  1952  	if testing.Short() {
  1953  		t.Skip()
  1954  	}
  1955  
  1956  	// Execute something to make sure we have ranges in GTIDs.
  1957  	execStatements(t, []string{
  1958  		"create table stream1(id int, val varbinary(128), primary key(id))",
  1959  	})
  1960  	defer execStatements(t, []string{
  1961  		"drop table stream1",
  1962  	})
  1963  	engine.se.Reload(context.Background())
  1964  
  1965  	pos := primaryPosition(t)
  1966  	t.Logf("current position: %v", pos)
  1967  	// Both mysql and mariadb have '-' in their gtids.
  1968  	// Invent a GTID in the future.
  1969  	index := strings.LastIndexByte(pos, '-')
  1970  	num, err := strconv.Atoi(pos[index+1:])
  1971  	require.NoError(t, err)
  1972  	future := pos[:index+1] + fmt.Sprintf("%d", num+1)
  1973  	t.Logf("future position: %v", future)
  1974  
  1975  	ctx, cancel := context.WithCancel(context.Background())
  1976  	defer cancel()
  1977  
  1978  	ch := make(chan []*binlogdatapb.VEvent)
  1979  	go func() {
  1980  		for range ch {
  1981  		}
  1982  	}()
  1983  	defer close(ch)
  1984  	err = vstream(ctx, t, future, nil, nil, ch)
  1985  	want := "GTIDSet Mismatch"
  1986  	if err == nil || !strings.Contains(err.Error(), want) {
  1987  		t.Errorf("err: %v, must contain %s", err, want)
  1988  	}
  1989  }
  1990  
  1991  func TestFilteredMultipleWhere(t *testing.T) {
  1992  	if testing.Short() {
  1993  		t.Skip()
  1994  	}
  1995  
  1996  	execStatements(t, []string{
  1997  		"create table t1(id1 int, id2 int, id3 int, val varbinary(128), primary key(id1))",
  1998  	})
  1999  	defer execStatements(t, []string{
  2000  		"drop table t1",
  2001  	})
  2002  	engine.se.Reload(context.Background())
  2003  
  2004  	setVSchema(t, shardedVSchema)
  2005  	defer env.SetVSchema("{}")
  2006  
  2007  	filter := &binlogdatapb.Filter{
  2008  		Rules: []*binlogdatapb.Rule{{
  2009  			Match:  "t1",
  2010  			Filter: "select id1, val from t1 where in_keyrange('-80') and id2 = 200 and id3 = 1000 and val = 'newton'",
  2011  		}},
  2012  	}
  2013  
  2014  	testcases := []testcase{{
  2015  		input: []string{
  2016  			"begin",
  2017  			"insert into t1 values (1, 100, 1000, 'kepler')",
  2018  			"insert into t1 values (2, 200, 1000, 'newton')",
  2019  			"insert into t1 values (3, 100, 2000, 'kepler')",
  2020  			"insert into t1 values (128, 200, 1000, 'newton')",
  2021  			"insert into t1 values (5, 200, 2000, 'kepler')",
  2022  			"insert into t1 values (129, 200, 1000, 'kepler')",
  2023  			"commit",
  2024  		},
  2025  		output: [][]string{{
  2026  			`begin`,
  2027  			`type:FIELD field_event:{table_name:"t1" fields:{name:"id1" type:INT32 table:"t1" org_table:"t1" database:"vttest" org_name:"id1" column_length:11 charset:63 column_type:"int(11)"} fields:{name:"val" type:VARBINARY table:"t1" org_table:"t1" database:"vttest" org_name:"val" column_length:128 charset:63 column_type:"varbinary(128)"}}`,
  2028  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:1 lengths:6 values:"2newton"}}}`,
  2029  			`type:ROW row_event:{table_name:"t1" row_changes:{after:{lengths:3 lengths:6 values:"128newton"}}}`,
  2030  			`gtid`,
  2031  			`commit`,
  2032  		}},
  2033  	}}
  2034  	runCases(t, filter, testcases, "", nil)
  2035  }
  2036  
  2037  // TestGeneratedColumns just confirms that generated columns are sent in a vstream as expected
  2038  func TestGeneratedColumns(t *testing.T) {
  2039  	flavor := strings.ToLower(env.Flavor)
  2040  	// Disable tests on percona (which identifies as mysql56) and mariadb platforms in CI since they
  2041  	// generated columns support was added in 5.7 and mariadb added mysql compatible generated columns in 10.2
  2042  	if !strings.Contains(flavor, "mysql57") && !strings.Contains(flavor, "mysql80") {
  2043  		return
  2044  	}
  2045  	execStatements(t, []string{
  2046  		"create table t1(id int, val varbinary(6), val2 varbinary(6) as (concat(id, val)), val3 varbinary(6) as (concat(val, id)), id2 int, primary key(id))",
  2047  	})
  2048  	defer execStatements(t, []string{
  2049  		"drop table t1",
  2050  	})
  2051  	engine.se.Reload(context.Background())
  2052  	queries := []string{
  2053  		"begin",
  2054  		"insert into t1(id, val, id2) values (1, 'aaa', 10)",
  2055  		"insert into t1(id, val, id2) values (2, 'bbb', 20)",
  2056  		"commit",
  2057  	}
  2058  
  2059  	fe := &TestFieldEvent{
  2060  		table: "t1",
  2061  		db:    "vttest",
  2062  		cols: []*TestColumn{
  2063  			{name: "id", dataType: "INT32", colType: "", len: 11, charset: 63},
  2064  			{name: "val", dataType: "VARBINARY", colType: "", len: 6, charset: 63},
  2065  			{name: "val2", dataType: "VARBINARY", colType: "", len: 6, charset: 63},
  2066  			{name: "val3", dataType: "VARBINARY", colType: "", len: 6, charset: 63},
  2067  			{name: "id2", dataType: "INT32", colType: "", len: 11, charset: 63},
  2068  		},
  2069  	}
  2070  
  2071  	testcases := []testcase{{
  2072  		input: queries,
  2073  		output: [][]string{{
  2074  			`begin`,
  2075  			fe.String(),
  2076  			`type:ROW row_event:<table_name:"t1" row_changes:<after:<lengths:1 lengths:3 lengths:4 lengths:4 lengths:2 values:"1aaa1aaaaaa110" > > > `,
  2077  			`type:ROW row_event:<table_name:"t1" row_changes:<after:<lengths:1 lengths:3 lengths:4 lengths:4 lengths:2 values:"2bbb2bbbbbb220" > > > `,
  2078  			`gtid`,
  2079  			`commit`,
  2080  		}},
  2081  	}}
  2082  	runCases(t, nil, testcases, "current", nil)
  2083  }
  2084  
  2085  func runCases(t *testing.T, filter *binlogdatapb.Filter, testcases []testcase, position string, tablePK []*binlogdatapb.TableLastPK) {
  2086  
  2087  	ctx, cancel := context.WithCancel(context.Background())
  2088  	defer cancel()
  2089  	wg, ch := startStream(ctx, t, filter, position, tablePK)
  2090  	defer wg.Wait()
  2091  	// If position is 'current', we wait for a heartbeat to be
  2092  	// sure the vstreamer has started.
  2093  	if position == "current" {
  2094  		log.Infof("Starting stream with current position")
  2095  		expectLog(ctx, t, "current pos", ch, [][]string{{`gtid`, `type:OTHER`}})
  2096  	}
  2097  
  2098  	log.Infof("Starting to run test cases")
  2099  	for _, tcase := range testcases {
  2100  		switch input := tcase.input.(type) {
  2101  		case []string:
  2102  			execStatements(t, input)
  2103  		case string:
  2104  			execStatement(t, input)
  2105  		default:
  2106  			t.Fatalf("unexpected input: %#v", input)
  2107  		}
  2108  		engine.se.Reload(ctx)
  2109  		expectLog(ctx, t, tcase.input, ch, tcase.output)
  2110  	}
  2111  
  2112  	cancel()
  2113  	if evs, ok := <-ch; ok {
  2114  		t.Fatalf("unexpected evs: %v", evs)
  2115  	}
  2116  	log.Infof("Last line of runCases")
  2117  }
  2118  
  2119  func expectLog(ctx context.Context, t *testing.T, input any, ch <-chan []*binlogdatapb.VEvent, output [][]string) {
  2120  	timer := time.NewTimer(1 * time.Minute)
  2121  	defer timer.Stop()
  2122  	for _, wantset := range output {
  2123  		var evs []*binlogdatapb.VEvent
  2124  		for {
  2125  			select {
  2126  			case allevs, ok := <-ch:
  2127  				if !ok {
  2128  					t.Fatal("expectLog: not ok, stream ended early")
  2129  				}
  2130  				for _, ev := range allevs {
  2131  					// Ignore spurious heartbeats that can happen on slow machines.
  2132  					if ev.Type == binlogdatapb.VEventType_HEARTBEAT {
  2133  						continue
  2134  					}
  2135  					if ev.Throttled {
  2136  						continue
  2137  					}
  2138  					evs = append(evs, ev)
  2139  				}
  2140  			case <-ctx.Done():
  2141  				t.Fatalf("expectLog: Done(), stream ended early")
  2142  			case <-timer.C:
  2143  				t.Fatalf("expectLog: timed out waiting for events: %v", wantset)
  2144  			}
  2145  			if len(evs) != 0 {
  2146  				break
  2147  			}
  2148  		}
  2149  		if len(wantset) != len(evs) {
  2150  			t.Fatalf("%v: evs\n%v, want\n%v, >> got length %d, wanted length %d", input, evs, wantset, len(evs), len(wantset))
  2151  		}
  2152  		for i, want := range wantset {
  2153  			// CurrentTime is not testable.
  2154  			evs[i].CurrentTime = 0
  2155  			evs[i].Keyspace = ""
  2156  			evs[i].Shard = ""
  2157  			switch want {
  2158  			case "begin":
  2159  				if evs[i].Type != binlogdatapb.VEventType_BEGIN {
  2160  					t.Fatalf("%v (%d): event: %v, want gtid or begin", input, i, evs[i])
  2161  				}
  2162  			case "gtid":
  2163  				if evs[i].Type != binlogdatapb.VEventType_GTID {
  2164  					t.Fatalf("%v (%d): event: %v, want gtid", input, i, evs[i])
  2165  				}
  2166  			case "lastpk":
  2167  				if evs[i].Type != binlogdatapb.VEventType_LASTPK {
  2168  					t.Fatalf("%v (%d): event: %v, want lastpk", input, i, evs[i])
  2169  				}
  2170  			case "commit":
  2171  				if evs[i].Type != binlogdatapb.VEventType_COMMIT {
  2172  					t.Fatalf("%v (%d): event: %v, want commit", input, i, evs[i])
  2173  				}
  2174  			case "other":
  2175  				if evs[i].Type != binlogdatapb.VEventType_OTHER {
  2176  					t.Fatalf("%v (%d): event: %v, want other", input, i, evs[i])
  2177  				}
  2178  			case "ddl":
  2179  				if evs[i].Type != binlogdatapb.VEventType_DDL {
  2180  					t.Fatalf("%v (%d): event: %v, want ddl", input, i, evs[i])
  2181  				}
  2182  			case "copy_completed":
  2183  				if evs[i].Type != binlogdatapb.VEventType_COPY_COMPLETED {
  2184  					t.Fatalf("%v (%d): event: %v, want copy_completed", input, i, evs[i])
  2185  				}
  2186  			default:
  2187  				evs[i].Timestamp = 0
  2188  				if evs[i].Type == binlogdatapb.VEventType_FIELD {
  2189  					for j := range evs[i].FieldEvent.Fields {
  2190  						evs[i].FieldEvent.Fields[j].Flags = 0
  2191  						if ignoreKeyspaceShardInFieldAndRowEvents {
  2192  							evs[i].FieldEvent.Keyspace = ""
  2193  							evs[i].FieldEvent.Shard = ""
  2194  						}
  2195  					}
  2196  				}
  2197  				if ignoreKeyspaceShardInFieldAndRowEvents && evs[i].Type == binlogdatapb.VEventType_ROW {
  2198  					evs[i].RowEvent.Keyspace = ""
  2199  					evs[i].RowEvent.Shard = ""
  2200  				}
  2201  				want = env.RemoveAnyDeprecatedDisplayWidths(want)
  2202  				if got := fmt.Sprintf("%v", evs[i]); got != want {
  2203  					log.Errorf("%v (%d): event:\n%q, want\n%q", input, i, got, want)
  2204  					t.Fatalf("%v (%d): event:\n%q, want\n%q", input, i, got, want)
  2205  				}
  2206  			}
  2207  		}
  2208  	}
  2209  }
  2210  
  2211  func startStream(ctx context.Context, t *testing.T, filter *binlogdatapb.Filter, position string, tablePKs []*binlogdatapb.TableLastPK) (*sync.WaitGroup, <-chan []*binlogdatapb.VEvent) {
  2212  	switch position {
  2213  	case "":
  2214  		position = primaryPosition(t)
  2215  	case "vscopy":
  2216  		position = ""
  2217  	}
  2218  
  2219  	wg := sync.WaitGroup{}
  2220  	wg.Add(1)
  2221  	ch := make(chan []*binlogdatapb.VEvent)
  2222  
  2223  	go func() {
  2224  		defer close(ch)
  2225  		defer wg.Done()
  2226  		vstream(ctx, t, position, tablePKs, filter, ch)
  2227  	}()
  2228  	return &wg, ch
  2229  }
  2230  
  2231  func vstream(ctx context.Context, t *testing.T, pos string, tablePKs []*binlogdatapb.TableLastPK, filter *binlogdatapb.Filter, ch chan []*binlogdatapb.VEvent) error {
  2232  	if filter == nil {
  2233  		filter = &binlogdatapb.Filter{
  2234  			Rules: []*binlogdatapb.Rule{{
  2235  				Match: "/.*/",
  2236  			}},
  2237  		}
  2238  	}
  2239  	return engine.Stream(ctx, pos, tablePKs, filter, func(evs []*binlogdatapb.VEvent) error {
  2240  		timer := time.NewTimer(2 * time.Second)
  2241  		defer timer.Stop()
  2242  
  2243  		t.Logf("Received events: %v", evs)
  2244  		select {
  2245  		case ch <- evs:
  2246  		case <-ctx.Done():
  2247  			return fmt.Errorf("engine.Stream Done() stream ended early")
  2248  		case <-timer.C:
  2249  			t.Log("VStream timed out waiting for events")
  2250  			return io.EOF
  2251  		}
  2252  		return nil
  2253  	})
  2254  }
  2255  
  2256  func execStatement(t *testing.T, query string) {
  2257  	t.Helper()
  2258  	if err := env.Mysqld.ExecuteSuperQuery(context.Background(), query); err != nil {
  2259  		t.Fatal(err)
  2260  	}
  2261  }
  2262  
  2263  func execStatements(t *testing.T, queries []string) {
  2264  	if err := env.Mysqld.ExecuteSuperQueryList(context.Background(), queries); err != nil {
  2265  		t.Fatal(err)
  2266  	}
  2267  }
  2268  
  2269  func primaryPosition(t *testing.T) string {
  2270  	t.Helper()
  2271  	// We use the engine's cp because there is one test that overrides
  2272  	// the flavor to FilePos. If so, we have to obtain the position
  2273  	// in that flavor format.
  2274  	connParam, err := engine.env.Config().DB.DbaWithDB().MysqlParams()
  2275  	if err != nil {
  2276  		t.Fatal(err)
  2277  	}
  2278  	conn, err := mysql.Connect(context.Background(), connParam)
  2279  	if err != nil {
  2280  		t.Fatal(err)
  2281  	}
  2282  	defer conn.Close()
  2283  	pos, err := conn.PrimaryPosition()
  2284  	if err != nil {
  2285  		t.Fatal(err)
  2286  	}
  2287  	return mysql.EncodePosition(pos)
  2288  }
  2289  
  2290  func setVSchema(t *testing.T, vschema string) {
  2291  	t.Helper()
  2292  
  2293  	curCount := engine.vschemaUpdates.Get()
  2294  	if err := env.SetVSchema(vschema); err != nil {
  2295  		t.Fatal(err)
  2296  	}
  2297  	// Wait for curCount to go up.
  2298  	updated := false
  2299  	for i := 0; i < 10; i++ {
  2300  		if engine.vschemaUpdates.Get() != curCount {
  2301  			updated = true
  2302  			break
  2303  		}
  2304  		time.Sleep(10 * time.Millisecond)
  2305  	}
  2306  	if !updated {
  2307  		log.Infof("vschema did not get updated")
  2308  		t.Error("vschema did not get updated")
  2309  	}
  2310  }