vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletmanager/vreplication/vplayer_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 vreplication
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math"
    23  	"os"
    24  	"strconv"
    25  	"strings"
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"vitess.io/vitess/go/mysql"
    31  
    32  	"github.com/spyzhov/ajson"
    33  	"github.com/stretchr/testify/require"
    34  
    35  	"vitess.io/vitess/go/vt/log"
    36  
    37  	"vitess.io/vitess/go/sqltypes"
    38  	"vitess.io/vitess/go/vt/binlog/binlogplayer"
    39  
    40  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    41  	qh "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication/queryhistory"
    42  )
    43  
    44  func TestPlayerInvisibleColumns(t *testing.T) {
    45  	if !supportsInvisibleColumns() {
    46  		t.Skip()
    47  	}
    48  	defer deleteTablet(addTablet(100))
    49  
    50  	execStatements(t, []string{
    51  		"create table t1(id int, val varchar(20), id2 int invisible, pk2 int invisible, primary key(id, pk2))",
    52  		fmt.Sprintf("create table %s.t1(id int, val varchar(20), id2 int invisible, pk2 int invisible, primary key(id, pk2))", vrepldb),
    53  	})
    54  	defer execStatements(t, []string{
    55  		"drop table t1",
    56  		fmt.Sprintf("drop table %s.t1", vrepldb),
    57  	})
    58  	env.SchemaEngine.Reload(context.Background())
    59  
    60  	filter := &binlogdatapb.Filter{
    61  		Rules: []*binlogdatapb.Rule{{
    62  			Match:  "t1",
    63  			Filter: "select * from t1",
    64  		}},
    65  	}
    66  	bls := &binlogdatapb.BinlogSource{
    67  		Keyspace: env.KeyspaceName,
    68  		Shard:    env.ShardName,
    69  		Filter:   filter,
    70  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
    71  	}
    72  	cancel, _ := startVReplication(t, bls, "")
    73  	defer cancel()
    74  
    75  	testcases := []struct {
    76  		input       string
    77  		output      string
    78  		table       string
    79  		data        [][]string
    80  		query       string
    81  		queryResult [][]string
    82  	}{{
    83  		input:  "insert into t1(id,val,id2,pk2) values (1,'aaa',10,100)",
    84  		output: "insert into t1(id,val,id2,pk2) values (1,'aaa',10,100)",
    85  		table:  "t1",
    86  		data: [][]string{
    87  			{"1", "aaa"},
    88  		},
    89  		query: "select id, val, id2, pk2 from t1",
    90  		queryResult: [][]string{
    91  			{"1", "aaa", "10", "100"},
    92  		},
    93  	}}
    94  
    95  	for _, tcases := range testcases {
    96  		execStatements(t, []string{tcases.input})
    97  		output := qh.Expect(tcases.output)
    98  		expectNontxQueries(t, output)
    99  		time.Sleep(1 * time.Second)
   100  		log.Flush()
   101  		if tcases.table != "" {
   102  			expectData(t, tcases.table, tcases.data)
   103  		}
   104  		if tcases.query != "" {
   105  			expectQueryResult(t, tcases.query, tcases.queryResult)
   106  		}
   107  	}
   108  }
   109  
   110  func TestHeartbeatFrequencyFlag(t *testing.T) {
   111  	origVReplicationHeartbeatUpdateInterval := vreplicationHeartbeatUpdateInterval
   112  	defer func() {
   113  		vreplicationHeartbeatUpdateInterval = origVReplicationHeartbeatUpdateInterval
   114  	}()
   115  
   116  	stats := binlogplayer.NewStats()
   117  	vp := &vplayer{vr: &vreplicator{dbClient: newVDBClient(realDBClientFactory(), stats), stats: stats}}
   118  
   119  	type testcount struct {
   120  		count      int
   121  		mustUpdate bool
   122  	}
   123  	type testcase struct {
   124  		name     string
   125  		interval int
   126  		counts   []testcount
   127  	}
   128  	testcases := []*testcase{
   129  		{"default frequency", 1, []testcount{{count: 0, mustUpdate: false}, {1, true}}},
   130  		{"custom frequency", 4, []testcount{{count: 0, mustUpdate: false}, {count: 3, mustUpdate: false}, {4, true}}},
   131  		{"minumum frequency", 61, []testcount{{count: 59, mustUpdate: false}, {count: 60, mustUpdate: true}, {61, true}}},
   132  	}
   133  	for _, tcase := range testcases {
   134  		t.Run(tcase.name, func(t *testing.T) {
   135  			vreplicationHeartbeatUpdateInterval = tcase.interval
   136  			for _, tcount := range tcase.counts {
   137  				vp.numAccumulatedHeartbeats = tcount.count
   138  				require.Equal(t, tcount.mustUpdate, vp.mustUpdateHeartbeat())
   139  			}
   140  		})
   141  	}
   142  }
   143  
   144  func TestVReplicationTimeUpdated(t *testing.T) {
   145  	ctx := context.Background()
   146  	defer deleteTablet(addTablet(100))
   147  	execStatements(t, []string{
   148  		"create table t1(id int, val varbinary(128), primary key(id))",
   149  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
   150  	})
   151  	defer execStatements(t, []string{
   152  		"drop table t1",
   153  		fmt.Sprintf("drop table %s.t1", vrepldb),
   154  	})
   155  	env.SchemaEngine.Reload(context.Background())
   156  
   157  	filter := &binlogdatapb.Filter{
   158  		Rules: []*binlogdatapb.Rule{{
   159  			Match: "/.*",
   160  		}},
   161  	}
   162  	bls := &binlogdatapb.BinlogSource{
   163  		Keyspace: env.KeyspaceName,
   164  		Shard:    env.ShardName,
   165  		Filter:   filter,
   166  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   167  	}
   168  	cancel, _ := startVReplication(t, bls, "")
   169  	defer cancel()
   170  
   171  	execStatements(t, []string{
   172  		"insert into t1 values(1, 'aaa')",
   173  	})
   174  
   175  	var getTimestamps = func() (int64, int64, int64) {
   176  		qr, err := env.Mysqld.FetchSuperQuery(ctx, "select time_updated, transaction_timestamp, time_heartbeat from _vt.vreplication")
   177  		require.NoError(t, err)
   178  		require.NotNil(t, qr)
   179  		require.Equal(t, 1, len(qr.Rows))
   180  		row := qr.Named().Row()
   181  		timeUpdated, err := row.ToInt64("time_updated")
   182  		require.NoError(t, err)
   183  		transactionTimestamp, err := row.ToInt64("transaction_timestamp")
   184  		require.NoError(t, err)
   185  		timeHeartbeat, err := row.ToInt64("time_heartbeat")
   186  		require.NoError(t, err)
   187  		return timeUpdated, transactionTimestamp, timeHeartbeat
   188  	}
   189  	expectNontxQueries(t, qh.Expect("insert into t1(id,val) values (1,'aaa')"))
   190  	time.Sleep(1 * time.Second)
   191  	timeUpdated1, transactionTimestamp1, timeHeartbeat1 := getTimestamps()
   192  	time.Sleep(2 * time.Second)
   193  	timeUpdated2, _, timeHeartbeat2 := getTimestamps()
   194  	require.Greater(t, timeUpdated2, timeUpdated1, "time_updated not updated")
   195  	require.Greater(t, timeUpdated2, transactionTimestamp1, "transaction_timestamp should not be < time_updated")
   196  	require.Greater(t, timeHeartbeat2, timeHeartbeat1, "time_heartbeat not updated")
   197  }
   198  
   199  func TestCharPK(t *testing.T) {
   200  	defer deleteTablet(addTablet(100))
   201  
   202  	execStatements(t, []string{
   203  		"create table t1(id int, val binary(2), primary key(val))",
   204  		fmt.Sprintf("create table %s.t1(id int, val binary(2), primary key(val))", vrepldb),
   205  		"create table t2(id int, val char(2), primary key(val))",
   206  		fmt.Sprintf("create table %s.t2(id int, val char(2), primary key(val))", vrepldb),
   207  		"create table t3(id int, val varbinary(2), primary key(val))",
   208  		fmt.Sprintf("create table %s.t3(id int, val varbinary(2), primary key(val))", vrepldb),
   209  		"create table t4(id int, val varchar(2), primary key(val))",
   210  		fmt.Sprintf("create table %s.t4(id int, val varchar(2), primary key(val))", vrepldb),
   211  	})
   212  	defer execStatements(t, []string{
   213  		"drop table t1",
   214  		fmt.Sprintf("drop table %s.t1", vrepldb),
   215  		"drop table t2",
   216  		fmt.Sprintf("drop table %s.t2", vrepldb),
   217  		"drop table t3",
   218  		fmt.Sprintf("drop table %s.t3", vrepldb),
   219  		"drop table t4",
   220  		fmt.Sprintf("drop table %s.t4", vrepldb),
   221  	})
   222  	env.SchemaEngine.Reload(context.Background())
   223  
   224  	filter := &binlogdatapb.Filter{
   225  		Rules: []*binlogdatapb.Rule{{
   226  			Match:  "t1",
   227  			Filter: "select * from t1",
   228  		}, {
   229  			Match:  "t2",
   230  			Filter: "select * from t2",
   231  		}, {
   232  			Match:  "t3",
   233  			Filter: "select * from t3",
   234  		}, {
   235  			Match:  "t4",
   236  			Filter: "select * from t4",
   237  		}},
   238  	}
   239  	bls := &binlogdatapb.BinlogSource{
   240  		Keyspace: env.KeyspaceName,
   241  		Shard:    env.ShardName,
   242  		Filter:   filter,
   243  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   244  	}
   245  	cancel, _ := startVReplication(t, bls, "")
   246  	defer cancel()
   247  
   248  	testcases := []struct {
   249  		input  string
   250  		output string
   251  		table  string
   252  		data   [][]string
   253  	}{{ //binary(2)
   254  		input:  "insert into t1 values(1, 'a')",
   255  		output: "insert into t1(id,val) values (1,'a\\0')",
   256  		table:  "t1",
   257  		data: [][]string{
   258  			{"1", "a\000"},
   259  		},
   260  	}, {
   261  		input:  "update t1 set id = 2 where val = 'a\000'",
   262  		output: "update t1 set id=2 where val='a\\0'",
   263  		table:  "t1",
   264  		data: [][]string{
   265  			{"2", "a\000"},
   266  		},
   267  	}, { //char(2)
   268  		input:  "insert into t2 values(1, 'a')",
   269  		output: "insert into t2(id,val) values (1,'a')",
   270  		table:  "t2",
   271  		data: [][]string{
   272  			{"1", "a"},
   273  		},
   274  	}, {
   275  		input:  "update t2 set id = 2 where val = 'a'",
   276  		output: "update t2 set id=2 where val='a'",
   277  		table:  "t2",
   278  		data: [][]string{
   279  			{"2", "a"},
   280  		},
   281  	}, { //varbinary(2)
   282  		input:  "insert into t3 values(1, 'a')",
   283  		output: "insert into t3(id,val) values (1,'a')",
   284  		table:  "t3",
   285  		data: [][]string{
   286  			{"1", "a"},
   287  		},
   288  	}, {
   289  		input:  "update t3 set id = 2 where val = 'a'",
   290  		output: "update t3 set id=2 where val='a'",
   291  		table:  "t3",
   292  		data: [][]string{
   293  			{"2", "a"},
   294  		},
   295  	}, { //varchar(2)
   296  		input:  "insert into t4 values(1, 'a')",
   297  		output: "insert into t4(id,val) values (1,'a')",
   298  		table:  "t4",
   299  		data: [][]string{
   300  			{"1", "a"},
   301  		},
   302  	}, {
   303  		input:  "update t4 set id = 2 where val = 'a'",
   304  		output: "update t4 set id=2 where val='a'",
   305  		table:  "t4",
   306  		data: [][]string{
   307  			{"2", "a"},
   308  		},
   309  	}}
   310  
   311  	for _, tcases := range testcases {
   312  		execStatements(t, []string{tcases.input})
   313  		output := qh.Expect(
   314  			"begin",
   315  			tcases.output,
   316  			"/update _vt.vreplication set pos",
   317  			"commit",
   318  		)
   319  		expectDBClientQueries(t, output)
   320  		if tcases.table != "" {
   321  			expectData(t, tcases.table, tcases.data)
   322  		}
   323  	}
   324  }
   325  
   326  func TestRollup(t *testing.T) {
   327  	defer deleteTablet(addTablet(100))
   328  
   329  	execStatements(t, []string{
   330  		"create table t1(id int, val varchar(20), primary key(id))",
   331  		fmt.Sprintf("create table %s.t1(rollupname varchar(20), kount int, primary key(rollupname))", vrepldb),
   332  	})
   333  	defer execStatements(t, []string{
   334  		"drop table t1",
   335  		fmt.Sprintf("drop table %s.t1", vrepldb),
   336  	})
   337  	env.SchemaEngine.Reload(context.Background())
   338  
   339  	filter := &binlogdatapb.Filter{
   340  		Rules: []*binlogdatapb.Rule{{
   341  			Match:  "t1",
   342  			Filter: "select 'total' as rollupname, count(*) as kount from t1 group by rollupname",
   343  		}},
   344  	}
   345  	bls := &binlogdatapb.BinlogSource{
   346  		Keyspace: env.KeyspaceName,
   347  		Shard:    env.ShardName,
   348  		Filter:   filter,
   349  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   350  	}
   351  	cancel, _ := startVReplication(t, bls, "")
   352  	defer cancel()
   353  
   354  	testcases := []struct {
   355  		input  string
   356  		output string
   357  		table  string
   358  		data   [][]string
   359  	}{{
   360  		// Start with all nulls
   361  		input:  "insert into t1 values(1, 'a')",
   362  		output: "insert into t1(rollupname,kount) values ('total',1) on duplicate key update kount=kount+1",
   363  		table:  "t1",
   364  		data: [][]string{
   365  			{"total", "1"},
   366  		},
   367  	}}
   368  
   369  	for _, tcases := range testcases {
   370  		execStatements(t, []string{tcases.input})
   371  		output := qh.Expect(
   372  			"begin",
   373  			tcases.output,
   374  			"/update _vt.vreplication set pos",
   375  			"commit",
   376  		)
   377  		expectDBClientQueries(t, output)
   378  		if tcases.table != "" {
   379  			expectData(t, tcases.table, tcases.data)
   380  		}
   381  	}
   382  }
   383  
   384  func TestPlayerSavepoint(t *testing.T) {
   385  	defer deleteTablet(addTablet(100))
   386  	execStatements(t, []string{
   387  		"create table t1(id int, primary key(id))",
   388  		fmt.Sprintf("create table %s.t1(id int, primary key(id))", vrepldb),
   389  	})
   390  	defer execStatements(t, []string{
   391  		"drop table t1",
   392  		fmt.Sprintf("drop table %s.t1", vrepldb),
   393  	})
   394  	env.SchemaEngine.Reload(context.Background())
   395  
   396  	filter := &binlogdatapb.Filter{
   397  		Rules: []*binlogdatapb.Rule{{
   398  			Match: "/.*",
   399  		}},
   400  	}
   401  	bls := &binlogdatapb.BinlogSource{
   402  		Keyspace: env.KeyspaceName,
   403  		Shard:    env.ShardName,
   404  		Filter:   filter,
   405  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   406  	}
   407  	cancel, _ := startVReplication(t, bls, "")
   408  	// Issue a dummy change to ensure vreplication is initialized. Otherwise there
   409  	// is a race between the DDLs and the schema loader of vstreamer.
   410  	// Root cause seems to be with MySQL where t1 shows up in information_schema before
   411  	// the actual table is created.
   412  	execStatements(t, []string{"insert into t1 values(1)"})
   413  	expectDBClientQueries(t, qh.Expect(
   414  		"begin",
   415  		"insert into t1(id) values (1)",
   416  		"/update _vt.vreplication set pos=",
   417  		"commit",
   418  	))
   419  
   420  	execStatements(t, []string{
   421  		"begin",
   422  		"savepoint vrepl_a",
   423  		"insert into t1(id) values (2)",
   424  		"savepoint vrepl_b",
   425  		"insert into t1(id) values (3)",
   426  		"release savepoint vrepl_b",
   427  		"savepoint vrepl_a",
   428  		"insert into t1(id) values (42)",
   429  		"rollback work to savepoint vrepl_a",
   430  		"commit",
   431  	})
   432  	expectDBClientQueries(t, qh.Expect(
   433  		"begin",
   434  		"/insert into t1.*2.*",
   435  		"/insert into t1.*3.*",
   436  		"/update _vt.vreplication set pos=",
   437  		"commit",
   438  	))
   439  	cancel()
   440  }
   441  
   442  func TestPlayerStatementModeWithFilter(t *testing.T) {
   443  	defer deleteTablet(addTablet(100))
   444  
   445  	execStatements(t, []string{
   446  		"create table src1(id int, val varbinary(128), primary key(id))",
   447  	})
   448  	defer execStatements(t, []string{
   449  		"drop table src1",
   450  	})
   451  	env.SchemaEngine.Reload(context.Background())
   452  
   453  	filter := &binlogdatapb.Filter{
   454  		Rules: []*binlogdatapb.Rule{{
   455  			Match:  "dst1",
   456  			Filter: "select * from src1",
   457  		}},
   458  	}
   459  	bls := &binlogdatapb.BinlogSource{
   460  		Keyspace: env.KeyspaceName,
   461  		Shard:    env.ShardName,
   462  		Filter:   filter,
   463  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   464  	}
   465  	cancel, _ := startVReplication(t, bls, "")
   466  	defer cancel()
   467  
   468  	input := []string{
   469  		"set @@session.binlog_format='STATEMENT'",
   470  		"insert into src1 values(1, 'aaa')",
   471  		"set @@session.binlog_format='ROW'",
   472  	}
   473  
   474  	// It does not work when filter is enabled
   475  	output := qh.Expect(
   476  		"begin",
   477  		"rollback",
   478  		"/update _vt.vreplication set message='filter rules are not supported for SBR",
   479  	)
   480  
   481  	execStatements(t, input)
   482  	expectDBClientQueries(t, output)
   483  }
   484  
   485  func TestPlayerStatementMode(t *testing.T) {
   486  	defer deleteTablet(addTablet(100))
   487  
   488  	execStatements(t, []string{
   489  		"create table src1(id int, val varbinary(128), primary key(id))",
   490  		fmt.Sprintf("create table %s.src1(id int, val varbinary(128), primary key(id))", vrepldb),
   491  	})
   492  	defer execStatements(t, []string{
   493  		"drop table src1",
   494  		fmt.Sprintf("drop table %s.src1", vrepldb),
   495  	})
   496  	env.SchemaEngine.Reload(context.Background())
   497  
   498  	filter := &binlogdatapb.Filter{
   499  		Rules: []*binlogdatapb.Rule{{
   500  			Match:  "/.*",
   501  			Filter: "",
   502  		}},
   503  	}
   504  	bls := &binlogdatapb.BinlogSource{
   505  		Keyspace: env.KeyspaceName,
   506  		Shard:    env.ShardName,
   507  		Filter:   filter,
   508  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   509  	}
   510  	cancel, _ := startVReplication(t, bls, "")
   511  	defer cancel()
   512  
   513  	input := []string{
   514  		"set @@session.binlog_format='STATEMENT'",
   515  		"insert into src1 values(1, 'aaa')",
   516  		"set @@session.binlog_format='ROW'",
   517  	}
   518  
   519  	output := qh.Expect(
   520  		"begin",
   521  		"insert into src1 values(1, 'aaa')",
   522  		"/update _vt.vreplication set pos=",
   523  		"commit",
   524  	)
   525  
   526  	execStatements(t, input)
   527  	expectDBClientQueries(t, output)
   528  }
   529  
   530  func TestPlayerFilters(t *testing.T) {
   531  	defer deleteTablet(addTablet(100))
   532  
   533  	execStatements(t, []string{
   534  		"create table src1(id int, val varbinary(128), primary key(id))",
   535  		fmt.Sprintf("create table %s.dst1(id int, val varbinary(128), primary key(id))", vrepldb),
   536  		"create table src2(id int, val1 int, val2 int, primary key(id))",
   537  		fmt.Sprintf("create table %s.dst2(id int, val1 int, sval2 int, rcount int, primary key(id))", vrepldb),
   538  		"create table src3(id int, val varbinary(128), primary key(id))",
   539  		fmt.Sprintf("create table %s.dst3(id int, val varbinary(128), primary key(id))", vrepldb),
   540  		"create table yes(id int, val varbinary(128), primary key(id))",
   541  		fmt.Sprintf("create table %s.yes(id int, val varbinary(128), primary key(id))", vrepldb),
   542  		"create table no(id int, val varbinary(128), primary key(id))",
   543  		"create table nopk(id int, val varbinary(128))",
   544  		fmt.Sprintf("create table %s.nopk(id int, val varbinary(128))", vrepldb),
   545  		"create table src4(id1 int, id2 int, val varbinary(128), primary key(id1))",
   546  		fmt.Sprintf("create table %s.dst4(id1 int, val varbinary(128), primary key(id1))", vrepldb),
   547  		"create table src5(id1 int, id2 int, val varbinary(128), primary key(id1))",
   548  		fmt.Sprintf("create table %s.dst5(id1 int, val varbinary(128), primary key(id1))", vrepldb),
   549  		"create table srcCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))",
   550  		fmt.Sprintf("create table %s.dstCharset(id1 int, val varchar(128) character set utf8mb4 collate utf8mb4_bin, val2 varchar(128) character set utf8mb4 collate utf8mb4_bin, primary key(id1))", vrepldb),
   551  	})
   552  	defer execStatements(t, []string{
   553  		"drop table src1",
   554  		fmt.Sprintf("drop table %s.dst1", vrepldb),
   555  		"drop table src2",
   556  		fmt.Sprintf("drop table %s.dst2", vrepldb),
   557  		"drop table src3",
   558  		fmt.Sprintf("drop table %s.dst3", vrepldb),
   559  		"drop table yes",
   560  		fmt.Sprintf("drop table %s.yes", vrepldb),
   561  		"drop table no",
   562  		"drop table nopk",
   563  		fmt.Sprintf("drop table %s.nopk", vrepldb),
   564  		"drop table src4",
   565  		fmt.Sprintf("drop table %s.dst4", vrepldb),
   566  		"drop table src5",
   567  		fmt.Sprintf("drop table %s.dst5", vrepldb),
   568  		"drop table srcCharset",
   569  		fmt.Sprintf("drop table %s.dstCharset", vrepldb),
   570  	})
   571  	env.SchemaEngine.Reload(context.Background())
   572  
   573  	filter := &binlogdatapb.Filter{
   574  		Rules: []*binlogdatapb.Rule{{
   575  			Match:  "dst1",
   576  			Filter: "select * from src1",
   577  		}, {
   578  			Match:  "dst2",
   579  			Filter: "select id, val1, sum(val2) as sval2, count(*) as rcount from src2 group by id",
   580  		}, {
   581  			Match:  "dst3",
   582  			Filter: "select id, val from src3 group by id, val",
   583  		}, {
   584  			Match: "/yes",
   585  		}, {
   586  			Match: "/nopk",
   587  		}, {
   588  			Match:  "dst4",
   589  			Filter: "select id1, val from src4 where id2 = 100",
   590  		}, {
   591  			Match:  "dst5",
   592  			Filter: "select id1, val from src5 where val = 'abc'",
   593  		}, {
   594  			Match:  "dstCharset",
   595  			Filter: "select id1, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val, concat(substr(_utf8mb4 val collate utf8mb4_bin,1,1),'abcxyz') val2 from srcCharset",
   596  		}},
   597  	}
   598  	bls := &binlogdatapb.BinlogSource{
   599  		Keyspace: env.KeyspaceName,
   600  		Shard:    env.ShardName,
   601  		Filter:   filter,
   602  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   603  	}
   604  
   605  	cancel, _ := startVReplication(t, bls, "")
   606  	defer cancel()
   607  
   608  	testcases := []struct {
   609  		input  string
   610  		output qh.ExpectationSequence
   611  		table  string
   612  		data   [][]string
   613  		logs   []LogExpectation // logs are defined for a few testcases since they are enough to test all log events
   614  	}{{
   615  		// insert with insertNormal
   616  		input: "insert into src1 values(1, 'aaa')",
   617  		output: qh.Expect(
   618  			"begin",
   619  			"insert into dst1(id,val) values (1,'aaa')",
   620  			"/update _vt.vreplication set pos=",
   621  			"commit",
   622  		),
   623  		table: "dst1",
   624  		data: [][]string{
   625  			{"1", "aaa"},
   626  		},
   627  		logs: []LogExpectation{
   628  			{"FIELD", "/src1.*id.*INT32.*val.*VARBINARY.*"},
   629  			{"ROWCHANGE", "insert into dst1(id,val) values (1,'aaa')"},
   630  			{"ROW", "/src1.*3.*1aaa.*"},
   631  		},
   632  	}, {
   633  		// update with insertNormal
   634  		input: "update src1 set val='bbb'",
   635  		output: qh.Expect(
   636  			"begin",
   637  			"update dst1 set val='bbb' where id=1",
   638  			"/update _vt.vreplication set pos=",
   639  			"commit",
   640  		),
   641  		table: "dst1",
   642  		data: [][]string{
   643  			{"1", "bbb"},
   644  		},
   645  		logs: []LogExpectation{
   646  			{"ROWCHANGE", "update dst1 set val='bbb' where id=1"},
   647  			{"ROW", "/src1.*3.*1aaa.*"},
   648  		},
   649  	}, {
   650  		// delete with insertNormal
   651  		input: "delete from src1 where id=1",
   652  		output: qh.Expect(
   653  			"begin",
   654  			"delete from dst1 where id=1",
   655  			"/update _vt.vreplication set pos=",
   656  			"commit",
   657  		),
   658  		table: "dst1",
   659  		data:  [][]string{},
   660  		logs: []LogExpectation{
   661  			{"ROWCHANGE", "delete from dst1 where id=1"},
   662  			{"ROW", "/src1.*3.*1bbb.*"},
   663  		},
   664  	}, {
   665  		// insert with insertOnDup
   666  		input: "insert into src2 values(1, 2, 3)",
   667  		output: qh.Expect(
   668  			"begin",
   669  			"insert into dst2(id,val1,sval2,rcount) values (1,2,ifnull(3, 0),1) on duplicate key update val1=values(val1), sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1",
   670  			"/update _vt.vreplication set pos=",
   671  			"commit",
   672  		),
   673  		table: "dst2",
   674  		data: [][]string{
   675  			{"1", "2", "3", "1"},
   676  		},
   677  		logs: []LogExpectation{
   678  			{"FIELD", "/src2.*id.*val1.*val2.*"},
   679  			{"ROWCHANGE", "insert into dst2(id,val1,sval2,rcount) values (1,2,ifnull(3, 0),1) on duplicate key update val1=values(val1), sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1"},
   680  		},
   681  	}, {
   682  		// update with insertOnDup
   683  		input: "update src2 set val1=5, val2=1 where id=1",
   684  		output: qh.Expect(
   685  			"begin",
   686  			"update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1",
   687  			"/update _vt.vreplication set pos=",
   688  			"commit",
   689  		),
   690  		table: "dst2",
   691  		data: [][]string{
   692  			{"1", "5", "1", "1"},
   693  		},
   694  		logs: []LogExpectation{
   695  			{"ROWCHANGE", "update dst2 set val1=5, sval2=sval2-ifnull(3, 0)+ifnull(1, 0), rcount=rcount where id=1"},
   696  			{"ROW", "/src2.*123.*"},
   697  		},
   698  	}, {
   699  		// delete with insertOnDup
   700  		input: "delete from src2 where id=1",
   701  		output: qh.Expect(
   702  			"begin",
   703  			"update dst2 set val1=null, sval2=sval2-ifnull(1, 0), rcount=rcount-1 where id=1",
   704  			"/update _vt.vreplication set pos=",
   705  			"commit",
   706  		),
   707  		table: "dst2",
   708  		data: [][]string{
   709  			{"1", "", "0", "0"},
   710  		},
   711  	}, {
   712  		// insert with insertIgnore
   713  		input: "insert into src3 values(1, 'aaa')",
   714  		output: qh.Expect(
   715  			"begin",
   716  			"insert ignore into dst3(id,val) values (1,'aaa')",
   717  			"/update _vt.vreplication set pos=",
   718  			"commit",
   719  		),
   720  		table: "dst3",
   721  		data: [][]string{
   722  			{"1", "aaa"},
   723  		},
   724  	}, {
   725  		// update with insertIgnore
   726  		input: "update src3 set val='bbb'",
   727  		output: qh.Expect(
   728  			"begin",
   729  			"insert ignore into dst3(id,val) values (1,'bbb')",
   730  			"/update _vt.vreplication set pos=",
   731  			"commit",
   732  		),
   733  		table: "dst3",
   734  		data: [][]string{
   735  			{"1", "aaa"},
   736  		},
   737  	}, {
   738  		// delete with insertIgnore
   739  		input: "delete from src3 where id=1",
   740  		output: qh.Expect(
   741  			"begin",
   742  			"/update _vt.vreplication set pos=",
   743  			"commit",
   744  		),
   745  		table: "dst3",
   746  		data: [][]string{
   747  			{"1", "aaa"},
   748  		},
   749  	}, {
   750  		// insert: regular expression filter
   751  		input: "insert into yes values(1, 'aaa')",
   752  		output: qh.Expect(
   753  			"begin",
   754  			"insert into yes(id,val) values (1,'aaa')",
   755  			"/update _vt.vreplication set pos=",
   756  			"commit",
   757  		),
   758  		table: "yes",
   759  		data: [][]string{
   760  			{"1", "aaa"},
   761  		},
   762  	}, {
   763  		// update: regular expression filter
   764  		input: "update yes set val='bbb'",
   765  		output: qh.Expect(
   766  			"begin",
   767  			"update yes set val='bbb' where id=1",
   768  			"/update _vt.vreplication set pos=",
   769  			"commit",
   770  		),
   771  		table: "yes",
   772  		data: [][]string{
   773  			{"1", "bbb"},
   774  		},
   775  	}, {
   776  		// table should not match a rule
   777  		input:  "insert into no values(1, 'aaa')",
   778  		output: qh.ExpectNone(),
   779  	}, {
   780  		// nopk: insert
   781  		input: "insert into nopk values(1, 'aaa')",
   782  		output: qh.Expect(
   783  			"begin",
   784  			"insert into nopk(id,val) values (1,'aaa')",
   785  			"/update _vt.vreplication set pos=",
   786  			"commit",
   787  		),
   788  		table: "nopk",
   789  		data: [][]string{
   790  			{"1", "aaa"},
   791  		},
   792  	}, {
   793  		// nopk: update
   794  		input: "update nopk set val='bbb' where id=1",
   795  		output: qh.Expect(
   796  			"begin",
   797  			"delete from nopk where id=1 and val='aaa'",
   798  			"insert into nopk(id,val) values (1,'bbb')",
   799  			"/update _vt.vreplication set pos=",
   800  			"commit",
   801  		),
   802  		table: "nopk",
   803  		data: [][]string{
   804  			{"1", "bbb"},
   805  		},
   806  	}, {
   807  		// nopk: delete
   808  		input: "delete from nopk where id=1",
   809  		output: qh.Expect(
   810  			"begin",
   811  			"delete from nopk where id=1 and val='bbb'",
   812  			"/update _vt.vreplication set pos=",
   813  			"commit",
   814  		),
   815  		table: "nopk",
   816  		data:  [][]string{},
   817  	}, {
   818  		// filter by int
   819  		input: "insert into src4 values (1,100,'aaa'),(2,200,'bbb'),(3,100,'ccc')",
   820  		output: qh.Expect(
   821  			"begin",
   822  			"insert into dst4(id1,val) values (1,'aaa')",
   823  			"insert into dst4(id1,val) values (3,'ccc')",
   824  			"/update _vt.vreplication set pos=",
   825  			"commit",
   826  		),
   827  		table: "dst4",
   828  		data:  [][]string{{"1", "aaa"}, {"3", "ccc"}},
   829  	}, {
   830  		// filter by int
   831  		input: "insert into src5 values (1,100,'abc'),(2,200,'xyz'),(3,100,'xyz'),(4,300,'abc'),(5,200,'xyz')",
   832  		output: qh.Expect(
   833  			"begin",
   834  			"insert into dst5(id1,val) values (1,'abc')",
   835  			"insert into dst5(id1,val) values (4,'abc')",
   836  			"/update _vt.vreplication set pos=",
   837  			"commit",
   838  		),
   839  		table: "dst5",
   840  		data:  [][]string{{"1", "abc"}, {"4", "abc"}},
   841  	}, {
   842  		// test collation + filter
   843  		input: "insert into srcCharset values (1,'木元')",
   844  		output: qh.Expect(
   845  			"begin",
   846  			"insert into dstCharset(id1,val,val2) values (1,concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'),concat(substr(_utf8mb4 '木元' collate utf8mb4_bin, 1, 1), 'abcxyz'))",
   847  			"/update _vt.vreplication set pos=",
   848  			"commit",
   849  		),
   850  		table: "dstCharset",
   851  		data:  [][]string{{"1", "木abcxyz", "木abcxyz"}},
   852  	}}
   853  
   854  	for _, tcase := range testcases {
   855  		t.Run(tcase.input, func(t *testing.T) {
   856  			if tcase.logs != nil {
   857  				logch := vrLogStatsLogger.Subscribe("vrlogstats")
   858  				defer expectLogsAndUnsubscribe(t, tcase.logs, logch)
   859  			}
   860  			execStatements(t, []string{tcase.input})
   861  			expectDBClientQueries(t, tcase.output)
   862  			if tcase.table != "" {
   863  				expectData(t, tcase.table, tcase.data)
   864  			}
   865  		})
   866  	}
   867  }
   868  
   869  func TestPlayerKeywordNames(t *testing.T) {
   870  	defer deleteTablet(addTablet(100))
   871  
   872  	execStatements(t, []string{
   873  		"create table `begin`(`primary` int, `column` varbinary(128), primary key(`primary`))",
   874  		fmt.Sprintf("create table %s.`begin`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb),
   875  		"create table `rollback`(`primary` int, `column` varbinary(128), primary key(`primary`))",
   876  		fmt.Sprintf("create table %s.`rollback`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb),
   877  		"create table `commit`(`primary` int, `column` varbinary(128), primary key(`primary`))",
   878  		fmt.Sprintf("create table %s.`commit`(`primary` int, `column` varbinary(128), primary key(`primary`))", vrepldb),
   879  	})
   880  	defer execStatements(t, []string{
   881  		"drop table `begin`",
   882  		fmt.Sprintf("drop table %s.`begin`", vrepldb),
   883  		"drop table `rollback`",
   884  		fmt.Sprintf("drop table %s.`rollback`", vrepldb),
   885  		"drop table `commit`",
   886  		fmt.Sprintf("drop table %s.`commit`", vrepldb),
   887  	})
   888  	env.SchemaEngine.Reload(context.Background())
   889  
   890  	filter := &binlogdatapb.Filter{
   891  		Rules: []*binlogdatapb.Rule{{
   892  			Match:  "begin",
   893  			Filter: "select * from `begin`",
   894  		}, {
   895  			Match:  "rollback",
   896  			Filter: "select `primary`, `column` from `rollback`",
   897  		}, {
   898  			Match:  "commit",
   899  			Filter: "select `primary`+1 as `primary`, concat(`column`, 'a') as `column` from `commit`",
   900  		}},
   901  	}
   902  
   903  	bls := &binlogdatapb.BinlogSource{
   904  		Keyspace: env.KeyspaceName,
   905  		Shard:    env.ShardName,
   906  		Filter:   filter,
   907  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
   908  	}
   909  
   910  	cancel, _ := startVReplication(t, bls, "")
   911  	defer cancel()
   912  
   913  	testcases := []struct {
   914  		input  string
   915  		output qh.ExpectationSequence
   916  		table  string
   917  		data   [][]string
   918  	}{{
   919  		input: "insert into `begin` values(1, 'aaa')",
   920  		output: qh.Expect(
   921  			"begin",
   922  			"insert into `begin`(`primary`,`column`) values (1,'aaa')",
   923  			"/update _vt.vreplication set pos=",
   924  			"commit",
   925  		),
   926  		table: "begin",
   927  		data: [][]string{
   928  			{"1", "aaa"},
   929  		},
   930  	}, {
   931  		input: "update `begin` set `column`='bbb'",
   932  		output: qh.Expect(
   933  			"begin",
   934  			"update `begin` set `column`='bbb' where `primary`=1",
   935  			"/update _vt.vreplication set pos=",
   936  			"commit",
   937  		),
   938  		table: "begin",
   939  		data: [][]string{
   940  			{"1", "bbb"},
   941  		},
   942  	}, {
   943  		input: "delete from `begin` where `primary`=1",
   944  		output: qh.Expect(
   945  			"begin",
   946  			"delete from `begin` where `primary`=1",
   947  			"/update _vt.vreplication set pos=",
   948  			"commit",
   949  		),
   950  		table: "begin",
   951  		data:  [][]string{},
   952  	}, {
   953  		input: "insert into `rollback` values(1, 'aaa')",
   954  		output: qh.Expect(
   955  			"begin",
   956  			"insert into `rollback`(`primary`,`column`) values (1,'aaa')",
   957  			"/update _vt.vreplication set pos=",
   958  			"commit",
   959  		),
   960  		table: "rollback",
   961  		data: [][]string{
   962  			{"1", "aaa"},
   963  		},
   964  	}, {
   965  		input: "update `rollback` set `column`='bbb'",
   966  		output: qh.Expect(
   967  			"begin",
   968  			"update `rollback` set `column`='bbb' where `primary`=1",
   969  			"/update _vt.vreplication set pos=",
   970  			"commit",
   971  		),
   972  		table: "rollback",
   973  		data: [][]string{
   974  			{"1", "bbb"},
   975  		},
   976  	}, {
   977  		input: "delete from `rollback` where `primary`=1",
   978  		output: qh.Expect(
   979  			"begin",
   980  			"delete from `rollback` where `primary`=1",
   981  			"/update _vt.vreplication set pos=",
   982  			"commit",
   983  		),
   984  		table: "rollback",
   985  		data:  [][]string{},
   986  	}, {
   987  		input: "insert into `commit` values(1, 'aaa')",
   988  		output: qh.Expect(
   989  			"begin",
   990  			"insert into `commit`(`primary`,`column`) values (1 + 1,concat('aaa', 'a'))",
   991  			"/update _vt.vreplication set pos=",
   992  			"commit",
   993  		),
   994  		table: "commit",
   995  		data: [][]string{
   996  			{"2", "aaaa"},
   997  		},
   998  	}, {
   999  		input: "update `commit` set `column`='bbb' where `primary`=1",
  1000  		output: qh.Expect(
  1001  			"begin",
  1002  			"update `commit` set `column`=concat('bbb', 'a') where `primary`=(1 + 1)",
  1003  			"/update _vt.vreplication set pos=",
  1004  			"commit",
  1005  		),
  1006  		table: "commit",
  1007  		data: [][]string{
  1008  			{"2", "bbba"},
  1009  		},
  1010  	}, {
  1011  		input: "update `commit` set `primary`=2 where `primary`=1",
  1012  		output: qh.Expect(
  1013  			"begin",
  1014  			"delete from `commit` where `primary`=(1 + 1)",
  1015  			"insert into `commit`(`primary`,`column`) values (2 + 1,concat('bbb', 'a'))",
  1016  			"/update _vt.vreplication set pos=",
  1017  			"commit",
  1018  		),
  1019  		table: "commit",
  1020  		data: [][]string{
  1021  			{"3", "bbba"},
  1022  		},
  1023  	}, {
  1024  		input: "delete from `commit` where `primary`=2",
  1025  		output: qh.Expect(
  1026  			"begin",
  1027  			"delete from `commit` where `primary`=(2 + 1)",
  1028  			"/update _vt.vreplication set pos=",
  1029  			"commit",
  1030  		),
  1031  		table: "commit",
  1032  		data:  [][]string{},
  1033  	}}
  1034  
  1035  	for _, tcases := range testcases {
  1036  		execStatements(t, []string{tcases.input})
  1037  		expectDBClientQueries(t, tcases.output)
  1038  		if tcases.table != "" {
  1039  			expectData(t, tcases.table, tcases.data)
  1040  		}
  1041  	}
  1042  }
  1043  
  1044  var shardedVSchema = `{
  1045    "sharded": true,
  1046    "vindexes": {
  1047      "hash": {
  1048        "type": "hash"
  1049      }
  1050    },
  1051    "tables": {
  1052      "src1": {
  1053        "column_vindexes": [
  1054          {
  1055            "column": "id",
  1056            "name": "hash"
  1057          }
  1058        ]
  1059      }
  1060    }
  1061  }`
  1062  
  1063  func TestPlayerKeyspaceID(t *testing.T) {
  1064  	defer deleteTablet(addTablet(100))
  1065  
  1066  	execStatements(t, []string{
  1067  		"create table src1(id int, val varbinary(128), primary key(id))",
  1068  		fmt.Sprintf("create table %s.dst1(id int, val varbinary(128), primary key(id))", vrepldb),
  1069  	})
  1070  	defer execStatements(t, []string{
  1071  		"drop table src1",
  1072  		fmt.Sprintf("drop table %s.dst1", vrepldb),
  1073  	})
  1074  	env.SchemaEngine.Reload(context.Background())
  1075  
  1076  	if err := env.SetVSchema(shardedVSchema); err != nil {
  1077  		t.Fatal(err)
  1078  	}
  1079  	defer env.SetVSchema("{}")
  1080  
  1081  	filter := &binlogdatapb.Filter{
  1082  		Rules: []*binlogdatapb.Rule{{
  1083  			Match:  "dst1",
  1084  			Filter: "select id, keyspace_id() as val from src1",
  1085  		}},
  1086  	}
  1087  	bls := &binlogdatapb.BinlogSource{
  1088  		Keyspace: env.KeyspaceName,
  1089  		Shard:    env.ShardName,
  1090  		Filter:   filter,
  1091  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1092  	}
  1093  	cancel, _ := startVReplication(t, bls, "")
  1094  	defer cancel()
  1095  
  1096  	testcases := []struct {
  1097  		input  string
  1098  		output qh.ExpectationSequence
  1099  		table  string
  1100  		data   [][]string
  1101  	}{{
  1102  		// insert with insertNormal
  1103  		input: "insert into src1 values(1, 'aaa')",
  1104  		output: qh.Expect(
  1105  			"begin",
  1106  			"insert into dst1(id,val) values (1,'\x16k@\xb4J\xbaK\xd6')",
  1107  			"/update _vt.vreplication set pos=",
  1108  			"commit",
  1109  		),
  1110  		table: "dst1",
  1111  		data: [][]string{
  1112  			{"1", "\x16k@\xb4J\xbaK\xd6"},
  1113  		},
  1114  	}}
  1115  
  1116  	for _, tcases := range testcases {
  1117  		execStatements(t, []string{tcases.input})
  1118  		expectDBClientQueries(t, tcases.output)
  1119  		if tcases.table != "" {
  1120  			expectData(t, tcases.table, tcases.data)
  1121  		}
  1122  	}
  1123  }
  1124  
  1125  func TestUnicode(t *testing.T) {
  1126  	defer deleteTablet(addTablet(100))
  1127  
  1128  	execStatements(t, []string{
  1129  		"create table src1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id))",
  1130  		fmt.Sprintf("create table %s.dst1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id)) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", vrepldb),
  1131  	})
  1132  	defer execStatements(t, []string{
  1133  		"drop table src1",
  1134  		fmt.Sprintf("drop table %s.dst1", vrepldb),
  1135  	})
  1136  	env.SchemaEngine.Reload(context.Background())
  1137  
  1138  	filter := &binlogdatapb.Filter{
  1139  		Rules: []*binlogdatapb.Rule{{
  1140  			Match:  "dst1",
  1141  			Filter: "select * from src1",
  1142  		}},
  1143  	}
  1144  	bls := &binlogdatapb.BinlogSource{
  1145  		Keyspace: env.KeyspaceName,
  1146  		Shard:    env.ShardName,
  1147  		Filter:   filter,
  1148  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1149  	}
  1150  	cancel, _ := startVReplication(t, bls, "")
  1151  	defer cancel()
  1152  
  1153  	testcases := []struct {
  1154  		input  string
  1155  		output qh.ExpectationSequence
  1156  		table  string
  1157  		data   [][]string
  1158  	}{{
  1159  		// insert with insertNormal
  1160  		input: "insert into src1 values(1, '👍')",
  1161  		output: qh.Expect(
  1162  			"begin",
  1163  			// We should expect the "Mojibaked" version.
  1164  			"insert into dst1(id,val) values (1,'ðŸ‘\u008d')",
  1165  			"/update _vt.vreplication set pos=",
  1166  			"commit",
  1167  		),
  1168  		table: "dst1",
  1169  		data: [][]string{
  1170  			{"1", "👍"},
  1171  		},
  1172  	}}
  1173  
  1174  	// We need a latin1 connection.
  1175  	conn, err := env.Mysqld.GetDbaConnection(context.Background())
  1176  	if err != nil {
  1177  		t.Fatal(err)
  1178  	}
  1179  	defer conn.Close()
  1180  
  1181  	if _, err := conn.ExecuteFetch("set names latin1", 10000, false); err != nil {
  1182  		t.Fatal(err)
  1183  	}
  1184  
  1185  	for _, tcases := range testcases {
  1186  		if _, err := conn.ExecuteFetch(tcases.input, 10000, false); err != nil {
  1187  			t.Error(err)
  1188  		}
  1189  		expectDBClientQueries(t, tcases.output)
  1190  		if tcases.table != "" {
  1191  			customExpectData(t, tcases.table, tcases.data, func(ctx context.Context, query string) (*sqltypes.Result, error) {
  1192  				return conn.ExecuteFetch(query, 10000, true)
  1193  			})
  1194  		}
  1195  	}
  1196  }
  1197  
  1198  func TestPlayerUpdates(t *testing.T) {
  1199  	defer deleteTablet(addTablet(100))
  1200  
  1201  	execStatements(t, []string{
  1202  		"create table t1(id int, grouped int, ungrouped int, summed int, primary key(id))",
  1203  		fmt.Sprintf("create table %s.t1(id int, grouped int, ungrouped int, summed int, rcount int, primary key(id))", vrepldb),
  1204  	})
  1205  	defer execStatements(t, []string{
  1206  		"drop table t1",
  1207  		fmt.Sprintf("drop table %s.t1", vrepldb),
  1208  	})
  1209  	env.SchemaEngine.Reload(context.Background())
  1210  
  1211  	filter := &binlogdatapb.Filter{
  1212  		Rules: []*binlogdatapb.Rule{{
  1213  			Match:  "t1",
  1214  			Filter: "select id, grouped, ungrouped, sum(summed) as summed, count(*) as rcount from t1 group by id, grouped",
  1215  		}},
  1216  	}
  1217  	bls := &binlogdatapb.BinlogSource{
  1218  		Keyspace: env.KeyspaceName,
  1219  		Shard:    env.ShardName,
  1220  		Filter:   filter,
  1221  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1222  	}
  1223  	cancel, _ := startVReplication(t, bls, "")
  1224  	defer cancel()
  1225  
  1226  	testcases := []struct {
  1227  		input  string
  1228  		output string
  1229  		table  string
  1230  		data   [][]string
  1231  	}{{
  1232  		// Start with all nulls
  1233  		input:  "insert into t1 values(1, null, null, null)",
  1234  		output: "insert into t1(id,grouped,ungrouped,summed,rcount) values (1,null,null,ifnull(null, 0),1) on duplicate key update ungrouped=values(ungrouped), summed=summed+ifnull(values(summed), 0), rcount=rcount+1",
  1235  		table:  "t1",
  1236  		data: [][]string{
  1237  			{"1", "", "", "0", "1"},
  1238  		},
  1239  	}, {
  1240  		// null to null values
  1241  		input:  "update t1 set grouped=1 where id=1",
  1242  		output: "update t1 set ungrouped=null, summed=summed-ifnull(null, 0)+ifnull(null, 0), rcount=rcount where id=1",
  1243  		table:  "t1",
  1244  		data: [][]string{
  1245  			{"1", "", "", "0", "1"},
  1246  		},
  1247  	}, {
  1248  		// null to non-null values
  1249  		input:  "update t1 set ungrouped=1, summed=1 where id=1",
  1250  		output: "update t1 set ungrouped=1, summed=summed-ifnull(null, 0)+ifnull(1, 0), rcount=rcount where id=1",
  1251  		table:  "t1",
  1252  		data: [][]string{
  1253  			{"1", "", "1", "1", "1"},
  1254  		},
  1255  	}, {
  1256  		// non-null to non-null values
  1257  		input:  "update t1 set ungrouped=2, summed=2 where id=1",
  1258  		output: "update t1 set ungrouped=2, summed=summed-ifnull(1, 0)+ifnull(2, 0), rcount=rcount where id=1",
  1259  		table:  "t1",
  1260  		data: [][]string{
  1261  			{"1", "", "2", "2", "1"},
  1262  		},
  1263  	}, {
  1264  		// non-null to null values
  1265  		input:  "update t1 set ungrouped=null, summed=null where id=1",
  1266  		output: "update t1 set ungrouped=null, summed=summed-ifnull(2, 0)+ifnull(null, 0), rcount=rcount where id=1",
  1267  		table:  "t1",
  1268  		data: [][]string{
  1269  			{"1", "", "", "0", "1"},
  1270  		},
  1271  	}, {
  1272  		// insert non-null values
  1273  		input:  "insert into t1 values(2, 2, 3, 4)",
  1274  		output: "insert into t1(id,grouped,ungrouped,summed,rcount) values (2,2,3,ifnull(4, 0),1) on duplicate key update ungrouped=values(ungrouped), summed=summed+ifnull(values(summed), 0), rcount=rcount+1",
  1275  		table:  "t1",
  1276  		data: [][]string{
  1277  			{"1", "", "", "0", "1"},
  1278  			{"2", "2", "3", "4", "1"},
  1279  		},
  1280  	}, {
  1281  		// delete non-null values
  1282  		input:  "delete from t1 where id=2",
  1283  		output: "update t1 set ungrouped=null, summed=summed-ifnull(4, 0), rcount=rcount-1 where id=2",
  1284  		table:  "t1",
  1285  		data: [][]string{
  1286  			{"1", "", "", "0", "1"},
  1287  			{"2", "2", "", "0", "0"},
  1288  		},
  1289  	}}
  1290  
  1291  	for _, tcases := range testcases {
  1292  		execStatements(t, []string{tcases.input})
  1293  		output := qh.Expect(
  1294  			"begin",
  1295  			tcases.output,
  1296  			"/update _vt.vreplication set pos=",
  1297  			"commit",
  1298  		)
  1299  		if tcases.output == "" {
  1300  			output = qh.Expect(
  1301  				"begin",
  1302  				"/update _vt.vreplication set pos=",
  1303  				"commit",
  1304  			)
  1305  		}
  1306  		expectDBClientQueries(t, output)
  1307  		if tcases.table != "" {
  1308  			expectData(t, tcases.table, tcases.data)
  1309  		}
  1310  	}
  1311  	validateQueryCountStat(t, "replicate", 7)
  1312  }
  1313  
  1314  func TestPlayerRowMove(t *testing.T) {
  1315  	defer deleteTablet(addTablet(100))
  1316  
  1317  	execStatements(t, []string{
  1318  		"create table src(id int, val1 int, val2 int, primary key(id))",
  1319  		fmt.Sprintf("create table %s.dst(val1 int, sval2 int, rcount int, primary key(val1))", vrepldb),
  1320  	})
  1321  	defer execStatements(t, []string{
  1322  		"drop table src",
  1323  		fmt.Sprintf("drop table %s.dst", vrepldb),
  1324  	})
  1325  	env.SchemaEngine.Reload(context.Background())
  1326  
  1327  	filter := &binlogdatapb.Filter{
  1328  		Rules: []*binlogdatapb.Rule{{
  1329  			Match:  "dst",
  1330  			Filter: "select val1, sum(val2) as sval2, count(*) as rcount from src group by val1",
  1331  		}},
  1332  	}
  1333  	bls := &binlogdatapb.BinlogSource{
  1334  		Keyspace: env.KeyspaceName,
  1335  		Shard:    env.ShardName,
  1336  		Filter:   filter,
  1337  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1338  	}
  1339  	cancel, _ := startVReplication(t, bls, "")
  1340  	defer cancel()
  1341  
  1342  	execStatements(t, []string{
  1343  		"insert into src values(1, 1, 1), (2, 2, 2), (3, 2, 3)",
  1344  	})
  1345  	expectDBClientQueries(t, qh.Expect(
  1346  		"begin",
  1347  		"insert into dst(val1,sval2,rcount) values (1,ifnull(1, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1",
  1348  		"insert into dst(val1,sval2,rcount) values (2,ifnull(2, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1",
  1349  		"insert into dst(val1,sval2,rcount) values (2,ifnull(3, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1",
  1350  		"/update _vt.vreplication set pos=",
  1351  		"commit",
  1352  	))
  1353  	expectData(t, "dst", [][]string{
  1354  		{"1", "1", "1"},
  1355  		{"2", "5", "2"},
  1356  	})
  1357  	validateQueryCountStat(t, "replicate", 3)
  1358  
  1359  	execStatements(t, []string{
  1360  		"update src set val1=1, val2=4 where id=3",
  1361  	})
  1362  	expectDBClientQueries(t, qh.Expect(
  1363  		"begin",
  1364  		"update dst set sval2=sval2-ifnull(3, 0), rcount=rcount-1 where val1=2",
  1365  		"insert into dst(val1,sval2,rcount) values (1,ifnull(4, 0),1) on duplicate key update sval2=sval2+ifnull(values(sval2), 0), rcount=rcount+1",
  1366  		"/update _vt.vreplication set pos=",
  1367  		"commit",
  1368  	))
  1369  	expectData(t, "dst", [][]string{
  1370  		{"1", "5", "2"},
  1371  		{"2", "2", "1"},
  1372  	})
  1373  	validateQueryCountStat(t, "replicate", 5)
  1374  }
  1375  
  1376  func TestPlayerTypes(t *testing.T) {
  1377  	log.Errorf("TestPlayerTypes: flavor is %s", env.Flavor)
  1378  	enableJSONColumnTesting := false
  1379  	flavor := strings.ToLower(env.Flavor)
  1380  	// Disable tests on percona and mariadb platforms in CI since they
  1381  	// either don't support JSON or JSON support is not enabled by default
  1382  	if strings.Contains(flavor, "mysql57") || strings.Contains(flavor, "mysql80") {
  1383  		log.Infof("Running JSON column type tests on flavor %s", flavor)
  1384  		enableJSONColumnTesting = true
  1385  	} else {
  1386  		log.Warningf("Not running JSON column type tests on flavor %s", flavor)
  1387  	}
  1388  	defer deleteTablet(addTablet(100))
  1389  
  1390  	execStatements(t, []string{
  1391  		"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))",
  1392  		fmt.Sprintf("create table %s.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))", vrepldb),
  1393  		"create table vitess_fracts(id int, deci decimal(5,2), num numeric(5,2), f float, d double, primary key(id))",
  1394  		fmt.Sprintf("create table %s.vitess_fracts(id int, deci decimal(5,2), num numeric(5,2), f float, d double, primary key(id))", vrepldb),
  1395  		"create table vitess_strings(vb varbinary(16), c char(16), vc varchar(16), b binary(5), tb tinyblob, bl blob, ttx tinytext, tx text, en enum('a','b'), s set('a','b'), primary key(vb))",
  1396  		fmt.Sprintf("create table %s.vitess_strings(vb varbinary(16), c char(16), vc varchar(16), b binary(5), tb tinyblob, bl blob, ttx tinytext, tx text, en enum('a','b'), s set('a','b'), primary key(vb))", vrepldb),
  1397  		"create table vitess_misc(id int, b bit(8), d date, dt datetime, t time, g geometry, primary key(id))",
  1398  		fmt.Sprintf("create table %s.vitess_misc(id int, b bit(8), d date, dt datetime, t time, g geometry, primary key(id))", vrepldb),
  1399  		"create table vitess_null(id int, val varbinary(128), primary key(id))",
  1400  		fmt.Sprintf("create table %s.vitess_null(id int, val varbinary(128), primary key(id))", vrepldb),
  1401  		"create table src1(id int, val varbinary(128), primary key(id))",
  1402  		fmt.Sprintf("create table %s.src1(id int, val varbinary(128), primary key(id))", vrepldb),
  1403  		"create table binary_pk(b binary(4), val varbinary(4), primary key(b))",
  1404  		fmt.Sprintf("create table %s.binary_pk(b binary(4), val varbinary(4), primary key(b))", vrepldb),
  1405  		"create table vitess_decimal(id int, d1 decimal(8,0) default null, d2 decimal(8,0) default null, d3 decimal(8,0) default null, d4 decimal(8, 1), d5 decimal(8, 1), d6 decimal(8, 1), primary key(id))",
  1406  		fmt.Sprintf("create table %s.vitess_decimal(id int, d1 decimal(8,0) default null, d2 decimal(8,0) default null, d3 decimal(8,0) default null, d4 decimal(8, 1), d5 decimal(8, 1), d6 decimal(8, 1), primary key(id))", vrepldb),
  1407  	})
  1408  	defer execStatements(t, []string{
  1409  		"drop table vitess_ints",
  1410  		fmt.Sprintf("drop table %s.vitess_ints", vrepldb),
  1411  		"drop table vitess_fracts",
  1412  		fmt.Sprintf("drop table %s.vitess_fracts", vrepldb),
  1413  		"drop table vitess_strings",
  1414  		fmt.Sprintf("drop table %s.vitess_strings", vrepldb),
  1415  		"drop table vitess_misc",
  1416  		fmt.Sprintf("drop table %s.vitess_misc", vrepldb),
  1417  		"drop table vitess_null",
  1418  		fmt.Sprintf("drop table %s.vitess_null", vrepldb),
  1419  		"drop table src1",
  1420  		fmt.Sprintf("drop table %s.src1", vrepldb),
  1421  		"drop table binary_pk",
  1422  		fmt.Sprintf("drop table %s.binary_pk", vrepldb),
  1423  		"drop table vitess_decimal",
  1424  		fmt.Sprintf("drop table %s.vitess_decimal", vrepldb),
  1425  	})
  1426  	if enableJSONColumnTesting {
  1427  		execStatements(t, []string{
  1428  			"create table vitess_json(id int auto_increment, val1 json, val2 json, val3 json, val4 json, val5 json, primary key(id))",
  1429  			fmt.Sprintf("create table %s.vitess_json(id int, val1 json, val2 json, val3 json, val4 json, val5 json, primary key(id))", vrepldb),
  1430  		})
  1431  		defer execStatements(t, []string{
  1432  			"drop table vitess_json",
  1433  			fmt.Sprintf("drop table %s.vitess_json", vrepldb),
  1434  		})
  1435  
  1436  	}
  1437  	env.SchemaEngine.Reload(context.Background())
  1438  
  1439  	filter := &binlogdatapb.Filter{
  1440  		Rules: []*binlogdatapb.Rule{{
  1441  			Match: "/.*",
  1442  		}},
  1443  	}
  1444  	bls := &binlogdatapb.BinlogSource{
  1445  		Keyspace: env.KeyspaceName,
  1446  		Shard:    env.ShardName,
  1447  		Filter:   filter,
  1448  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1449  	}
  1450  	cancel, _ := startVReplication(t, bls, "")
  1451  	defer cancel()
  1452  	type testcase struct {
  1453  		input  string
  1454  		output string
  1455  		table  string
  1456  		data   [][]string
  1457  	}
  1458  	testcases := []testcase{{
  1459  		input:  "insert into vitess_ints values(-128, 255, -32768, 65535, -8388608, 16777215, -2147483648, 4294967295, -9223372036854775808, 18446744073709551615, 2012)",
  1460  		output: "insert into vitess_ints(tiny,tinyu,small,smallu,medium,mediumu,normal,normalu,big,bigu,y) values (-128,255,-32768,65535,-8388608,16777215,-2147483648,4294967295,-9223372036854775808,18446744073709551615,2012)",
  1461  		table:  "vitess_ints",
  1462  		data: [][]string{
  1463  			{"-128", "255", "-32768", "65535", "-8388608", "16777215", "-2147483648", "4294967295", "-9223372036854775808", "18446744073709551615", "2012"},
  1464  		},
  1465  	}, {
  1466  		input:  "insert into vitess_fracts values(1, 1.99, 2.99, 3.99, 4.99)",
  1467  		output: "insert into vitess_fracts(id,deci,num,f,d) values (1,1.99,2.99,3.99E+00,4.99E+00)",
  1468  		table:  "vitess_fracts",
  1469  		data: [][]string{
  1470  			{"1", "1.99", "2.99", "3.99", "4.99"},
  1471  		},
  1472  	}, {
  1473  		input:  "insert into vitess_strings values('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'a', 'a,b')",
  1474  		output: "insert into vitess_strings(vb,c,vc,b,tb,bl,ttx,tx,en,s) values ('a','b','c','d\\0\\0\\0\\0','e','f','g','h',1,'3')",
  1475  		table:  "vitess_strings",
  1476  		data: [][]string{
  1477  			{"a", "b", "c", "d\000\000\000\000", "e", "f", "g", "h", "a", "a,b"},
  1478  		},
  1479  	}, {
  1480  		input:  "insert into vitess_misc values(1, '\x01', '2012-01-01', '2012-01-01 15:45:45', '15:45:45', point(1, 2))",
  1481  		output: "insert into vitess_misc(id,b,d,dt,t,g) values (1,b'00000001','2012-01-01','2012-01-01 15:45:45','15:45:45','\\0\\0\\0\\0\x01\x01\\0\\0\\0\\0\\0\\0\\0\\0\\0\xf0?\\0\\0\\0\\0\\0\\0\\0@')",
  1482  		table:  "vitess_misc",
  1483  		data: [][]string{
  1484  			{"1", "\x01", "2012-01-01", "2012-01-01 15:45:45", "15: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@"},
  1485  		},
  1486  	}, {
  1487  		input:  "insert into vitess_null values(1, null)",
  1488  		output: "insert into vitess_null(id,val) values (1,null)",
  1489  		table:  "vitess_null",
  1490  		data: [][]string{
  1491  			{"1", ""},
  1492  		},
  1493  	}, {
  1494  		input:  "insert into binary_pk values('a', 'aaa')",
  1495  		output: "insert into binary_pk(b,val) values ('a\\0\\0\\0','aaa')",
  1496  		table:  "binary_pk",
  1497  		data: [][]string{
  1498  			{"a\000\000\000", "aaa"},
  1499  		},
  1500  	}, {
  1501  		input:  "insert into vitess_decimal values(1, 0, 1, null, 0, 1.1, 1)",
  1502  		output: "insert into vitess_decimal(id,d1,d2,d3,d4,d5,d6) values (1,0,1,null,.0,1.1,1.0)",
  1503  		table:  "vitess_decimal",
  1504  		data: [][]string{
  1505  			{"1", "0", "1", "", "0.0", "1.1", "1.0"},
  1506  		},
  1507  	}, {
  1508  		// Binary pk is a special case: https://github.com/vitessio/vitess/issues/3984
  1509  		input:  "update binary_pk set val='bbb' where b='a\\0\\0\\0'",
  1510  		output: "update binary_pk set val='bbb' where b='a\\0\\0\\0'",
  1511  		table:  "binary_pk",
  1512  		data: [][]string{
  1513  			{"a\000\000\000", "bbb"},
  1514  		},
  1515  	}}
  1516  	if enableJSONColumnTesting {
  1517  		testcases = append(testcases, testcase{
  1518  			input: "insert into vitess_json(val1,val2,val3,val4,val5) values (null,'{}','1629849600','{\"a\":[42,-1,3.1415,-128,127,-9223372036854775808,9223372036854775807,18446744073709551615]}', '{\"foo\":\"bar\"}')",
  1519  			output: "insert into vitess_json(id,val1,val2,val3,val4,val5) values (1," +
  1520  				"convert(null using utf8mb4)," + "convert('{}' using utf8mb4)," + "convert('1629849600' using utf8mb4)," +
  1521  				"convert('{\\\"a\\\":[42,-1,3.1415,-128,127,-9223372036854775808,9223372036854775807,18446744073709551615]}' using utf8mb4)," + "convert('{\\\"foo\\\":\\\"bar\\\"}' using utf8mb4))",
  1522  			table: "vitess_json",
  1523  			data: [][]string{
  1524  				{"1", "", "{}", "1629849600", `{"a": [42, -1, 3.1415, -128, 127, -9223372036854775808, 9223372036854775807, 18446744073709551615]}`, `{"foo": "bar"}`},
  1525  			},
  1526  		})
  1527  		testcases = append(testcases, testcase{
  1528  			input:  "update vitess_json set val4 = '{\"a\": [-9223372036854775808, -2147483648]}', val5 = convert(x'7b7d' using utf8mb4)",
  1529  			output: "update vitess_json set val1=convert(null using utf8mb4), val2=convert('{}' using utf8mb4), val3=convert('1629849600' using utf8mb4), val4=convert('{\\\"a\\\":[-9223372036854775808,-2147483648]}' using utf8mb4), val5=convert('{}' using utf8mb4) where id=1",
  1530  			table:  "vitess_json",
  1531  			data: [][]string{
  1532  				{"1", "", "{}", "1629849600", `{"a": [-9223372036854775808, -2147483648]}`, `{}`},
  1533  			},
  1534  		})
  1535  	}
  1536  
  1537  	for _, tcases := range testcases {
  1538  		execStatements(t, []string{tcases.input})
  1539  		want := qh.Expect(
  1540  			"begin",
  1541  			tcases.output,
  1542  			"/update _vt.vreplication set pos=",
  1543  			"commit",
  1544  		)
  1545  		expectDBClientQueries(t, want)
  1546  		if tcases.table != "" {
  1547  			expectData(t, tcases.table, tcases.data)
  1548  		}
  1549  	}
  1550  }
  1551  
  1552  func TestPlayerDDL(t *testing.T) {
  1553  	defer deleteTablet(addTablet(100))
  1554  	execStatements(t, []string{
  1555  		"create table t1(id int, primary key(id))",
  1556  		fmt.Sprintf("create table %s.t1(id int, primary key(id))", vrepldb),
  1557  	})
  1558  	defer execStatements(t, []string{
  1559  		"drop table t1",
  1560  		fmt.Sprintf("drop table %s.t1", vrepldb),
  1561  	})
  1562  	env.SchemaEngine.Reload(context.Background())
  1563  
  1564  	filter := &binlogdatapb.Filter{
  1565  		Rules: []*binlogdatapb.Rule{{
  1566  			Match: "/.*",
  1567  		}},
  1568  	}
  1569  	bls := &binlogdatapb.BinlogSource{
  1570  		Keyspace: env.KeyspaceName,
  1571  		Shard:    env.ShardName,
  1572  		Filter:   filter,
  1573  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1574  	}
  1575  	cancel, _ := startVReplication(t, bls, "")
  1576  	// Issue a dummy change to ensure vreplication is initialized. Otherwise there
  1577  	// is a race between the DDLs and the schema loader of vstreamer.
  1578  	// Root cause seems to be with MySQL where t1 shows up in information_schema before
  1579  	// the actual table is created.
  1580  	execStatements(t, []string{"insert into t1 values(1)"})
  1581  	expectDBClientQueries(t, qh.Expect(
  1582  		"begin",
  1583  		"insert into t1(id) values (1)",
  1584  		"/update _vt.vreplication set pos=",
  1585  		"commit",
  1586  	))
  1587  
  1588  	execStatements(t, []string{"alter table t1 add column val varchar(128)"})
  1589  	execStatements(t, []string{"alter table t1 drop column val"})
  1590  	expectDBClientQueries(t, qh.Expect(
  1591  		"/update _vt.vreplication set pos=",
  1592  		"/update _vt.vreplication set pos=",
  1593  	))
  1594  	cancel()
  1595  	bls = &binlogdatapb.BinlogSource{
  1596  		Keyspace: env.KeyspaceName,
  1597  		Shard:    env.ShardName,
  1598  		Filter:   filter,
  1599  		OnDdl:    binlogdatapb.OnDDLAction_STOP,
  1600  	}
  1601  	cancel, id := startVReplication(t, bls, "")
  1602  	pos0 := primaryPosition(t) //For debugging only
  1603  	execStatements(t, []string{"alter table t1 add column val varchar(128)"})
  1604  	pos1 := primaryPosition(t)
  1605  	// The stop position must be the GTID of the first DDL
  1606  	expectDBClientQueries(t, qh.Expect(
  1607  		"begin",
  1608  		fmt.Sprintf("/update _vt.vreplication set pos='%s'", pos1),
  1609  		"/update _vt.vreplication set state='Stopped'",
  1610  		"commit",
  1611  	))
  1612  	pos2b := primaryPosition(t)
  1613  	execStatements(t, []string{"alter table t1 drop column val"})
  1614  	pos2 := primaryPosition(t)
  1615  	log.Errorf("Expected log:: TestPlayerDDL Positions are: before first alter %v, after first alter %v, before second alter %v, after second alter %v",
  1616  		pos0, pos1, pos2b, pos2) //For debugging only: to check what are the positions when test works and if/when it fails
  1617  	// Restart vreplication
  1618  	if _, err := playerEngine.Exec(fmt.Sprintf(`update _vt.vreplication set state = 'Running', message='' where id=%d`, id)); err != nil {
  1619  		t.Fatal(err)
  1620  	}
  1621  	// It should stop at the next DDL
  1622  	expectDBClientQueries(t, qh.Expect(
  1623  		"/update.*'Running'",
  1624  		// Second update is from vreplicator.
  1625  		"/update _vt.vreplication set message='Picked source tablet.*",
  1626  		"/update.*'Running'",
  1627  		"begin",
  1628  		fmt.Sprintf("/update.*'%s'", pos2),
  1629  		"/update _vt.vreplication set state='Stopped'",
  1630  		"commit",
  1631  	))
  1632  	cancel()
  1633  	bls = &binlogdatapb.BinlogSource{
  1634  		Keyspace: env.KeyspaceName,
  1635  		Shard:    env.ShardName,
  1636  		Filter:   filter,
  1637  		OnDdl:    binlogdatapb.OnDDLAction_EXEC,
  1638  	}
  1639  	execStatements(t, []string{fmt.Sprintf("alter table %s.t1 add column val2 varchar(128)", vrepldb)})
  1640  	cancel, _ = startVReplication(t, bls, "")
  1641  	execStatements(t, []string{"alter table t1 add column val1 varchar(128)"})
  1642  	expectDBClientQueries(t, qh.Expect(
  1643  		"alter table t1 add column val1 varchar(128)",
  1644  		"/update _vt.vreplication set pos=",
  1645  		// The apply of the DDL on target generates an "other" event.
  1646  		"/update _vt.vreplication set pos=",
  1647  	))
  1648  	execStatements(t, []string{"alter table t1 add column val2 varchar(128)"})
  1649  	expectDBClientQueries(t, qh.Expect(
  1650  		"alter table t1 add column val2 varchar(128)",
  1651  		"/update _vt.vreplication set message='Duplicate",
  1652  		"/update _vt.vreplication set state='Error', message='Duplicate",
  1653  	))
  1654  	cancel()
  1655  
  1656  	execStatements(t, []string{
  1657  		"alter table t1 drop column val1",
  1658  		"alter table t1 drop column val2",
  1659  		fmt.Sprintf("alter table %s.t1 drop column val1", vrepldb),
  1660  	})
  1661  
  1662  	bls = &binlogdatapb.BinlogSource{
  1663  		Keyspace: env.KeyspaceName,
  1664  		Shard:    env.ShardName,
  1665  		Filter:   filter,
  1666  		OnDdl:    binlogdatapb.OnDDLAction_EXEC_IGNORE,
  1667  	}
  1668  	execStatements(t, []string{fmt.Sprintf("create table %s.t2(id int, primary key(id))", vrepldb)})
  1669  	cancel, _ = startVReplication(t, bls, "")
  1670  	execStatements(t, []string{"alter table t1 add column val1 varchar(128)"})
  1671  	expectDBClientQueries(t, qh.Expect(
  1672  		"alter table t1 add column val1 varchar(128)",
  1673  		"/update _vt.vreplication set pos=",
  1674  		// The apply of the DDL on target generates an "other" event.
  1675  		"/update _vt.vreplication set pos=",
  1676  	))
  1677  	execStatements(t, []string{"alter table t1 add column val2 varchar(128)"})
  1678  	expectDBClientQueries(t, qh.Expect(
  1679  		"alter table t1 add column val2 varchar(128)",
  1680  		"/update _vt.vreplication set pos=",
  1681  	))
  1682  	cancel()
  1683  }
  1684  
  1685  func TestGTIDCompress(t *testing.T) {
  1686  	ctx := context.Background()
  1687  	defer deleteTablet(addTablet(100))
  1688  	err := env.Mysqld.ExecuteSuperQuery(ctx, "insert into _vt.vreplication (id, workflow, source, pos, max_tps, max_replication_lag, time_updated, transaction_timestamp, state,db_name) values (1, '', '', '', 0,0,0,0,'Stopped','')")
  1689  	require.NoError(t, err)
  1690  
  1691  	type testCase struct {
  1692  		name, gtid string
  1693  		compress   bool
  1694  	}
  1695  
  1696  	testCases := []testCase{
  1697  		{"cleartext1", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092", false},
  1698  		{"cleartext2", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092,320a5e98-6965-11ea-b949-eeafd34ae6e4:1-3,81cbdbf8-6969-11ea-aeb1-a6143b021f67:1-524891956,c9a0f301-6965-11ea-ba9d-02c229065569:1-3,cb698dac-6969-11ea-ac38-16e5d0ac5c3a:1-524441991,e39fca4d-6960-11ea-b4c2-1e895fd49fa0:1-3", false},
  1699  		{"compress1", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092", true},
  1700  		{"compress2", "MySQL56/14b68925-696a-11ea-aee7-fec597a91f5e:1-308092,320a5e98-6965-11ea-b949-eeafd34ae6e4:1-3,81cbdbf8-6969-11ea-aeb1-a6143b021f67:1-524891956,c9a0f301-6965-11ea-ba9d-02c229065569:1-3,cb698dac-6969-11ea-ac38-16e5d0ac5c3a:1-524441991,e39fca4d-6960-11ea-b4c2-1e895fd49fa0:1-3", true},
  1701  		{"nil-compress", "", true},
  1702  		{"nil-clear", "", false},
  1703  	}
  1704  	for _, tCase := range testCases {
  1705  		t.Run(tCase.name, func(t *testing.T) {
  1706  			strGTID := fmt.Sprintf("'%s'", tCase.gtid)
  1707  			if tCase.compress {
  1708  				strGTID = fmt.Sprintf("compress(%s)", strGTID)
  1709  			}
  1710  			err := env.Mysqld.ExecuteSuperQuery(ctx, fmt.Sprintf("update _vt.vreplication set pos=%s where id = 1", strGTID))
  1711  			require.NoError(t, err)
  1712  			qr, err := env.Mysqld.FetchSuperQuery(ctx, "select pos from _vt.vreplication where id = 1")
  1713  			require.NoError(t, err)
  1714  			require.NotNil(t, qr)
  1715  			require.Equal(t, 1, len(qr.Rows))
  1716  			gotGTID := qr.Rows[0][0].ToString()
  1717  			pos, err := mysql.DecodePosition(gotGTID)
  1718  			if tCase.compress {
  1719  				require.True(t, pos.IsZero())
  1720  				pos, err = binlogplayer.DecodePosition(gotGTID)
  1721  				require.NoError(t, err)
  1722  				require.NotNil(t, pos)
  1723  				tpos, err := mysql.DecodePosition(tCase.gtid)
  1724  				require.NoError(t, err)
  1725  				require.Equal(t, tpos.String(), pos.String())
  1726  			} else {
  1727  				require.NoError(t, err)
  1728  				require.NotNil(t, pos)
  1729  				require.Equal(t, tCase.gtid, gotGTID)
  1730  			}
  1731  		})
  1732  	}
  1733  }
  1734  
  1735  func TestPlayerStopPos(t *testing.T) {
  1736  	defer deleteTablet(addTablet(100))
  1737  	vreplicationStoreCompressedGTID = true
  1738  	defer func() {
  1739  		vreplicationStoreCompressedGTID = false
  1740  	}()
  1741  	execStatements(t, []string{
  1742  		"create table yes(id int, val varbinary(128), primary key(id))",
  1743  		fmt.Sprintf("create table %s.yes(id int, val varbinary(128), primary key(id))", vrepldb),
  1744  		"create table no(id int, val varbinary(128), primary key(id))",
  1745  	})
  1746  	defer execStatements(t, []string{
  1747  		"drop table yes",
  1748  		fmt.Sprintf("drop table %s.yes", vrepldb),
  1749  		"drop table no",
  1750  	})
  1751  	env.SchemaEngine.Reload(context.Background())
  1752  
  1753  	filter := &binlogdatapb.Filter{
  1754  		Rules: []*binlogdatapb.Rule{{
  1755  			Match: "/yes",
  1756  		}},
  1757  	}
  1758  	bls := &binlogdatapb.BinlogSource{
  1759  		Keyspace: env.KeyspaceName,
  1760  		Shard:    env.ShardName,
  1761  		Filter:   filter,
  1762  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1763  	}
  1764  	startPos := primaryPosition(t)
  1765  	query := binlogplayer.CreateVReplicationState("test", bls, startPos, binlogplayer.BlpStopped, vrepldb, 0, 0)
  1766  	qr, err := playerEngine.Exec(query)
  1767  	if err != nil {
  1768  		t.Fatal(err)
  1769  	}
  1770  	id := uint32(qr.InsertID)
  1771  	for q := range globalDBQueries {
  1772  		if strings.HasPrefix(q, "insert into _vt.vreplication") {
  1773  			break
  1774  		}
  1775  	}
  1776  
  1777  	// Test normal stop.
  1778  	execStatements(t, []string{
  1779  		"insert into yes values(1, 'aaa')",
  1780  	})
  1781  	stopPos := primaryPosition(t)
  1782  	query = binlogplayer.StartVReplicationUntil(id, stopPos)
  1783  	if _, err := playerEngine.Exec(query); err != nil {
  1784  		t.Fatal(err)
  1785  	}
  1786  	expectDBClientQueries(t, qh.Expect(
  1787  		"/update.*'Running'",
  1788  		// Second update is from vreplicator.
  1789  		"/update _vt.vreplication set message='Picked source tablet.*",
  1790  		"/update.*'Running'",
  1791  		"begin",
  1792  		"insert into yes(id,val) values (1,'aaa')",
  1793  		fmt.Sprintf("/update.*compress.*'%s'", stopPos),
  1794  		"/update.*'Stopped'",
  1795  		"commit",
  1796  	))
  1797  
  1798  	// Test stopping at empty transaction.
  1799  	execStatements(t, []string{
  1800  		"insert into no values(2, 'aaa')",
  1801  		"insert into no values(3, 'aaa')",
  1802  	})
  1803  	stopPos = primaryPosition(t)
  1804  	execStatements(t, []string{
  1805  		"insert into no values(4, 'aaa')",
  1806  	})
  1807  	query = binlogplayer.StartVReplicationUntil(id, stopPos)
  1808  	if _, err := playerEngine.Exec(query); err != nil {
  1809  		t.Fatal(err)
  1810  	}
  1811  	expectDBClientQueries(t, qh.Expect(
  1812  		"/update.*'Running'",
  1813  		// Second update is from vreplicator.
  1814  		"/update _vt.vreplication set message='Picked source tablet.*",
  1815  		"/update.*'Running'",
  1816  		"begin",
  1817  		// Since 'no' generates empty transactions that are skipped by
  1818  		// vplayer, a commit is done only for the stop position event.
  1819  		fmt.Sprintf("/update.*'%s'", stopPos),
  1820  		"/update.*'Stopped'",
  1821  		"commit",
  1822  	))
  1823  
  1824  	// Test stopping when position is already reached.
  1825  	query = binlogplayer.StartVReplicationUntil(id, stopPos)
  1826  	if _, err := playerEngine.Exec(query); err != nil {
  1827  		t.Fatal(err)
  1828  	}
  1829  	expectDBClientQueries(t, qh.Expect(
  1830  		"/update.*'Running'",
  1831  		// Second update is from vreplicator.
  1832  		"/update _vt.vreplication set message='Picked source tablet.*",
  1833  		"/update.*'Running'",
  1834  		"/update.*'Stopped'.*already reached",
  1835  	))
  1836  }
  1837  
  1838  func TestPlayerStopAtOther(t *testing.T) {
  1839  	t.Skip("This test was written to verify a bug fix, but is extremely flaky. Only a manual test is possible")
  1840  
  1841  	defer deleteTablet(addTablet(100))
  1842  
  1843  	execStatements(t, []string{
  1844  		"create table t1(id int, val varbinary(128), primary key(id))",
  1845  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  1846  	})
  1847  	defer execStatements(t, []string{
  1848  		"drop table t1",
  1849  		fmt.Sprintf("drop table %s.t1", vrepldb),
  1850  	})
  1851  	env.SchemaEngine.Reload(context.Background())
  1852  
  1853  	// Insert a source row.
  1854  	execStatements(t, []string{
  1855  		"insert into t1 values(1, 'aaa')",
  1856  	})
  1857  	startPos := primaryPosition(t)
  1858  	filter := &binlogdatapb.Filter{
  1859  		Rules: []*binlogdatapb.Rule{{
  1860  			Match: "/.*",
  1861  		}},
  1862  	}
  1863  	bls := &binlogdatapb.BinlogSource{
  1864  		Keyspace: env.KeyspaceName,
  1865  		Shard:    env.ShardName,
  1866  		Filter:   filter,
  1867  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1868  	}
  1869  	query := binlogplayer.CreateVReplicationState("test", bls, startPos, binlogplayer.BlpStopped, vrepldb, 0, 0)
  1870  	qr, err := playerEngine.Exec(query)
  1871  	if err != nil {
  1872  		t.Fatal(err)
  1873  	}
  1874  	id := uint32(qr.InsertID)
  1875  	for q := range globalDBQueries {
  1876  		if strings.HasPrefix(q, "insert into _vt.vreplication") {
  1877  			break
  1878  		}
  1879  	}
  1880  	defer func() {
  1881  		if _, err := playerEngine.Exec(fmt.Sprintf("delete from _vt.vreplication where id = %d", id)); err != nil {
  1882  			t.Fatal(err)
  1883  		}
  1884  		expectDeleteQueries(t)
  1885  	}()
  1886  
  1887  	vconn := &realDBClient{nolog: true}
  1888  	if err := vconn.Connect(); err != nil {
  1889  		t.Error(err)
  1890  	}
  1891  	defer vconn.Close()
  1892  
  1893  	// Insert the same row on the target and lock it.
  1894  	if _, err := vconn.ExecuteFetch("insert into t1 values(1, 'aaa')", 1); err != nil {
  1895  		t.Error(err)
  1896  	}
  1897  	if _, err := vconn.ExecuteFetch("begin", 1); err != nil {
  1898  		t.Error(err)
  1899  	}
  1900  	if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil {
  1901  		t.Error(err)
  1902  	}
  1903  
  1904  	// Start a VReplication where the first transaction updates the locked row.
  1905  	// It will cause the apply to wait, which will cause the other two events
  1906  	// to accumulate. The stop position will be on the grant.
  1907  	// We're testing the behavior where an OTHER transaction is part of a batch,
  1908  	// we have to commit its stop position correctly.
  1909  	execStatements(t, []string{
  1910  		"update t1 set val='ccc' where id=1",
  1911  		"insert into t1 values(2, 'ddd')",
  1912  		"grant select on *.* to 'vt_app'@'127.0.0.1'",
  1913  	})
  1914  	stopPos := primaryPosition(t)
  1915  	query = binlogplayer.StartVReplicationUntil(id, stopPos)
  1916  	if _, err := playerEngine.Exec(query); err != nil {
  1917  		t.Fatal(err)
  1918  	}
  1919  
  1920  	// Wait for the begin. The update will be blocked.
  1921  	expectDBClientQueries(t, qh.Expect(
  1922  		"/update.*'Running'",
  1923  		// Second update is from vreplicator.
  1924  		"/update.*'Running'",
  1925  		"begin",
  1926  	))
  1927  
  1928  	// Give time for the other two transactions to reach the relay log.
  1929  	time.Sleep(100 * time.Millisecond)
  1930  	_, _ = vconn.ExecuteFetch("rollback", 1)
  1931  
  1932  	// This is approximately the expected sequence of updates.
  1933  	expectDBClientQueries(t, qh.Expect(
  1934  		"update t1 set val='ccc' where id=1",
  1935  		"/update _vt.vreplication set pos=",
  1936  		"commit",
  1937  		"begin",
  1938  		"insert into t1(id,val) values (2,'ddd')",
  1939  		"/update _vt.vreplication set pos=",
  1940  		"commit",
  1941  		fmt.Sprintf("/update _vt.vreplication set pos='%s'", stopPos),
  1942  		"/update.*'Stopped'",
  1943  	))
  1944  }
  1945  
  1946  func TestPlayerIdleUpdate(t *testing.T) {
  1947  	defer deleteTablet(addTablet(100))
  1948  
  1949  	savedIdleTimeout := idleTimeout
  1950  	defer func() { idleTimeout = savedIdleTimeout }()
  1951  	idleTimeout = 100 * time.Millisecond
  1952  
  1953  	execStatements(t, []string{
  1954  		"create table t1(id int, val varbinary(128), primary key(id))",
  1955  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  1956  	})
  1957  	defer execStatements(t, []string{
  1958  		"drop table t1",
  1959  		fmt.Sprintf("drop table %s.t1", vrepldb),
  1960  	})
  1961  	env.SchemaEngine.Reload(context.Background())
  1962  
  1963  	filter := &binlogdatapb.Filter{
  1964  		Rules: []*binlogdatapb.Rule{{
  1965  			Match: "/.*",
  1966  		}},
  1967  	}
  1968  	bls := &binlogdatapb.BinlogSource{
  1969  		Keyspace: env.KeyspaceName,
  1970  		Shard:    env.ShardName,
  1971  		Filter:   filter,
  1972  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  1973  	}
  1974  	cancel, _ := startVReplication(t, bls, "")
  1975  	defer cancel()
  1976  
  1977  	execStatements(t, []string{
  1978  		"insert into t1 values(1, 'aaa')",
  1979  	})
  1980  	start := time.Now()
  1981  	expectDBClientQueries(t, qh.Expect(
  1982  		"begin",
  1983  		"insert into t1(id,val) values (1,'aaa')",
  1984  		"/update _vt.vreplication set pos=",
  1985  		"commit",
  1986  	),
  1987  		"/update _vt.vreplication set pos=",
  1988  	)
  1989  	// The above write will generate a new binlog event, and
  1990  	// that event will loopback into player as an empty event.
  1991  	// But it must not get saved until idleTimeout has passed.
  1992  	// The exact positions are hard to verify because of this
  1993  	// loopback mechanism.
  1994  	expectDBClientQueries(t, qh.Expect(
  1995  		"/update _vt.vreplication set pos=",
  1996  	))
  1997  	if duration := time.Since(start); duration < idleTimeout {
  1998  		t.Errorf("duration: %v, must be at least %v", duration, idleTimeout)
  1999  	}
  2000  }
  2001  
  2002  func TestPlayerSplitTransaction(t *testing.T) {
  2003  	defer deleteTablet(addTablet(100))
  2004  	setFlag("vstream_packet_size", "10")
  2005  	defer setFlag("vstream_packet_size", "10000")
  2006  
  2007  	execStatements(t, []string{
  2008  		"create table t1(id int, val varbinary(128), primary key(id))",
  2009  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2010  	})
  2011  	defer execStatements(t, []string{
  2012  		"drop table t1",
  2013  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2014  	})
  2015  	env.SchemaEngine.Reload(context.Background())
  2016  
  2017  	filter := &binlogdatapb.Filter{
  2018  		Rules: []*binlogdatapb.Rule{{
  2019  			Match: "/.*",
  2020  		}},
  2021  	}
  2022  	bls := &binlogdatapb.BinlogSource{
  2023  		Keyspace: env.KeyspaceName,
  2024  		Shard:    env.ShardName,
  2025  		Filter:   filter,
  2026  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2027  	}
  2028  	cancel, _ := startVReplication(t, bls, "")
  2029  	defer cancel()
  2030  
  2031  	execStatements(t, []string{
  2032  		"begin",
  2033  		"insert into t1 values(1, '123456')",
  2034  		"insert into t1 values(2, '789012')",
  2035  		"commit",
  2036  	})
  2037  	// Because the packet size is 10, this is received as two events,
  2038  	// but still combined as one transaction.
  2039  	expectDBClientQueries(t, qh.Expect(
  2040  		"begin",
  2041  		"insert into t1(id,val) values (1,'123456')",
  2042  		"insert into t1(id,val) values (2,'789012')",
  2043  		"/update _vt.vreplication set pos=",
  2044  		"commit",
  2045  	))
  2046  }
  2047  
  2048  func TestPlayerLockErrors(t *testing.T) {
  2049  	defer deleteTablet(addTablet(100))
  2050  
  2051  	execStatements(t, []string{
  2052  		"create table t1(id int, val varbinary(128), primary key(id))",
  2053  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2054  	})
  2055  	defer execStatements(t, []string{
  2056  		"drop table t1",
  2057  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2058  	})
  2059  	env.SchemaEngine.Reload(context.Background())
  2060  
  2061  	filter := &binlogdatapb.Filter{
  2062  		Rules: []*binlogdatapb.Rule{{
  2063  			Match: "/.*",
  2064  		}},
  2065  	}
  2066  	bls := &binlogdatapb.BinlogSource{
  2067  		Keyspace: env.KeyspaceName,
  2068  		Shard:    env.ShardName,
  2069  		Filter:   filter,
  2070  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2071  	}
  2072  	cancel, _ := startVReplication(t, bls, "")
  2073  	defer cancel()
  2074  
  2075  	execStatements(t, []string{
  2076  		"begin",
  2077  		"insert into t1 values(1, 'aaa')",
  2078  		"insert into t1 values(2, 'bbb')",
  2079  		"commit",
  2080  	})
  2081  	expectDBClientQueries(t, qh.Expect(
  2082  		"begin",
  2083  		"insert into t1(id,val) values (1,'aaa')",
  2084  		"insert into t1(id,val) values (2,'bbb')",
  2085  		"/update _vt.vreplication set pos=",
  2086  		"commit",
  2087  	))
  2088  
  2089  	vconn := &realDBClient{nolog: true}
  2090  	if err := vconn.Connect(); err != nil {
  2091  		t.Error(err)
  2092  	}
  2093  	defer vconn.Close()
  2094  
  2095  	// Start a transaction and lock the second row.
  2096  	if _, err := vconn.ExecuteFetch("begin", 1); err != nil {
  2097  		t.Error(err)
  2098  	}
  2099  	if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=2", 1); err != nil {
  2100  		t.Error(err)
  2101  	}
  2102  
  2103  	execStatements(t, []string{
  2104  		"begin",
  2105  		"update t1 set val='ccc' where id=1",
  2106  		"update t1 set val='ccc' where id=2",
  2107  		"commit",
  2108  	})
  2109  	// The innodb lock wait timeout is set to 1s.
  2110  	expectDBClientQueries(t, qh.Expect(
  2111  		"begin",
  2112  		"update t1 set val='ccc' where id=1",
  2113  		"update t1 set val='ccc' where id=2",
  2114  		"rollback",
  2115  	))
  2116  
  2117  	// Release the lock, and watch the retry go through.
  2118  	_, _ = vconn.ExecuteFetch("rollback", 1)
  2119  	expectDBClientQueries(t, qh.Expect(
  2120  		"begin",
  2121  		"update t1 set val='ccc' where id=1",
  2122  		"update t1 set val='ccc' where id=2",
  2123  		"/update _vt.vreplication set pos=",
  2124  		"commit",
  2125  	))
  2126  }
  2127  
  2128  func TestPlayerCancelOnLock(t *testing.T) {
  2129  	defer deleteTablet(addTablet(100))
  2130  
  2131  	execStatements(t, []string{
  2132  		"create table t1(id int, val varbinary(128), primary key(id))",
  2133  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2134  	})
  2135  	defer execStatements(t, []string{
  2136  		"drop table t1",
  2137  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2138  	})
  2139  	env.SchemaEngine.Reload(context.Background())
  2140  
  2141  	filter := &binlogdatapb.Filter{
  2142  		Rules: []*binlogdatapb.Rule{{
  2143  			Match: "/.*",
  2144  		}},
  2145  	}
  2146  	bls := &binlogdatapb.BinlogSource{
  2147  		Keyspace: env.KeyspaceName,
  2148  		Shard:    env.ShardName,
  2149  		Filter:   filter,
  2150  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2151  	}
  2152  	cancel, _ := startVReplication(t, bls, "")
  2153  	defer cancel()
  2154  
  2155  	execStatements(t, []string{
  2156  		"begin",
  2157  		"insert into t1 values(1, 'aaa')",
  2158  		"commit",
  2159  	})
  2160  	expectDBClientQueries(t, qh.Expect(
  2161  		"begin",
  2162  		"insert into t1(id,val) values (1,'aaa')",
  2163  		"/update _vt.vreplication set pos=",
  2164  		"commit",
  2165  	))
  2166  
  2167  	vconn := &realDBClient{nolog: true}
  2168  	if err := vconn.Connect(); err != nil {
  2169  		t.Error(err)
  2170  	}
  2171  	defer vconn.Close()
  2172  
  2173  	// Start a transaction and lock the row.
  2174  	if _, err := vconn.ExecuteFetch("begin", 1); err != nil {
  2175  		t.Error(err)
  2176  	}
  2177  	if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil {
  2178  		t.Error(err)
  2179  	}
  2180  
  2181  	execStatements(t, []string{
  2182  		"begin",
  2183  		"update t1 set val='ccc' where id=1",
  2184  		"commit",
  2185  	})
  2186  	// The innodb lock wait timeout is set to 1s.
  2187  	expectDBClientQueries(t, qh.Expect(
  2188  		"begin",
  2189  		"update t1 set val='ccc' where id=1",
  2190  		"rollback",
  2191  	))
  2192  
  2193  	// VReplication should not get stuck if you cancel now.
  2194  	done := make(chan bool)
  2195  	go func() {
  2196  		cancel()
  2197  		close(done)
  2198  	}()
  2199  	select {
  2200  	case <-done:
  2201  	case <-time.After(5 * time.Second):
  2202  		t.Error("cancel is hung")
  2203  	}
  2204  }
  2205  
  2206  func TestPlayerBatching(t *testing.T) {
  2207  	defer deleteTablet(addTablet(100))
  2208  
  2209  	execStatements(t, []string{
  2210  		"create table t1(id int, val varbinary(128), primary key(id))",
  2211  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2212  	})
  2213  	defer execStatements(t, []string{
  2214  		"drop table t1",
  2215  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2216  	})
  2217  	env.SchemaEngine.Reload(context.Background())
  2218  
  2219  	filter := &binlogdatapb.Filter{
  2220  		Rules: []*binlogdatapb.Rule{{
  2221  			Match: "/.*",
  2222  		}},
  2223  	}
  2224  	bls := &binlogdatapb.BinlogSource{
  2225  		Keyspace: env.KeyspaceName,
  2226  		Shard:    env.ShardName,
  2227  		Filter:   filter,
  2228  		OnDdl:    binlogdatapb.OnDDLAction_EXEC,
  2229  	}
  2230  	cancel, _ := startVReplication(t, bls, "")
  2231  	defer cancel()
  2232  
  2233  	execStatements(t, []string{
  2234  		"insert into t1 values(1, 'aaa')",
  2235  	})
  2236  	expectDBClientQueries(t, qh.Expect(
  2237  		"begin",
  2238  		"insert into t1(id,val) values (1,'aaa')",
  2239  		"/update _vt.vreplication set pos=",
  2240  		"commit",
  2241  	))
  2242  
  2243  	vconn := &realDBClient{nolog: true}
  2244  	if err := vconn.Connect(); err != nil {
  2245  		t.Error(err)
  2246  	}
  2247  	defer vconn.Close()
  2248  
  2249  	// Start a transaction and lock the row.
  2250  	if _, err := vconn.ExecuteFetch("begin", 1); err != nil {
  2251  		t.Error(err)
  2252  	}
  2253  	if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil {
  2254  		t.Error(err)
  2255  	}
  2256  
  2257  	// create one transaction
  2258  	execStatements(t, []string{
  2259  		"update t1 set val='ccc' where id=1",
  2260  	})
  2261  	// Wait for the begin. The update will be blocked.
  2262  	expectDBClientQueries(t, qh.Expect(
  2263  		"begin",
  2264  	))
  2265  
  2266  	// Create two more transactions. They will go and wait in the relayLog.
  2267  	execStatements(t, []string{
  2268  		"insert into t1 values(2, 'aaa')",
  2269  		"insert into t1 values(3, 'aaa')",
  2270  		"alter table t1 add column val2 varbinary(128)",
  2271  		"alter table t1 drop column val2",
  2272  	})
  2273  
  2274  	// Release the lock.
  2275  	_, _ = vconn.ExecuteFetch("rollback", 1)
  2276  	// First transaction will complete. The other two
  2277  	// transactions must be batched into one. But the
  2278  	// DDLs should be on their own.
  2279  	expectDBClientQueries(t, qh.Expect(
  2280  		"update t1 set val='ccc' where id=1",
  2281  		"/update _vt.vreplication set pos=",
  2282  		"commit",
  2283  		"begin",
  2284  		"insert into t1(id,val) values (2,'aaa')",
  2285  		"insert into t1(id,val) values (3,'aaa')",
  2286  		"/update _vt.vreplication set pos=",
  2287  		"commit",
  2288  		"alter table t1 add column val2 varbinary(128)",
  2289  		"/update _vt.vreplication set pos=",
  2290  		"alter table t1 drop column val2",
  2291  		"/update _vt.vreplication set pos=",
  2292  		// The apply of the DDLs on target generates two "other" event.
  2293  		"/update _vt.vreplication set pos=",
  2294  		"/update _vt.vreplication set pos=",
  2295  	))
  2296  }
  2297  
  2298  func TestPlayerRelayLogMaxSize(t *testing.T) {
  2299  	defer deleteTablet(addTablet(100))
  2300  
  2301  	for i := 0; i < 2; i++ {
  2302  		// First iteration checks max size, second checks max items
  2303  		func() {
  2304  			switch i {
  2305  			case 0:
  2306  				savedSize := relayLogMaxSize
  2307  				defer func() { relayLogMaxSize = savedSize }()
  2308  				relayLogMaxSize = 10
  2309  			case 1:
  2310  				savedLen := relayLogMaxItems
  2311  				defer func() { relayLogMaxItems = savedLen }()
  2312  				relayLogMaxItems = 2
  2313  			}
  2314  
  2315  			execStatements(t, []string{
  2316  				"create table t1(id int, val varbinary(128), primary key(id))",
  2317  				fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2318  			})
  2319  			defer execStatements(t, []string{
  2320  				"drop table t1",
  2321  				fmt.Sprintf("drop table %s.t1", vrepldb),
  2322  			})
  2323  			env.SchemaEngine.Reload(context.Background())
  2324  
  2325  			filter := &binlogdatapb.Filter{
  2326  				Rules: []*binlogdatapb.Rule{{
  2327  					Match: "/.*",
  2328  				}},
  2329  			}
  2330  			bls := &binlogdatapb.BinlogSource{
  2331  				Keyspace: env.KeyspaceName,
  2332  				Shard:    env.ShardName,
  2333  				Filter:   filter,
  2334  				OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2335  			}
  2336  			cancel, _ := startVReplication(t, bls, "")
  2337  			defer cancel()
  2338  
  2339  			execStatements(t, []string{
  2340  				"insert into t1 values(1, '123456')",
  2341  			})
  2342  			expectDBClientQueries(t, qh.Expect(
  2343  				"begin",
  2344  				"insert into t1(id,val) values (1,'123456')",
  2345  				"/update _vt.vreplication set pos=",
  2346  				"commit",
  2347  			))
  2348  
  2349  			vconn := &realDBClient{nolog: true}
  2350  			if err := vconn.Connect(); err != nil {
  2351  				t.Error(err)
  2352  			}
  2353  			defer vconn.Close()
  2354  
  2355  			// Start a transaction and lock the row.
  2356  			if _, err := vconn.ExecuteFetch("begin", 1); err != nil {
  2357  				t.Error(err)
  2358  			}
  2359  			if _, err := vconn.ExecuteFetch("update t1 set val='bbb' where id=1", 1); err != nil {
  2360  				t.Error(err)
  2361  			}
  2362  
  2363  			// create one transaction
  2364  			execStatements(t, []string{
  2365  				"update t1 set val='ccc' where id=1",
  2366  			})
  2367  			// Wait for the begin. The update will be blocked.
  2368  			expectDBClientQueries(t, qh.Expect(
  2369  				"begin",
  2370  			))
  2371  
  2372  			// Create two more transactions. They will go and wait in the relayLog.
  2373  			execStatements(t, []string{
  2374  				"insert into t1 values(2, '789012')",
  2375  				"insert into t1 values(3, '345678')",
  2376  				"insert into t1 values(4, '901234')",
  2377  			})
  2378  
  2379  			// Release the lock.
  2380  			_, _ = vconn.ExecuteFetch("rollback", 1)
  2381  			// First transaction will complete. The other two
  2382  			// transactions must be batched into one. The last transaction
  2383  			// will wait to be sent to the relay until the player fetches
  2384  			// them.
  2385  			expectDBClientQueries(t, qh.Expect(
  2386  				"update t1 set val='ccc' where id=1",
  2387  				"/update _vt.vreplication set pos=",
  2388  				"commit",
  2389  				"begin",
  2390  				"insert into t1(id,val) values (2,'789012')",
  2391  				"insert into t1(id,val) values (3,'345678')",
  2392  				"/update _vt.vreplication set pos=",
  2393  				"commit",
  2394  				"begin",
  2395  				"insert into t1(id,val) values (4,'901234')",
  2396  				"/update _vt.vreplication set pos=",
  2397  				"commit",
  2398  			))
  2399  		}()
  2400  	}
  2401  }
  2402  
  2403  func TestRestartOnVStreamEnd(t *testing.T) {
  2404  	defer deleteTablet(addTablet(100))
  2405  
  2406  	savedDelay := retryDelay
  2407  	defer func() { retryDelay = savedDelay }()
  2408  	retryDelay = 1 * time.Millisecond
  2409  
  2410  	execStatements(t, []string{
  2411  		"create table t1(id int, val varbinary(128), primary key(id))",
  2412  		fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb),
  2413  	})
  2414  	defer execStatements(t, []string{
  2415  		"drop table t1",
  2416  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2417  	})
  2418  	env.SchemaEngine.Reload(context.Background())
  2419  
  2420  	filter := &binlogdatapb.Filter{
  2421  		Rules: []*binlogdatapb.Rule{{
  2422  			Match: "/.*",
  2423  		}},
  2424  	}
  2425  	bls := &binlogdatapb.BinlogSource{
  2426  		Keyspace: env.KeyspaceName,
  2427  		Shard:    env.ShardName,
  2428  		Filter:   filter,
  2429  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2430  	}
  2431  	cancel, _ := startVReplication(t, bls, "")
  2432  	defer cancel()
  2433  
  2434  	execStatements(t, []string{
  2435  		"insert into t1 values(1, 'aaa')",
  2436  	})
  2437  	expectDBClientQueries(t, qh.Expect(
  2438  		"begin",
  2439  		"insert into t1(id,val) values (1,'aaa')",
  2440  		"/update _vt.vreplication set pos=",
  2441  		"commit",
  2442  	))
  2443  
  2444  	streamerEngine.Close()
  2445  	expectDBClientQueries(t, qh.Expect(
  2446  		"/update _vt.vreplication set message='vstream ended'",
  2447  	))
  2448  	streamerEngine.Open()
  2449  	execStatements(t, []string{
  2450  		"insert into t1 values(2, 'aaa')",
  2451  	})
  2452  	expectDBClientQueries(t, qh.Expect(
  2453  		"/update _vt.vreplication set message='Picked source tablet.*",
  2454  		"/update _vt.vreplication set state='Running'",
  2455  		"begin",
  2456  		"insert into t1(id,val) values (2,'aaa')",
  2457  		"/update _vt.vreplication set pos=",
  2458  		"commit",
  2459  	))
  2460  }
  2461  
  2462  func TestTimestamp(t *testing.T) {
  2463  	defer deleteTablet(addTablet(100))
  2464  
  2465  	execStatements(t, []string{
  2466  		"create table t1(id int, ts timestamp, dt datetime)",
  2467  		fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb),
  2468  	})
  2469  	defer execStatements(t, []string{
  2470  		"drop table t1",
  2471  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2472  	})
  2473  	env.SchemaEngine.Reload(context.Background())
  2474  
  2475  	filter := &binlogdatapb.Filter{
  2476  		Rules: []*binlogdatapb.Rule{{
  2477  			Match: "/.*",
  2478  		}},
  2479  	}
  2480  
  2481  	bls := &binlogdatapb.BinlogSource{
  2482  		Keyspace: env.KeyspaceName,
  2483  		Shard:    env.ShardName,
  2484  		Filter:   filter,
  2485  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2486  	}
  2487  	cancel, _ := startVReplication(t, bls, "")
  2488  	defer cancel()
  2489  
  2490  	qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()")
  2491  	if err != nil {
  2492  		t.Fatal(err)
  2493  	}
  2494  	want := qr.Rows[0][0].ToString()
  2495  	t.Logf("want: %s", want)
  2496  
  2497  	execStatements(t, []string{
  2498  		fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want),
  2499  	})
  2500  	expectDBClientQueries(t, qh.Expect(
  2501  		"begin",
  2502  		// The insert value for ts will be in UTC.
  2503  		// We'll check the row instead.
  2504  		"/insert into t1",
  2505  		"/update _vt.vreplication set pos=",
  2506  		"commit",
  2507  	))
  2508  
  2509  	expectData(t, "t1", [][]string{{"1", want, want}})
  2510  }
  2511  
  2512  func shouldRunJSONTests(t *testing.T, name string) bool {
  2513  	skipTest := true
  2514  	flavors := []string{"mysql80", "mysql57"}
  2515  	for _, flavor := range flavors {
  2516  		if strings.EqualFold(env.Flavor, flavor) {
  2517  			skipTest = false
  2518  			break
  2519  		}
  2520  	}
  2521  	if skipTest {
  2522  		t.Logf("not running %s on %s", name, env.Flavor)
  2523  		return false
  2524  	}
  2525  	return true
  2526  }
  2527  
  2528  // TestPlayerJSONDocs validates more complex and 'large' json docs. It only validates that the data on target matches that on source.
  2529  // TestPlayerTypes, above, also verifies the sql queries applied on the target.
  2530  func TestPlayerJSONDocs(t *testing.T) {
  2531  	if !shouldRunJSONTests(t, "TestPlayerJSONDocs") {
  2532  		return
  2533  	}
  2534  	defer deleteTablet(addTablet(100))
  2535  
  2536  	execStatements(t, []string{
  2537  		"create table vitess_json(id int auto_increment, val json, primary key(id))",
  2538  		fmt.Sprintf("create table %s.vitess_json(id int, val json, primary key(id))", vrepldb),
  2539  	})
  2540  	defer execStatements(t, []string{
  2541  		"drop table vitess_json",
  2542  		fmt.Sprintf("drop table %s.vitess_json", vrepldb),
  2543  	})
  2544  
  2545  	env.SchemaEngine.Reload(context.Background())
  2546  
  2547  	filter := &binlogdatapb.Filter{
  2548  		Rules: []*binlogdatapb.Rule{{
  2549  			Match: "/.*",
  2550  		}},
  2551  	}
  2552  	bls := &binlogdatapb.BinlogSource{
  2553  		Keyspace: env.KeyspaceName,
  2554  		Shard:    env.ShardName,
  2555  		Filter:   filter,
  2556  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2557  	}
  2558  	cancel, _ := startVReplication(t, bls, "")
  2559  	defer cancel()
  2560  	type testcase struct {
  2561  		name  string
  2562  		input string
  2563  		data  [][]string
  2564  	}
  2565  	var testcases []testcase
  2566  	id := 0
  2567  	var addTestCase = func(name, val string) {
  2568  		id++
  2569  		testcases = append(testcases, testcase{
  2570  			name:  name,
  2571  			input: fmt.Sprintf("insert into vitess_json(val) values (%s)", encodeString(val)),
  2572  			data: [][]string{
  2573  				{strconv.Itoa(id), val},
  2574  			},
  2575  		})
  2576  	}
  2577  	addTestCase("singleDoc", jsonSingleDoc)
  2578  	addTestCase("multipleDocs", jsonMultipleDocs)
  2579  	longString := strings.Repeat("aa", math.MaxInt16)
  2580  
  2581  	largeObject := fmt.Sprintf(singleLargeObjectTemplate, longString)
  2582  	addTestCase("singleLargeObject", largeObject)
  2583  
  2584  	largeArray := fmt.Sprintf(`[1, 1234567890, "a", true, %s]`, largeObject)
  2585  	_ = largeArray
  2586  	addTestCase("singleLargeArray", largeArray)
  2587  
  2588  	// the json doc is repeated multiple times to hit the 64K threshold: 140 is got by trial and error
  2589  	addTestCase("largeArrayDoc", repeatJSON(jsonSingleDoc, 140, largeJSONArrayCollection))
  2590  	addTestCase("largeObjectDoc", repeatJSON(jsonSingleDoc, 140, largeJSONObjectCollection))
  2591  	id = 0
  2592  	for _, tcase := range testcases {
  2593  		t.Run(tcase.name, func(t *testing.T) {
  2594  			id++
  2595  			execStatements(t, []string{tcase.input})
  2596  			want := qh.Expect(
  2597  				"begin",
  2598  				"/insert into vitess_json",
  2599  				"/update _vt.vreplication set pos=",
  2600  				"commit",
  2601  			)
  2602  			expectDBClientQueries(t, want)
  2603  			expectJSON(t, "vitess_json", tcase.data, id, env.Mysqld.FetchSuperQuery)
  2604  		})
  2605  	}
  2606  }
  2607  
  2608  // TestPlayerJSONTwoColumns tests for two json columns in a table
  2609  func TestPlayerJSONTwoColumns(t *testing.T) {
  2610  	if !shouldRunJSONTests(t, "TestPlayerJSONTwoColumns") {
  2611  		return
  2612  	}
  2613  	defer deleteTablet(addTablet(100))
  2614  	execStatements(t, []string{
  2615  		"create table vitess_json2(id int auto_increment, val json, val2 json, primary key(id))",
  2616  		fmt.Sprintf("create table %s.vitess_json2(id int, val json, val2 json, primary key(id))", vrepldb),
  2617  	})
  2618  	defer execStatements(t, []string{
  2619  		"drop table vitess_json2",
  2620  		fmt.Sprintf("drop table %s.vitess_json2", vrepldb),
  2621  	})
  2622  
  2623  	env.SchemaEngine.Reload(context.Background())
  2624  
  2625  	filter := &binlogdatapb.Filter{
  2626  		Rules: []*binlogdatapb.Rule{{
  2627  			Match: "/.*",
  2628  		}},
  2629  	}
  2630  	bls := &binlogdatapb.BinlogSource{
  2631  		Keyspace: env.KeyspaceName,
  2632  		Shard:    env.ShardName,
  2633  		Filter:   filter,
  2634  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2635  	}
  2636  	cancel, _ := startVReplication(t, bls, "")
  2637  	defer cancel()
  2638  	type testcase struct {
  2639  		name  string
  2640  		input string
  2641  		data  [][]string
  2642  	}
  2643  	var testcases []testcase
  2644  	id := 0
  2645  	var addTestCase = func(name, val, val2 string) {
  2646  		id++
  2647  		testcases = append(testcases, testcase{
  2648  			name:  name,
  2649  			input: fmt.Sprintf("insert into vitess_json2(val, val2) values (%s, %s)", encodeString(val), encodeString(val2)),
  2650  			data: [][]string{
  2651  				{strconv.Itoa(id), val, val2},
  2652  			},
  2653  		})
  2654  	}
  2655  	longString := strings.Repeat("aa", math.MaxInt16)
  2656  	largeObject := fmt.Sprintf(singleLargeObjectTemplate, longString)
  2657  	addTestCase("twoCols", jsonSingleDoc, largeObject)
  2658  	id = 0
  2659  	for _, tcase := range testcases {
  2660  		t.Run(tcase.name, func(t *testing.T) {
  2661  			id++
  2662  			execStatements(t, []string{tcase.input})
  2663  			want := qh.Expect(
  2664  				"begin",
  2665  				"/insert into vitess_json2",
  2666  				"/update _vt.vreplication set pos=",
  2667  				"commit",
  2668  			)
  2669  			expectDBClientQueries(t, want)
  2670  			expectJSON(t, "vitess_json2", tcase.data, id, env.Mysqld.FetchSuperQuery)
  2671  		})
  2672  	}
  2673  
  2674  }
  2675  
  2676  func TestVReplicationLogs(t *testing.T) {
  2677  	defer deleteTablet(addTablet(100))
  2678  	dbClient := playerEngine.dbClientFactoryDba()
  2679  	err := dbClient.Connect()
  2680  	require.NoError(t, err)
  2681  	defer dbClient.Close()
  2682  	vdbc := newVDBClient(dbClient, binlogplayer.NewStats())
  2683  	query := "select vrepl_id, state, message, count from _vt.vreplication_log order by id desc limit 1"
  2684  
  2685  	expected := []string{
  2686  		"[[INT32(1) VARBINARY(\"Running\") TEXT(\"message1\") INT64(1)]]",
  2687  		"[[INT32(1) VARBINARY(\"Running\") TEXT(\"message1\") INT64(2)]]",
  2688  	}
  2689  
  2690  	for _, want := range expected {
  2691  		t.Run("", func(t *testing.T) {
  2692  			err = insertLog(vdbc, LogMessage, 1, "Running", "message1")
  2693  			require.NoError(t, err)
  2694  			qr, err := env.Mysqld.FetchSuperQuery(context.Background(), query)
  2695  			require.NoError(t, err)
  2696  			require.Equal(t, want, fmt.Sprintf("%v", qr.Rows))
  2697  		})
  2698  
  2699  	}
  2700  }
  2701  
  2702  func TestGeneratedColumns(t *testing.T) {
  2703  	flavor := strings.ToLower(env.Flavor)
  2704  	// Disable tests on percona (which identifies as mysql56) and mariadb platforms in CI since they
  2705  	// generated columns support was added in 5.7 and mariadb added mysql compatible generated columns in 10.2
  2706  	if !strings.Contains(flavor, "mysql57") && !strings.Contains(flavor, "mysql80") {
  2707  		return
  2708  	}
  2709  	defer deleteTablet(addTablet(100))
  2710  
  2711  	execStatements(t, []string{
  2712  		"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))",
  2713  		fmt.Sprintf("create table %s.t1(id int, val varbinary(6), val2 varbinary(6) as (concat(id, val)), val3 varbinary(6), id2 int, primary key(id))", vrepldb),
  2714  		"create table t2(id int, val varbinary(128), val2 varbinary(128) as (concat(id, val)) stored, val3 varbinary(128) as (concat(val, id)), id2 int, primary key(id))",
  2715  		fmt.Sprintf("create table %s.t2(id int, val3 varbinary(128), val varbinary(128), id2 int, primary key(id))", vrepldb),
  2716  	})
  2717  	defer execStatements(t, []string{
  2718  		"drop table t1",
  2719  		fmt.Sprintf("drop table %s.t1", vrepldb),
  2720  		"drop table t2",
  2721  		fmt.Sprintf("drop table %s.t2", vrepldb),
  2722  	})
  2723  	env.SchemaEngine.Reload(context.Background())
  2724  
  2725  	filter := &binlogdatapb.Filter{
  2726  		Rules: []*binlogdatapb.Rule{{
  2727  			Match:  "t1",
  2728  			Filter: "select * from t1",
  2729  		}, {
  2730  			Match:  "t2",
  2731  			Filter: "select id, val3, val, id2 from t2",
  2732  		}},
  2733  	}
  2734  	bls := &binlogdatapb.BinlogSource{
  2735  		Keyspace: env.KeyspaceName,
  2736  		Shard:    env.ShardName,
  2737  		Filter:   filter,
  2738  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2739  	}
  2740  	cancel, _ := startVReplication(t, bls, "")
  2741  	defer cancel()
  2742  
  2743  	testcases := []struct {
  2744  		input  string
  2745  		output string
  2746  		table  string
  2747  		data   [][]string
  2748  	}{{
  2749  		input:  "insert into t1(id, val, id2) values (1, 'aaa', 10)",
  2750  		output: "insert into t1(id,val,val3,id2) values (1,'aaa','aaa1',10)",
  2751  		table:  "t1",
  2752  		data: [][]string{
  2753  			{"1", "aaa", "1aaa", "aaa1", "10"},
  2754  		},
  2755  	}, {
  2756  		input:  "update t1 set val = 'bbb', id2 = 11 where id = 1",
  2757  		output: "update t1 set val='bbb', val3='bbb1', id2=11 where id=1",
  2758  		table:  "t1",
  2759  		data: [][]string{
  2760  			{"1", "bbb", "1bbb", "bbb1", "11"},
  2761  		},
  2762  	}, {
  2763  		input:  "insert into t2(id, val, id2) values (1, 'aaa', 10)",
  2764  		output: "insert into t2(id,val3,val,id2) values (1,'aaa1','aaa',10)",
  2765  		table:  "t2",
  2766  		data: [][]string{
  2767  			{"1", "aaa1", "aaa", "10"},
  2768  		},
  2769  	}, {
  2770  		input:  "update t2 set val = 'bbb', id2 = 11 where id = 1",
  2771  		output: "update t2 set val3='bbb1', val='bbb', id2=11 where id=1",
  2772  		table:  "t2",
  2773  		data: [][]string{
  2774  			{"1", "bbb1", "bbb", "11"},
  2775  		},
  2776  	}}
  2777  
  2778  	for _, tcases := range testcases {
  2779  		execStatements(t, []string{tcases.input})
  2780  		output := qh.Expect(tcases.output)
  2781  		expectNontxQueries(t, output)
  2782  		if tcases.table != "" {
  2783  			expectData(t, tcases.table, tcases.data)
  2784  		}
  2785  	}
  2786  }
  2787  
  2788  func TestPlayerInvalidDates(t *testing.T) {
  2789  	defer deleteTablet(addTablet(100))
  2790  
  2791  	execStatements(t, []string{
  2792  		"create table src1(id int, dt date, primary key(id))",
  2793  		fmt.Sprintf("create table %s.dst1(id int, dt date, primary key(id))", vrepldb),
  2794  	})
  2795  	defer execStatements(t, []string{
  2796  		"drop table src1",
  2797  		fmt.Sprintf("drop table %s.dst1", vrepldb),
  2798  	})
  2799  	pos := primaryPosition(t)
  2800  	execStatements(t, []string{"set sql_mode='';insert into src1 values(1, '0000-00-00');set sql_mode='STRICT_TRANS_TABLES';"})
  2801  	env.SchemaEngine.Reload(context.Background())
  2802  
  2803  	// default mysql flavor allows invalid dates: so disallow explicitly for this test
  2804  	if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(REPLACE(@@session.sql_mode, 'NO_ZERO_DATE', ''), 'NO_ZERO_IN_DATE', '')"); err != nil {
  2805  		fmt.Fprintf(os.Stderr, "%v", err)
  2806  	}
  2807  	defer func() {
  2808  		if err := env.Mysqld.ExecuteSuperQuery(context.Background(), "SET @@global.sql_mode=REPLACE(@@global.sql_mode, ',NO_ZERO_DATE,NO_ZERO_IN_DATE','')"); err != nil {
  2809  			fmt.Fprintf(os.Stderr, "%v", err)
  2810  		}
  2811  	}()
  2812  
  2813  	filter := &binlogdatapb.Filter{
  2814  		Rules: []*binlogdatapb.Rule{{
  2815  			Match:  "dst1",
  2816  			Filter: "select * from src1",
  2817  		}},
  2818  	}
  2819  	bls := &binlogdatapb.BinlogSource{
  2820  		Keyspace: env.KeyspaceName,
  2821  		Shard:    env.ShardName,
  2822  		Filter:   filter,
  2823  		OnDdl:    binlogdatapb.OnDDLAction_IGNORE,
  2824  	}
  2825  	cancel, _ := startVReplication(t, bls, pos)
  2826  	defer cancel()
  2827  	testcases := []struct {
  2828  		input  string
  2829  		output string
  2830  		table  string
  2831  		data   [][]string
  2832  	}{{
  2833  		input:  "select 1 from dual",
  2834  		output: "insert into dst1(id,dt) values (1,'0000-00-00')",
  2835  		table:  "dst1",
  2836  		data: [][]string{
  2837  			{"1", "0000-00-00"},
  2838  		},
  2839  	}, {
  2840  		input:  "insert into src1 values (2, '2020-01-01')",
  2841  		output: "insert into dst1(id,dt) values (2,'2020-01-01')",
  2842  		table:  "dst1",
  2843  		data: [][]string{
  2844  			{"1", "0000-00-00"},
  2845  			{"2", "2020-01-01"},
  2846  		},
  2847  	}}
  2848  
  2849  	for _, tcases := range testcases {
  2850  		execStatements(t, []string{tcases.input})
  2851  		output := qh.Expect(tcases.output)
  2852  		expectNontxQueries(t, output)
  2853  
  2854  		if tcases.table != "" {
  2855  			// without the sleep there is a flakiness where row inserted by vreplication is not visible to vdbclient
  2856  			time.Sleep(100 * time.Millisecond)
  2857  			expectData(t, tcases.table, tcases.data)
  2858  		}
  2859  	}
  2860  }
  2861  func expectJSON(t *testing.T, table string, values [][]string, id int, exec func(ctx context.Context, query string) (*sqltypes.Result, error)) {
  2862  	t.Helper()
  2863  
  2864  	var query string
  2865  	if len(strings.Split(table, ".")) == 1 {
  2866  		query = fmt.Sprintf("select * from %s.%s where id=%d", vrepldb, table, id)
  2867  	} else {
  2868  		query = fmt.Sprintf("select * from %s where id=%d", table, id)
  2869  	}
  2870  	qr, err := exec(context.Background(), query)
  2871  	if err != nil {
  2872  		t.Error(err)
  2873  		return
  2874  	}
  2875  	if len(values) != len(qr.Rows) {
  2876  		t.Fatalf("row counts don't match: %d, want %d", len(qr.Rows), len(values))
  2877  	}
  2878  	for i, row := range values {
  2879  		if len(row) != len(qr.Rows[i]) {
  2880  			t.Fatalf("Too few columns, \nrow: %d, \nresult: %d:%v, \nwant: %d:%v", i, len(qr.Rows[i]), qr.Rows[i], len(row), row)
  2881  		}
  2882  		if qr.Rows[i][0].ToString() != row[0] {
  2883  			t.Fatalf("Id mismatch: want %s, got %s", qr.Rows[i][0].ToString(), row[0])
  2884  		}
  2885  		got, err := ajson.Unmarshal([]byte(qr.Rows[i][1].ToString()))
  2886  		require.NoError(t, err)
  2887  		want, err := ajson.Unmarshal([]byte(row[1]))
  2888  		require.NoError(t, err)
  2889  		match, err := got.Eq(want)
  2890  		require.NoError(t, err)
  2891  		require.True(t, match)
  2892  	}
  2893  }
  2894  
  2895  func startVReplication(t *testing.T, bls *binlogdatapb.BinlogSource, pos string) (cancelFunc func(), id int) {
  2896  	t.Helper()
  2897  
  2898  	if pos == "" {
  2899  		pos = primaryPosition(t)
  2900  	}
  2901  	query := binlogplayer.CreateVReplication("test", bls, pos, 9223372036854775807, 9223372036854775807, 0, vrepldb, 0, 0, false)
  2902  	qr, err := playerEngine.Exec(query)
  2903  	if err != nil {
  2904  		t.Fatal(err)
  2905  	}
  2906  	expectDBClientQueries(t, qh.Expect(
  2907  		"/insert into _vt.vreplication",
  2908  		"/update _vt.vreplication set message='Picked source tablet.*",
  2909  		"/update _vt.vreplication set state='Running'",
  2910  	))
  2911  	var once sync.Once
  2912  	return func() {
  2913  		t.Helper()
  2914  		once.Do(func() {
  2915  			query := fmt.Sprintf("delete from _vt.vreplication where id = %d", qr.InsertID)
  2916  			if _, err := playerEngine.Exec(query); err != nil {
  2917  				t.Fatal(err)
  2918  			}
  2919  			expectDeleteQueries(t)
  2920  		})
  2921  	}, int(qr.InsertID)
  2922  }