vitess.io/vitess@v0.16.2/go/mysql/endtoend/replication_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 endtoend
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"reflect"
    23  	"strings"
    24  	"sync"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/require"
    30  
    31  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    32  
    33  	"context"
    34  
    35  	"vitess.io/vitess/go/mysql"
    36  	"vitess.io/vitess/go/sqltypes"
    37  
    38  	querypb "vitess.io/vitess/go/vt/proto/query"
    39  )
    40  
    41  // connectForReplication is a helper method to connect for replication
    42  // from the current binlog position.
    43  func connectForReplication(t *testing.T, rbr bool) (*mysql.Conn, mysql.BinlogFormat) {
    44  	ctx := context.Background()
    45  	conn, err := mysql.Connect(ctx, &connParams)
    46  	if err != nil {
    47  		t.Fatal(err)
    48  	}
    49  
    50  	// We need to know if this is MariaDB, to set the right flag.
    51  	if conn.IsMariaDB() {
    52  		// This flag is required to get GTIDs from MariaDB.
    53  		t.Log("MariaDB: sensing SET @mariadb_slave_capability=4")
    54  		if _, err := conn.ExecuteFetch("SET @mariadb_slave_capability=4", 0, false); err != nil {
    55  			t.Fatalf("failed to set @mariadb_slave_capability=4: %v", err)
    56  		}
    57  	}
    58  
    59  	// Switch server to RBR if needed.
    60  	if rbr {
    61  		if _, err := conn.ExecuteFetch("SET GLOBAL binlog_format='ROW'", 0, false); err != nil {
    62  			t.Fatalf("SET GLOBAL binlog_format='ROW' failed: %v", err)
    63  		}
    64  	}
    65  
    66  	// First we get the current binlog position.
    67  	result, err := conn.ExecuteFetch("SHOW MASTER STATUS", 1, true)
    68  	require.NoError(t, err, "SHOW MASTER STATUS failed: %v", err)
    69  
    70  	if len(result.Fields) < 2 || result.Fields[0].Name != "File" || result.Fields[1].Name != "Position" ||
    71  		len(result.Rows) != 1 {
    72  		t.Fatalf("SHOW MASTER STATUS returned unexpected result: %v", result)
    73  	}
    74  	file := result.Rows[0][0].ToString()
    75  	position, err := evalengine.ToUint64(result.Rows[0][1])
    76  	require.NoError(t, err, "SHOW MASTER STATUS returned invalid position: %v", result.Rows[0][1])
    77  
    78  	// Tell the server that we understand the format of events
    79  	// that will be used if binlog_checksum is enabled on the server.
    80  	if _, err := conn.ExecuteFetch("SET @master_binlog_checksum=@@global.binlog_checksum", 0, false); err != nil {
    81  		t.Fatalf("failed to set @master_binlog_checksum=@@global.binlog_checksum: %v", err)
    82  	}
    83  
    84  	// Write ComBinlogDump packet with to start streaming events from here.
    85  	if err := conn.WriteComBinlogDump(1, file, uint32(position), 0); err != nil {
    86  		t.Fatalf("WriteComBinlogDump failed: %v", err)
    87  	}
    88  
    89  	// Wait for the FORMAT_DESCRIPTION_EVENT
    90  	var f mysql.BinlogFormat
    91  	for {
    92  		be, err := conn.ReadBinlogEvent()
    93  		require.NoError(t, err, "ReadPacket failed: %v", err)
    94  		require.True(t, be.IsValid(), "NewMysql56BinlogEvent has an invalid packet: %v", be)
    95  
    96  		// Skip rotate packets. These are normal as first packets.
    97  		if be.IsRotate() {
    98  			t.Logf("Got a rotate packet: %v", be)
    99  			continue
   100  		}
   101  		// And we want a FORMAT_DESCRIPTION_EVENT.
   102  		// Print a few things about the event for sanity checks.
   103  		require.True(t, be.IsFormatDescription(), "Unexpected packet: %v", be)
   104  
   105  		f, err = be.Format()
   106  		require.NoError(t, err, "Format() returned error: %v", err)
   107  
   108  		t.Logf("Got a FORMAT_DESCRIPTION_EVENT packet: %v\nWith format: %v", be, f)
   109  		break
   110  	}
   111  
   112  	return conn, f
   113  }
   114  
   115  // TestReplicationConnectionClosing connects as a replication client,
   116  // gets the first packet, then waits a few milliseconds and closes the
   117  // connection. We should get the right error.
   118  func TestReplicationConnectionClosing(t *testing.T) {
   119  	conn, _ := connectForReplication(t, false /* rbr */)
   120  	defer conn.Close()
   121  
   122  	// One go routine is waiting on events.
   123  	wg := sync.WaitGroup{}
   124  	wg.Add(1)
   125  	go func() {
   126  		defer wg.Done()
   127  
   128  		for {
   129  			data, err := conn.ReadPacket()
   130  			if err != nil {
   131  				serr, ok := err.(*mysql.SQLError)
   132  				assert.True(t, ok, "Got a non mysql.SQLError error: %v", err)
   133  				assert.Equal(t, mysql.CRServerLost, serr.Num, "Got an unexpected mysql.SQLError error: %v", serr)
   134  
   135  				// we got the right error, all good.
   136  				return
   137  			}
   138  
   139  			// Make sure it's a replication packet.
   140  			switch data[0] {
   141  			case mysql.OKPacket:
   142  				// What we expect, keep going.
   143  			case mysql.ErrPacket:
   144  				err := mysql.ParseErrorPacket(data)
   145  				t.Errorf("ReadPacket returned an error packet: %v", err)
   146  			default:
   147  				// Very unexpected.
   148  				t.Errorf("ReadPacket returned a weird packet: %v", data)
   149  			}
   150  		}
   151  	}()
   152  
   153  	// Connect and create a table.
   154  	ctx := context.Background()
   155  	dConn, err := mysql.Connect(ctx, &connParams)
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  	defer dConn.Close()
   160  	createTable := "create table replicationError(id int, name varchar(128), primary key(id))"
   161  	if _, err := dConn.ExecuteFetch(createTable, 0, false); err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	result, err := dConn.ExecuteFetch("insert into replicationError(id, name) values(10, 'nice name')", 0, false)
   165  	require.NoError(t, err, "insert failed: %v", err)
   166  
   167  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
   168  		t.Errorf("unexpected result for insert: %v", result)
   169  	}
   170  	if _, err := dConn.ExecuteFetch("drop table replicationError", 0, false); err != nil {
   171  		t.Fatalf("drop table failed: %v", err)
   172  	}
   173  
   174  	// wait for a few milliseconds.
   175  	time.Sleep(10 * time.Millisecond)
   176  
   177  	// Close the replication connection, hopefully while we are
   178  	// reading packets from it.
   179  	conn.Close()
   180  
   181  	// And we wait for background routine to exit.
   182  	wg.Wait()
   183  }
   184  
   185  func TestRowReplicationWithRealDatabase(t *testing.T) {
   186  	conn, f := connectForReplication(t, true /* rbr */)
   187  	defer conn.Close()
   188  
   189  	// Create a table, insert some data in it.
   190  	ctx := context.Background()
   191  	dConn, err := mysql.Connect(ctx, &connParams)
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  	defer dConn.Close()
   196  	createTable := "create table replication(id int, name varchar(128), primary key(id))"
   197  	if _, err := dConn.ExecuteFetch(createTable, 0, false); err != nil {
   198  		t.Fatal(err)
   199  	}
   200  	result, err := dConn.ExecuteFetch("insert into replication(id, name) values(10, 'nice name')", 0, false)
   201  	require.NoError(t, err, "insert failed: %v", err)
   202  
   203  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
   204  		t.Errorf("unexpected result for insert: %v", result)
   205  	}
   206  	result, err = dConn.ExecuteFetch("update replication set name='nicer name' where id=10", 0, false)
   207  	require.NoError(t, err, "update failed: %v", err)
   208  
   209  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
   210  		t.Errorf("unexpected result for update: %v", result)
   211  	}
   212  	result, err = dConn.ExecuteFetch("delete from replication where id=10", 0, false)
   213  	require.NoError(t, err, "delete failed: %v", err)
   214  
   215  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
   216  		t.Errorf("unexpected result for delete: %v", result)
   217  	}
   218  
   219  	// Get the new events from the binlogs.
   220  	// Make sure we get four GTIDs, the table creation event, the
   221  	// table map event, and the insert/update/delete (with both a begin and a commit).
   222  	gtidCount := 0
   223  	beginCount := 0
   224  	commitCount := 0
   225  	gotCreateTable := false
   226  	gotTableMapEvent := false
   227  	gotInsert := false
   228  	gotUpdate := false
   229  	gotDelete := false
   230  
   231  	var tableID uint64
   232  	var tableMap *mysql.TableMap
   233  
   234  	//	for i := 0; i < 6 && (gtidCount < 2 || !gotCreateTable || !gotTableMapEvent || !gotBegin || !gotInsert || !gotCommit); i++ {
   235  	for gtidCount < 4 || !gotCreateTable || !gotTableMapEvent || !gotInsert || !gotUpdate || !gotDelete || beginCount != 3 || commitCount != 3 {
   236  		be, err := conn.ReadBinlogEvent()
   237  		require.NoError(t, err, "ReadPacket failed: %v", err)
   238  		require.True(t, be.IsValid(), "read an invalid packet: %v", be)
   239  
   240  		be, _, err = be.StripChecksum(f)
   241  		require.NoError(t, err, "StripChecksum failed: %v", err)
   242  
   243  		switch {
   244  		case be.IsGTID():
   245  			// We expect one of these at least.
   246  			gtid, hasBegin, err := be.GTID(f)
   247  			if err != nil {
   248  				t.Fatalf("GTID event is broken: %v", err)
   249  			}
   250  			t.Logf("Got GTID event: %v %v", gtid, hasBegin)
   251  			gtidCount++
   252  			if hasBegin {
   253  				beginCount++
   254  			}
   255  		case be.IsQuery():
   256  			q, err := be.Query(f)
   257  			if err != nil {
   258  				t.Fatalf("Query event is broken: %v", err)
   259  			}
   260  			t.Logf("Got Query event: %v", q)
   261  			switch strings.ToLower(q.SQL) {
   262  			case createTable:
   263  				gotCreateTable = true
   264  			case "begin":
   265  				beginCount++
   266  			case "commit":
   267  				commitCount++
   268  			}
   269  		case be.IsXID():
   270  			commitCount++
   271  			t.Logf("Got XID event")
   272  		case be.IsTableMap():
   273  			tableID = be.TableID(f) // This would be 0x00ffffff for an event to clear all table map entries.
   274  			var err error
   275  			tableMap, err = be.TableMap(f)
   276  			if err != nil {
   277  				t.Fatalf("TableMap event is broken: %v", err)
   278  			}
   279  			t.Logf("Got Table Map event: %v %v", tableID, tableMap)
   280  			if tableMap.Database != "vttest" ||
   281  				tableMap.Name != "replication" ||
   282  				len(tableMap.Types) != 2 ||
   283  				tableMap.CanBeNull.Bit(0) ||
   284  				!tableMap.CanBeNull.Bit(1) {
   285  				t.Errorf("got wrong TableMap: %v", tableMap)
   286  			}
   287  			gotTableMapEvent = true
   288  		case be.IsWriteRows():
   289  			if got := be.TableID(f); got != tableID {
   290  				t.Fatalf("WriteRows event got table ID %v but was expecting %v", got, tableID)
   291  			}
   292  			wr, err := be.Rows(f, tableMap)
   293  			if err != nil {
   294  				t.Fatalf("Rows event is broken: %v", err)
   295  			}
   296  
   297  			// Check it has 2 rows, and first value is '10', second value is 'nice name'.
   298  			values, _ := wr.StringValuesForTests(tableMap, 0)
   299  			t.Logf("Got WriteRows event data: %v %v", wr, values)
   300  			if expected := []string{"10", "nice name"}; !reflect.DeepEqual(values, expected) {
   301  				t.Fatalf("StringValues returned %v, expected %v", values, expected)
   302  			}
   303  
   304  			gotInsert = true
   305  		case be.IsUpdateRows():
   306  			if got := be.TableID(f); got != tableID {
   307  				t.Fatalf("UpdateRows event got table ID %v but was expecting %v", got, tableID)
   308  			}
   309  			ur, err := be.Rows(f, tableMap)
   310  			if err != nil {
   311  				t.Fatalf("UpdateRows event is broken: %v", err)
   312  			}
   313  
   314  			// Check it has 2 identify rows, and first value is '10', second value is 'nice name'.
   315  			values, _ := ur.StringIdentifiesForTests(tableMap, 0)
   316  			t.Logf("Got UpdateRows event identify: %v %v", ur, values)
   317  			if expected := []string{"10", "nice name"}; !reflect.DeepEqual(values, expected) {
   318  				t.Fatalf("StringIdentifies returned %v, expected %v", values, expected)
   319  			}
   320  
   321  			// Check it has 2 values rows, and first value is '10', second value is 'nicer name'.
   322  			values, _ = ur.StringValuesForTests(tableMap, 0)
   323  			t.Logf("Got UpdateRows event data: %v %v", ur, values)
   324  			if expected := []string{"10", "nicer name"}; !reflect.DeepEqual(values, expected) {
   325  				t.Fatalf("StringValues returned %v, expected %v", values, expected)
   326  			}
   327  
   328  			gotUpdate = true
   329  		case be.IsDeleteRows():
   330  			if got := be.TableID(f); got != tableID {
   331  				t.Fatalf("DeleteRows event got table ID %v but was expecting %v", got, tableID)
   332  			}
   333  			dr, err := be.Rows(f, tableMap)
   334  			if err != nil {
   335  				t.Fatalf("DeleteRows event is broken: %v", err)
   336  			}
   337  
   338  			// Check it has 2 rows, and first value is '10', second value is 'nicer name'.
   339  			values, _ := dr.StringIdentifiesForTests(tableMap, 0)
   340  			t.Logf("Got DeleteRows event identify: %v %v", dr, values)
   341  			if expected := []string{"10", "nicer name"}; !reflect.DeepEqual(values, expected) {
   342  				t.Fatalf("StringIdentifies returned %v, expected %v", values, expected)
   343  			}
   344  
   345  			gotDelete = true
   346  		default:
   347  			t.Logf("Got unrelated event: %v", be)
   348  		}
   349  	}
   350  
   351  	// Drop the table, we're done.
   352  	if _, err := dConn.ExecuteFetch("drop table replication", 0, false); err != nil {
   353  		t.Fatal(err)
   354  	}
   355  
   356  }
   357  
   358  // TestRowReplicationTypes creates a table with all
   359  // supported data types. Then we insert a row in it. then we re-build
   360  // the SQL for the values, re-insert these. Then we select from the
   361  // database and make sure both rows are identical.
   362  func TestRowReplicationTypes(t *testing.T) {
   363  	// testcases are ordered by the types numbers in constants.go.
   364  	// Number are always unsigned, as we don't pass in sqltypes.Type.
   365  	testcases := []struct {
   366  		name        string
   367  		createType  string
   368  		createValue string
   369  	}{{
   370  		// TINYINT
   371  		name:        "tinytiny",
   372  		createType:  "TINYINT UNSIGNED",
   373  		createValue: "145",
   374  	}, {
   375  		// SMALLINT
   376  		name:        "smallish",
   377  		createType:  "SMALLINT UNSIGNED",
   378  		createValue: "40000",
   379  	}, {
   380  		// INT
   381  		name:        "regular_int",
   382  		createType:  "INT UNSIGNED",
   383  		createValue: "4000000000",
   384  	}, {
   385  		// FLOAT
   386  		name:        "floating",
   387  		createType:  "FLOAT",
   388  		createValue: "-3.14159E-22",
   389  	}, {
   390  		// DOUBLE
   391  		name:        "doubling",
   392  		createType:  "DOUBLE",
   393  		createValue: "-3.14159265359E+12",
   394  	}, {
   395  		// TIMESTAMP (zero value)
   396  		name:        "timestamp_zero",
   397  		createType:  "TIMESTAMP",
   398  		createValue: "'0000-00-00 00:00:00'",
   399  	}, {
   400  		// TIMESTAMP (day precision)
   401  		name:        "timestamp_day",
   402  		createType:  "TIMESTAMP",
   403  		createValue: "'2012-11-10 00:00:00'",
   404  	}, {
   405  		// BIGINT
   406  		name:        "big_int",
   407  		createType:  "BIGINT UNSIGNED",
   408  		createValue: "10000000000000000000",
   409  	}, {
   410  		// MEDIUMINT
   411  		name:        "mediumish",
   412  		createType:  "MEDIUMINT UNSIGNED",
   413  		createValue: "10000000",
   414  	}, {
   415  		// DATE
   416  		name:        "date_regular",
   417  		createType:  "DATE",
   418  		createValue: "'1920-10-24'",
   419  	}, {
   420  		// TIME
   421  		name:        "time_regular",
   422  		createType:  "TIME",
   423  		createValue: "'120:44:58'",
   424  	}, {
   425  		// TIME
   426  		name:        "time_neg",
   427  		createType:  "TIME",
   428  		createValue: "'-212:44:58'",
   429  	}, {
   430  		// DATETIME
   431  		name:        "datetime0",
   432  		createType:  "DATETIME",
   433  		createValue: "'1020-08-23 12:44:58'",
   434  	}, {
   435  		// YEAR zero
   436  		name:        "year0",
   437  		createType:  "YEAR",
   438  		createValue: "0",
   439  	}, {
   440  		// YEAR
   441  		name:        "year_nonzero",
   442  		createType:  "YEAR",
   443  		createValue: "2052",
   444  	}, {
   445  		// VARCHAR 8 bits
   446  		name:        "shortvc",
   447  		createType:  "VARCHAR(30)",
   448  		createValue: "'short varchar'",
   449  	}, {
   450  		// VARCHAR 16 bits
   451  		name:        "longvc",
   452  		createType:  "VARCHAR(1000)",
   453  		createValue: "'long varchar'",
   454  	}, {
   455  		// BIT
   456  		name:        "bit1",
   457  		createType:  "BIT",
   458  		createValue: "b'1'",
   459  	}, {
   460  		// BIT
   461  		name:        "bit6",
   462  		createType:  "BIT(6)",
   463  		createValue: "b'100101'",
   464  	}, {
   465  		// BIT
   466  		name:        "bit8",
   467  		createType:  "BIT(8)",
   468  		createValue: "b'10100101'",
   469  	}, {
   470  		// BIT
   471  		name:        "bit14",
   472  		createType:  "BIT(14)",
   473  		createValue: "b'10100101000111'",
   474  	}, {
   475  		// BIT
   476  		name:        "bit55",
   477  		createType:  "BIT(55)",
   478  		createValue: "b'1010010100110100101001101001010011010010100110100101001'",
   479  	}, {
   480  		// BIT
   481  		name:        "bit64",
   482  		createType:  "BIT(64)",
   483  		createValue: "b'1111111111010010100110100101001101001010011010010100110100101001'",
   484  	}, {
   485  		// DECIMAL
   486  		name:        "decimal2_1",
   487  		createType:  "DECIMAL(2,1)",
   488  		createValue: "1.2",
   489  	}, {
   490  		// DECIMAL neg
   491  		name:        "decimal2_1_neg",
   492  		createType:  "DECIMAL(2,1)",
   493  		createValue: "-5.6",
   494  	}, {
   495  		// DECIMAL
   496  		name:        "decimal4_2",
   497  		createType:  "DECIMAL(4,2)",
   498  		createValue: "61.52",
   499  	}, {
   500  		// DECIMAL neg
   501  		name:        "decimal4_2_neg",
   502  		createType:  "DECIMAL(4,2)",
   503  		createValue: "-78.94",
   504  	}, {
   505  		// DECIMAL
   506  		name:        "decimal6_3",
   507  		createType:  "DECIMAL(6,3)",
   508  		createValue: "611.542",
   509  	}, {
   510  		// DECIMAL neg
   511  		name:        "decimal6_3_neg",
   512  		createType:  "DECIMAL(6,3)",
   513  		createValue: "-478.394",
   514  	}, {
   515  		// DECIMAL
   516  		name:        "decimal8_4",
   517  		createType:  "DECIMAL(8,4)",
   518  		createValue: "6311.5742",
   519  	}, {
   520  		// DECIMAL neg
   521  		name:        "decimal8_4_neg",
   522  		createType:  "DECIMAL(8,4)",
   523  		createValue: "-4778.3894",
   524  	}, {
   525  		// DECIMAL
   526  		name:        "decimal10_5",
   527  		createType:  "DECIMAL(10,5)",
   528  		createValue: "63711.57342",
   529  	}, {
   530  		// DECIMAL neg
   531  		name:        "decimal10_5_neg",
   532  		createType:  "DECIMAL(10,5)",
   533  		createValue: "-47378.38594",
   534  	}, {
   535  		// DECIMAL
   536  		name:        "decimal12_6",
   537  		createType:  "DECIMAL(12,6)",
   538  		createValue: "637311.557342",
   539  	}, {
   540  		// DECIMAL neg
   541  		name:        "decimal12_6_neg",
   542  		createType:  "DECIMAL(12,6)",
   543  		createValue: "-473788.385794",
   544  	}, {
   545  		// DECIMAL
   546  		name:        "decimal14_7",
   547  		createType:  "DECIMAL(14,7)",
   548  		createValue: "6375311.5574342",
   549  	}, {
   550  		// DECIMAL neg
   551  		name:        "decimal14_7_neg",
   552  		createType:  "DECIMAL(14,7)",
   553  		createValue: "-4732788.3853794",
   554  	}, {
   555  		// DECIMAL
   556  		name:        "decimal16_8",
   557  		createType:  "DECIMAL(16,8)",
   558  		createValue: "63375311.54574342",
   559  	}, {
   560  		// DECIMAL neg
   561  		name:        "decimal16_8_neg",
   562  		createType:  "DECIMAL(16,8)",
   563  		createValue: "-47327788.38533794",
   564  	}, {
   565  		// DECIMAL
   566  		name:        "decimal18_9",
   567  		createType:  "DECIMAL(18,9)",
   568  		createValue: "633075311.545714342",
   569  	}, {
   570  		// DECIMAL neg
   571  		name:        "decimal18_9_neg",
   572  		createType:  "DECIMAL(18,9)",
   573  		createValue: "-473327788.385033794",
   574  	}, {
   575  		// DECIMAL
   576  		name:        "decimal20_10",
   577  		createType:  "DECIMAL(20,10)",
   578  		createValue: "6330375311.5405714342",
   579  	}, {
   580  		// DECIMAL neg
   581  		name:        "decimal20_10_neg",
   582  		createType:  "DECIMAL(20,10)",
   583  		createValue: "-4731327788.3850337294",
   584  	}, {
   585  		// DECIMAL lots of left digits
   586  		name:        "decimal34_0",
   587  		createType:  "DECIMAL(34,0)",
   588  		createValue: "8765432345678987654345432123456786",
   589  	}, {
   590  		// DECIMAL lots of left digits neg
   591  		name:        "decimal34_0_neg",
   592  		createType:  "DECIMAL(34,0)",
   593  		createValue: "-8765432345678987654345432123456786",
   594  	}, {
   595  		// DECIMAL lots of right digits
   596  		name:        "decimal34_30",
   597  		createType:  "DECIMAL(34,30)",
   598  		createValue: "8765.432345678987654345432123456786",
   599  	}, {
   600  		// DECIMAL lots of right digits neg
   601  		name:        "decimal34_30_neg",
   602  		createType:  "DECIMAL(34,30)",
   603  		createValue: "-8765.432345678987654345432123456786",
   604  	}, {
   605  		// ENUM
   606  		name:        "tshirtsize",
   607  		createType:  "ENUM('x-small', 'small', 'medium', 'large', 'x-larg')",
   608  		createValue: "'large'",
   609  	}, {
   610  		// SET
   611  		name:        "setnumbers",
   612  		createType:  "SET('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten')",
   613  		createValue: "'two,three,ten'",
   614  	}, {
   615  		// TINYBLOB
   616  		name:        "tiny_blob",
   617  		createType:  "TINYBLOB",
   618  		createValue: "'ab\\'cd'",
   619  	}, {
   620  		// BLOB
   621  		name:        "bloby",
   622  		createType:  "BLOB",
   623  		createValue: "'ab\\'cd'",
   624  	}, {
   625  		// MEDIUMBLOB
   626  		name:        "medium_blob",
   627  		createType:  "MEDIUMBLOB",
   628  		createValue: "'ab\\'cd'",
   629  	}, {
   630  		// LONGBLOB
   631  		name:        "long_blob",
   632  		createType:  "LONGBLOB",
   633  		createValue: "'ab\\'cd'",
   634  	}, {
   635  		// CHAR 8 bits
   636  		name:        "shortchar",
   637  		createType:  "CHAR(30)",
   638  		createValue: "'short char'",
   639  	}, {
   640  		// CHAR 9 bits (100 * 3 = 300, 256<=300<512)
   641  		name:        "mediumchar",
   642  		createType:  "CHAR(100)",
   643  		createValue: "'medium char'",
   644  	}, {
   645  		// CHAR 10 bits (250 * 3 = 750, 512<=750<124)
   646  		name:        "longchar",
   647  		createType:  "CHAR(250)",
   648  		createValue: "'long char'",
   649  	}, {
   650  		// GEOMETRY
   651  		name:        "geo_stuff",
   652  		createType:  "GEOMETRY",
   653  		createValue: "ST_GeomFromText('POINT(1 1)')",
   654  	}}
   655  
   656  	conn, f := connectForReplication(t, true /* rbr */)
   657  	defer conn.Close()
   658  
   659  	// MariaDB timestamp(N) is not supported by our RBR. See doc.go.
   660  	if !conn.IsMariaDB() {
   661  		testcases = append(testcases, []struct {
   662  			name        string
   663  			createType  string
   664  			createValue string
   665  		}{{
   666  			// TIMESTAMP (second precision)
   667  			name:        "timestamp_second",
   668  			createType:  "TIMESTAMP",
   669  			createValue: "'2012-11-10 15:34:56'",
   670  		}, {
   671  			// TIMESTAMP (100 millisecond precision)
   672  			name:        "timestamp_100millisecond",
   673  			createType:  "TIMESTAMP(1)",
   674  			createValue: "'2012-11-10 15:34:56.6'",
   675  		}, {
   676  			// TIMESTAMP (10 millisecond precision)
   677  			name:        "timestamp_10millisecond",
   678  			createType:  "TIMESTAMP(2)",
   679  			createValue: "'2012-11-10 15:34:56.01'",
   680  		}, {
   681  			// TIMESTAMP (millisecond precision)
   682  			name:        "timestamp_millisecond",
   683  			createType:  "TIMESTAMP(3)",
   684  			createValue: "'2012-11-10 15:34:56.012'",
   685  		}, {
   686  			// TIMESTAMP (100 microsecond precision)
   687  			name:        "timestamp_100microsecond",
   688  			createType:  "TIMESTAMP(4)",
   689  			createValue: "'2012-11-10 15:34:56.0123'",
   690  		}, {
   691  			// TIMESTAMP (10 microsecond precision)
   692  			name:        "timestamp_10microsecond",
   693  			createType:  "TIMESTAMP(5)",
   694  			createValue: "'2012-11-10 15:34:56.01234'",
   695  		}, {
   696  			// TIMESTAMP (microsecond precision)
   697  			name:        "timestamp_microsecond",
   698  			createType:  "TIMESTAMP(6)",
   699  			createValue: "'2012-11-10 15:34:56.012345'",
   700  		}, {
   701  			// TIMESTAMP (0 with microsecond precision)
   702  			name:        "timestamp_microsecond_z",
   703  			createType:  "TIMESTAMP(6)",
   704  			createValue: "'0000-00-00 00:00:00.000000'",
   705  		}, {
   706  			// TIME
   707  			name:        "time_100milli",
   708  			createType:  "TIME(1)",
   709  			createValue: "'12:44:58.3'",
   710  		}, {
   711  			// TIME
   712  			name:        "time_10milli",
   713  			createType:  "TIME(2)",
   714  			createValue: "'412:44:58.01'",
   715  		}, {
   716  			// TIME
   717  			name:        "time_milli",
   718  			createType:  "TIME(3)",
   719  			createValue: "'-12:44:58.012'",
   720  		}, {
   721  			// TIME
   722  			name:        "time_100micro",
   723  			createType:  "TIME(4)",
   724  			createValue: "'12:44:58.0123'",
   725  		}, {
   726  			// TIME
   727  			name:        "time_10micro",
   728  			createType:  "TIME(5)",
   729  			createValue: "'12:44:58.01234'",
   730  		}, {
   731  			// TIME
   732  			name:        "time_micro",
   733  			createType:  "TIME(6)",
   734  			createValue: "'-12:44:58.012345'",
   735  		}, {
   736  			// DATETIME
   737  			name:        "datetime1",
   738  			createType:  "DATETIME(1)",
   739  			createValue: "'1020-08-23 12:44:58.8'",
   740  		}, {
   741  			// DATETIME
   742  			name:        "datetime2",
   743  			createType:  "DATETIME(2)",
   744  			createValue: "'1020-08-23 12:44:58.01'",
   745  		}, {
   746  			// DATETIME
   747  			name:        "datetime3",
   748  			createType:  "DATETIME(3)",
   749  			createValue: "'1020-08-23 12:44:58.012'",
   750  		}, {
   751  			// DATETIME
   752  			name:        "datetime4",
   753  			createType:  "DATETIME(4)",
   754  			createValue: "'1020-08-23 12:44:58.0123'",
   755  		}, {
   756  			// DATETIME
   757  			name:        "datetime5",
   758  			createType:  "DATETIME(5)",
   759  			createValue: "'1020-08-23 12:44:58.01234'",
   760  		}, {
   761  			// DATETIME
   762  			name:        "datetime6",
   763  			createType:  "DATETIME(6)",
   764  			createValue: "'1020-08-23 12:44:58.012345'",
   765  		}}...)
   766  	}
   767  
   768  	// JSON is only supported by MySQL 5.7+
   769  	// However the binary format is not just the text version.
   770  	// So it doesn't work as expected.
   771  	if strings.HasPrefix(conn.ServerVersion, "5.7") {
   772  		testcases = append(testcases, []struct {
   773  			name        string
   774  			createType  string
   775  			createValue string
   776  		}{{
   777  			name:        "json1",
   778  			createType:  "JSON",
   779  			createValue: "'{\"a\": 2}'",
   780  		}, {
   781  			name:        "json2",
   782  			createType:  "JSON",
   783  			createValue: "'[1,2]'",
   784  		}, {
   785  			name:        "json3",
   786  			createType:  "JSON",
   787  			createValue: "'{\"a\":\"b\", \"c\":\"d\",\"ab\":\"abc\", \"bc\": [\"x\", \"y\"]}'",
   788  		}, {
   789  			name:        "json4",
   790  			createType:  "JSON",
   791  			createValue: "'[\"here\", [\"I\", \"am\"], \"!!!\"]'",
   792  		}, {
   793  			name:        "json5",
   794  			createType:  "JSON",
   795  			createValue: "'\"scalar string\"'",
   796  		}, {
   797  			name:        "json6",
   798  			createType:  "JSON",
   799  			createValue: "'true'",
   800  		}, {
   801  			name:        "json7",
   802  			createType:  "JSON",
   803  			createValue: "'false'",
   804  		}, {
   805  			name:        "json8",
   806  			createType:  "JSON",
   807  			createValue: "'null'",
   808  		}, {
   809  			name:        "json9",
   810  			createType:  "JSON",
   811  			createValue: "'-1'",
   812  		}, {
   813  			name:        "json10",
   814  			createType:  "JSON",
   815  			createValue: "CAST(CAST(1 AS UNSIGNED) AS JSON)",
   816  		}, {
   817  			name:        "json11",
   818  			createType:  "JSON",
   819  			createValue: "'32767'",
   820  		}, {
   821  			name:        "json12",
   822  			createType:  "JSON",
   823  			createValue: "'32768'",
   824  		}, {
   825  			name:        "json13",
   826  			createType:  "JSON",
   827  			createValue: "'-32768'",
   828  		}, {
   829  			name:        "json14",
   830  			createType:  "JSON",
   831  			createValue: "'-32769'",
   832  		}, {
   833  			name:        "json15",
   834  			createType:  "JSON",
   835  			createValue: "'2147483647'",
   836  		}, {
   837  			name:        "json16",
   838  			createType:  "JSON",
   839  			createValue: "'2147483648'",
   840  		}, {
   841  			name:        "json17",
   842  			createType:  "JSON",
   843  			createValue: "'-2147483648'",
   844  		}, {
   845  			name:        "json18",
   846  			createType:  "JSON",
   847  			createValue: "'-2147483649'",
   848  		}, {
   849  			name:       "json19",
   850  			createType: "JSON",
   851  			// FIXME: was "'18446744073709551615'", unsigned int representation differs from MySQL's which saves this as select 1.8446744073709552e19
   852  			// probably need to replace the json library: "github.com/spyzhov/ajson"
   853  			createValue: "'18446744073709551616'",
   854  		}, {
   855  			name:        "json20",
   856  			createType:  "JSON",
   857  			createValue: "'18446744073709551616'",
   858  		}, {
   859  			name:        "json21",
   860  			createType:  "JSON",
   861  			createValue: "'3.14159'",
   862  		}, {
   863  			name:        "json22",
   864  			createType:  "JSON",
   865  			createValue: "'{}'",
   866  		}, {
   867  			name:        "json23",
   868  			createType:  "JSON",
   869  			createValue: "'[]'",
   870  		}, {
   871  			name:        "json24",
   872  			createType:  "JSON",
   873  			createValue: "CAST(CAST('2015-01-24 23:24:25' AS DATETIME) AS JSON)",
   874  		}, {
   875  			name:        "json25",
   876  			createType:  "JSON",
   877  			createValue: "CAST(CAST('23:24:25' AS TIME) AS JSON)",
   878  		}, {
   879  			name:        "json26",
   880  			createType:  "JSON",
   881  			createValue: "CAST(CAST('23:24:26.12' AS TIME(3)) AS JSON)",
   882  		}, {
   883  			name:        "json27",
   884  			createType:  "JSON",
   885  			createValue: "CAST(CAST('2015-01-27' AS DATE) AS JSON)",
   886  		}, {
   887  			name:        "json28",
   888  			createType:  "JSON",
   889  			createValue: "CAST(TIMESTAMP'2015-01-28 23:24:28' AS JSON)",
   890  		}, {
   891  			name:        "json29",
   892  			createType:  "JSON",
   893  			createValue: "CAST(ST_GeomFromText('POINT(1 1)') AS JSON)",
   894  		}, {
   895  			// Decimal has special treatment.
   896  			name:        "json30",
   897  			createType:  "JSON",
   898  			createValue: "CAST(CAST('123456789.1234' AS DECIMAL(13,4)) AS JSON)",
   899  			// FIXME(alainjobart) opaque types are complicated.
   900  			//		}, {
   901  			// This is a bit field. Opaque type in JSON.
   902  			//			name:        "json31",
   903  			//			createType:  "JSON",
   904  			//			createValue: "CAST(x'cafe' AS JSON)",
   905  		}}...)
   906  	}
   907  
   908  	ctx := context.Background()
   909  	dConn, err := mysql.Connect(ctx, &connParams)
   910  	if err != nil {
   911  		t.Fatal(err)
   912  	}
   913  	defer dConn.Close()
   914  
   915  	// Set the connection time zone for execution of the
   916  	// statements to PST. That way we're sure to test the
   917  	// conversion for the TIMESTAMP types.
   918  	if _, err := dConn.ExecuteFetch("SET time_zone = '+08:00'", 0, false); err != nil {
   919  		t.Fatal(err)
   920  	}
   921  
   922  	// Create the table with all fields.
   923  	createTable := "create table replicationtypes(id int"
   924  	for _, tcase := range testcases {
   925  		createTable += fmt.Sprintf(", %v %v", tcase.name, tcase.createType)
   926  	}
   927  	createTable += ", primary key(id))"
   928  	if _, err := dConn.ExecuteFetch(createTable, 0, false); err != nil {
   929  		t.Fatal(err)
   930  	}
   931  
   932  	// Insert the value with all fields.
   933  	insert := "insert into replicationtypes set id=1"
   934  	for _, tcase := range testcases {
   935  		insert += fmt.Sprintf(", %v=%v", tcase.name, tcase.createValue)
   936  	}
   937  
   938  	result, err := dConn.ExecuteFetch(insert, 0, false)
   939  	require.NoError(t, err, "insert failed: %v", err)
   940  
   941  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
   942  		t.Errorf("unexpected result for insert: %v", result)
   943  	}
   944  
   945  	// Get the new events from the binlogs.
   946  	// Only care about the Write event.
   947  	var tableID uint64
   948  	var tableMap *mysql.TableMap
   949  	var values []sqltypes.Value
   950  
   951  	for values == nil {
   952  		be, err := conn.ReadBinlogEvent()
   953  		require.NoError(t, err, "ReadPacket failed: %v", err)
   954  		require.True(t, be.IsValid(), "read an invalid packet: %v", be)
   955  
   956  		be, _, err = be.StripChecksum(f)
   957  		require.NoError(t, err, "StripChecksum failed: %v", err)
   958  
   959  		switch {
   960  		case be.IsTableMap():
   961  			tableID = be.TableID(f) // This would be 0x00ffffff for an event to clear all table map entries.
   962  			var err error
   963  			tableMap, err = be.TableMap(f)
   964  			if err != nil {
   965  				t.Fatalf("TableMap event is broken: %v", err)
   966  			}
   967  			t.Logf("Got Table Map event: %v %v", tableID, tableMap)
   968  			if tableMap.Database != "vttest" ||
   969  				tableMap.Name != "replicationtypes" ||
   970  				len(tableMap.Types) != len(testcases)+1 ||
   971  				tableMap.CanBeNull.Bit(0) {
   972  				t.Errorf("got wrong TableMap: %v", tableMap)
   973  			}
   974  		case be.IsWriteRows():
   975  			if got := be.TableID(f); got != tableID {
   976  				t.Fatalf("WriteRows event got table ID %v but was expecting %v", got, tableID)
   977  			}
   978  			wr, err := be.Rows(f, tableMap)
   979  			if err != nil {
   980  				t.Fatalf("Rows event is broken: %v", err)
   981  			}
   982  
   983  			// Check it has the right values
   984  			values, err = valuesForTests(t, &wr, tableMap, 0)
   985  			if err != nil {
   986  				t.Fatalf("valuesForTests is broken: %v", err)
   987  			}
   988  			t.Logf("Got WriteRows event data: %v %v", wr, values)
   989  			if len(values) != len(testcases)+1 {
   990  				t.Fatalf("Got wrong length %v for values, was expecting %v", len(values), len(testcases)+1)
   991  			}
   992  
   993  		default:
   994  			t.Logf("Got unrelated event: %v", be)
   995  		}
   996  	}
   997  
   998  	// Insert a second row with the same data.
   999  	var sql bytes.Buffer
  1000  	sql.WriteString("insert into replicationtypes set id=2")
  1001  	for i, tcase := range testcases {
  1002  		sql.WriteString(", ")
  1003  		sql.WriteString(tcase.name)
  1004  		sql.WriteString(" = ")
  1005  		valueBytes, err := values[i+1].ToBytes()
  1006  		// Expression values are not supported with ToBytes
  1007  		if values[i+1].Type() != querypb.Type_EXPRESSION {
  1008  			require.NoError(t, err)
  1009  		}
  1010  		if values[i+1].Type() == querypb.Type_TIMESTAMP && !bytes.HasPrefix(valueBytes, mysql.ZeroTimestamp) {
  1011  			// Values in the binary log are UTC. Let's convert them
  1012  			// to whatever timezone the connection is using,
  1013  			// so MySQL properly converts them back to UTC.
  1014  			sql.WriteString("convert_tz(")
  1015  			values[i+1].EncodeSQL(&sql)
  1016  			sql.WriteString(", '+00:00', @@session.time_zone)")
  1017  		} else {
  1018  			if strings.Index(tcase.name, "json") == 0 {
  1019  				sql.WriteString("'" + string(values[i+1].Raw()) + "'")
  1020  			} else {
  1021  				values[i+1].EncodeSQL(&sql)
  1022  			}
  1023  		}
  1024  	}
  1025  	result, err = dConn.ExecuteFetch(sql.String(), 0, false)
  1026  	require.NoError(t, err, "insert '%v' failed: %v", sql.String(), err)
  1027  
  1028  	if result.RowsAffected != 1 || len(result.Rows) != 0 {
  1029  		t.Errorf("unexpected result for insert: %v", result)
  1030  	}
  1031  	t.Logf("Insert after getting event is: %v", sql.String())
  1032  
  1033  	// Re-select both rows, make sure all columns are the same.
  1034  	stmt := "select id"
  1035  	for _, tcase := range testcases {
  1036  		stmt += ", " + tcase.name
  1037  	}
  1038  	stmt += " from replicationtypes"
  1039  	result, err = dConn.ExecuteFetch(stmt, 2, false)
  1040  	require.NoError(t, err, "select failed: %v", err)
  1041  	require.Equal(t, 2, len(result.Rows), "unexpected result for select: %v", result)
  1042  
  1043  	for i, tcase := range testcases {
  1044  		assert.True(t, reflect.DeepEqual(result.Rows[0][i+1], result.Rows[1][i+1]), "Field %v is not the same, got %v and %v", tcase.name, result.Rows[0][i+1], result.Rows[1][i+1])
  1045  
  1046  	}
  1047  
  1048  	// Drop the table, we're done.
  1049  	if _, err := dConn.ExecuteFetch("drop table replicationtypes", 0, false); err != nil {
  1050  		t.Fatal(err)
  1051  	}
  1052  
  1053  }
  1054  
  1055  // valuesForTests is a helper method to return the sqltypes.Value
  1056  // of all columns in a row in a Row. Only use it in tests, as the
  1057  // returned values cannot be interpreted correctly without the schema.
  1058  // We assume everything is unsigned in this method.
  1059  func valuesForTests(t *testing.T, rs *mysql.Rows, tm *mysql.TableMap, rowIndex int) ([]sqltypes.Value, error) {
  1060  	var result []sqltypes.Value
  1061  
  1062  	valueIndex := 0
  1063  	data := rs.Rows[rowIndex].Data
  1064  	pos := 0
  1065  	for c := 0; c < rs.DataColumns.Count(); c++ {
  1066  		if !rs.DataColumns.Bit(c) {
  1067  			continue
  1068  		}
  1069  
  1070  		if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) {
  1071  			// This column is represented, but its value is NULL.
  1072  			result = append(result, sqltypes.NULL)
  1073  			valueIndex++
  1074  			continue
  1075  		}
  1076  
  1077  		// We have real data
  1078  		value, l, err := mysql.CellValue(data, pos, tm.Types[c], tm.Metadata[c], &querypb.Field{Type: querypb.Type_UINT64})
  1079  		if err != nil {
  1080  			return nil, err
  1081  		}
  1082  		result = append(result, value)
  1083  		t.Logf("  %v: type=%v data=%v metadata=%v -> %v", c, tm.Types[c], data[pos:pos+l], tm.Metadata[c], value)
  1084  		pos += l
  1085  		valueIndex++
  1086  	}
  1087  
  1088  	return result, nil
  1089  }