vitess.io/vitess@v0.16.2/go/vt/binlog/binlog_streamer_rbr_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 binlog
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"context"
    24  
    25  	"vitess.io/vitess/go/mysql"
    26  	"vitess.io/vitess/go/vt/dbconfigs"
    27  	"vitess.io/vitess/go/vt/sqlparser"
    28  	"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
    29  
    30  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    31  	querypb "vitess.io/vitess/go/vt/proto/query"
    32  )
    33  
    34  // This file tests the RBR events are parsed correctly.
    35  
    36  func TestStreamerParseRBREvents(t *testing.T) {
    37  	f := mysql.NewMySQL56BinlogFormat()
    38  	s := mysql.NewFakeBinlogStream()
    39  	s.ServerID = 62344
    40  
    41  	// Create a schema.Engine for this test, with just one table.
    42  	// We only use the Columns.
    43  	se := schema.NewEngineForTests()
    44  	se.SetTableForTests(&schema.Table{
    45  		Name: sqlparser.NewIdentifierCS("vt_a"),
    46  		Fields: []*querypb.Field{{
    47  			Name: "id",
    48  			Type: querypb.Type_INT64,
    49  		}, {
    50  			Name: "message",
    51  			Type: querypb.Type_VARCHAR,
    52  		}},
    53  	})
    54  
    55  	// Create a tableMap event on the table.
    56  	tableID := uint64(0x102030405060)
    57  	tm := &mysql.TableMap{
    58  		Flags:    0x8090,
    59  		Database: "vt_test_keyspace",
    60  		Name:     "vt_a",
    61  		Types: []byte{
    62  			mysql.TypeLong,
    63  			mysql.TypeVarchar,
    64  		},
    65  		CanBeNull: mysql.NewServerBitmap(2),
    66  		Metadata: []uint16{
    67  			0,
    68  			384, // A VARCHAR(128) in utf8 would result in 384.
    69  		},
    70  	}
    71  	tm.CanBeNull.Set(1, true)
    72  
    73  	// Do an insert packet with all fields set.
    74  	insertRows := mysql.Rows{
    75  		Flags:       0x1234,
    76  		DataColumns: mysql.NewServerBitmap(2),
    77  		Rows: []mysql.Row{
    78  			{
    79  				NullColumns: mysql.NewServerBitmap(2),
    80  				Data: []byte{
    81  					0x10, 0x20, 0x30, 0x40, // long
    82  					0x04, 0x00, // len('abcd')
    83  					'a', 'b', 'c', 'd', // 'abcd'
    84  				},
    85  			},
    86  		},
    87  	}
    88  	insertRows.DataColumns.Set(0, true)
    89  	insertRows.DataColumns.Set(1, true)
    90  
    91  	// Do an update packet with all fields set.
    92  	updateRows := mysql.Rows{
    93  		Flags:           0x1234,
    94  		IdentifyColumns: mysql.NewServerBitmap(2),
    95  		DataColumns:     mysql.NewServerBitmap(2),
    96  		Rows: []mysql.Row{
    97  			{
    98  				NullIdentifyColumns: mysql.NewServerBitmap(2),
    99  				NullColumns:         mysql.NewServerBitmap(2),
   100  				Identify: []byte{
   101  					0x10, 0x20, 0x30, 0x40, // long
   102  					0x03, 0x00, // len('abc')
   103  					'a', 'b', 'c', // 'abc'
   104  				},
   105  				Data: []byte{
   106  					0x10, 0x20, 0x30, 0x40, // long
   107  					0x04, 0x00, // len('abcd')
   108  					'a', 'b', 'c', 'd', // 'abcd'
   109  				},
   110  			},
   111  		},
   112  	}
   113  	updateRows.IdentifyColumns.Set(0, true)
   114  	updateRows.IdentifyColumns.Set(1, true)
   115  	updateRows.DataColumns.Set(0, true)
   116  	updateRows.DataColumns.Set(1, true)
   117  
   118  	// Do an update packet with an identify set to NULL, and a
   119  	// value set to NULL.
   120  	updateRowsNull := mysql.Rows{
   121  		Flags:           0x1234,
   122  		IdentifyColumns: mysql.NewServerBitmap(2),
   123  		DataColumns:     mysql.NewServerBitmap(2),
   124  		Rows: []mysql.Row{
   125  			{
   126  				NullIdentifyColumns: mysql.NewServerBitmap(2),
   127  				NullColumns:         mysql.NewServerBitmap(2),
   128  				Identify: []byte{
   129  					0x10, 0x20, 0x30, 0x40, // long
   130  				},
   131  				Data: []byte{
   132  					0x10, 0x20, 0x30, 0x40, // long
   133  				},
   134  			},
   135  		},
   136  	}
   137  	updateRowsNull.IdentifyColumns.Set(0, true)
   138  	updateRowsNull.IdentifyColumns.Set(1, true)
   139  	updateRowsNull.DataColumns.Set(0, true)
   140  	updateRowsNull.DataColumns.Set(1, true)
   141  	updateRowsNull.Rows[0].NullIdentifyColumns.Set(1, true)
   142  	updateRowsNull.Rows[0].NullColumns.Set(1, true)
   143  
   144  	// Do a delete packet with all fields set.
   145  	deleteRows := mysql.Rows{
   146  		Flags:           0x1234,
   147  		IdentifyColumns: mysql.NewServerBitmap(2),
   148  		Rows: []mysql.Row{
   149  			{
   150  				NullIdentifyColumns: mysql.NewServerBitmap(2),
   151  				Identify: []byte{
   152  					0x10, 0x20, 0x30, 0x40, // long
   153  					0x03, 0x00, // len('abc')
   154  					'a', 'b', 'c', // 'abc'
   155  				},
   156  			},
   157  		},
   158  	}
   159  	deleteRows.IdentifyColumns.Set(0, true)
   160  	deleteRows.IdentifyColumns.Set(1, true)
   161  
   162  	input := []mysql.BinlogEvent{
   163  		mysql.NewRotateEvent(f, s, 0, ""),
   164  		mysql.NewFormatDescriptionEvent(f, s),
   165  		mysql.NewTableMapEvent(f, s, tableID, tm),
   166  		mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */),
   167  		mysql.NewQueryEvent(f, s, mysql.Query{
   168  			Database: "vt_test_keyspace",
   169  			SQL:      "BEGIN"}),
   170  		mysql.NewWriteRowsEvent(f, s, tableID, insertRows),
   171  		mysql.NewUpdateRowsEvent(f, s, tableID, updateRows),
   172  		mysql.NewUpdateRowsEvent(f, s, tableID, updateRowsNull),
   173  		mysql.NewDeleteRowsEvent(f, s, tableID, deleteRows),
   174  		mysql.NewXIDEvent(f, s),
   175  	}
   176  
   177  	events := make(chan mysql.BinlogEvent)
   178  	errs := make(chan error)
   179  
   180  	want := []fullBinlogTransaction{
   181  		{
   182  			statements: []FullBinlogStatement{
   183  				{
   184  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   185  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   186  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   187  					},
   188  				},
   189  				{
   190  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   191  						Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
   192  						Sql:      []byte("INSERT INTO vt_a SET id=1076895760, message='abcd'"),
   193  					},
   194  					Table: "vt_a",
   195  				},
   196  				{
   197  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   198  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   199  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   200  					},
   201  				},
   202  				{
   203  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   204  						Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
   205  						Sql:      []byte("UPDATE vt_a SET id=1076895760, message='abcd' WHERE id=1076895760 AND message='abc'"),
   206  					},
   207  					Table: "vt_a",
   208  				},
   209  				{
   210  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   211  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   212  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   213  					},
   214  				},
   215  				{
   216  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   217  						Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
   218  						Sql:      []byte("UPDATE vt_a SET id=1076895760, message=NULL WHERE id=1076895760 AND message IS NULL"),
   219  					},
   220  					Table: "vt_a",
   221  				},
   222  				{
   223  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   224  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   225  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   226  					},
   227  				},
   228  				{
   229  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   230  						Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE,
   231  						Sql:      []byte("DELETE FROM vt_a WHERE id=1076895760 AND message='abc'"),
   232  					},
   233  					Table: "vt_a",
   234  				},
   235  			},
   236  			eventToken: &querypb.EventToken{
   237  				Timestamp: 1407805592,
   238  				Position: mysql.EncodePosition(mysql.Position{
   239  					GTIDSet: mysql.MariadbGTIDSet{
   240  						0: mysql.MariadbGTID{
   241  							Domain:   0,
   242  							Server:   62344,
   243  							Sequence: 0x0d,
   244  						},
   245  					},
   246  				}),
   247  			},
   248  		},
   249  	}
   250  	var got []fullBinlogTransaction
   251  	sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
   252  		got = append(got, fullBinlogTransaction{
   253  			eventToken: eventToken,
   254  			statements: statements,
   255  		})
   256  		return nil
   257  	}
   258  	// Set mock mysql.ConnParams and dbconfig
   259  	mcp := &mysql.ConnParams{
   260  		DbName: "vt_test_keyspace",
   261  	}
   262  	dbcfgs := dbconfigs.New(mcp)
   263  
   264  	bls := NewStreamer(dbcfgs, se, nil, mysql.Position{}, 0, sendTransaction)
   265  
   266  	go sendTestEvents(events, input)
   267  	_, err := bls.parseEvents(context.Background(), events, errs)
   268  	if err != ErrServerEOF {
   269  		t.Errorf("unexpected error: %v", err)
   270  	}
   271  
   272  	if !reflect.DeepEqual(got, want) {
   273  		t.Errorf("binlogConnStreamer.parseEvents(): got:\n%+v\nwant:\n%+v", got, want)
   274  		for i, fbt := range got {
   275  			t.Errorf("Got (%v)=%v", i, fbt.statements)
   276  		}
   277  		for i, fbt := range want {
   278  			t.Errorf("Want(%v)=%v", i, fbt.statements)
   279  		}
   280  	}
   281  }
   282  
   283  func TestStreamerParseRBRNameEscapes(t *testing.T) {
   284  	f := mysql.NewMySQL56BinlogFormat()
   285  	s := mysql.NewFakeBinlogStream()
   286  	s.ServerID = 62344
   287  
   288  	// Create a schema.Engine for this test using keyword names.
   289  	se := schema.NewEngineForTests()
   290  	se.SetTableForTests(&schema.Table{
   291  		Name: sqlparser.NewIdentifierCS("insert"),
   292  		Fields: []*querypb.Field{{
   293  			Name: "update",
   294  			Type: querypb.Type_INT64,
   295  		}, {
   296  			Name: "delete",
   297  			Type: querypb.Type_VARCHAR,
   298  		}},
   299  	})
   300  
   301  	// Create a tableMap event on the table.
   302  	tableID := uint64(0x102030405060)
   303  	tm := &mysql.TableMap{
   304  		Flags:    0x8090,
   305  		Database: "vt_test_keyspace",
   306  		Name:     "insert",
   307  		Types: []byte{
   308  			mysql.TypeLong,
   309  			mysql.TypeVarchar,
   310  		},
   311  		CanBeNull: mysql.NewServerBitmap(2),
   312  		Metadata: []uint16{
   313  			0,
   314  			384, // A VARCHAR(128) in utf8 would result in 384.
   315  		},
   316  	}
   317  	tm.CanBeNull.Set(1, true)
   318  
   319  	// Do an insert packet with all fields set.
   320  	insertRows := mysql.Rows{
   321  		Flags:       0x1234,
   322  		DataColumns: mysql.NewServerBitmap(2),
   323  		Rows: []mysql.Row{
   324  			{
   325  				NullColumns: mysql.NewServerBitmap(2),
   326  				Data: []byte{
   327  					0x10, 0x20, 0x30, 0x40, // long
   328  					0x04, 0x00, // len('abcd')
   329  					'a', 'b', 'c', 'd', // 'abcd'
   330  				},
   331  			},
   332  		},
   333  	}
   334  	insertRows.DataColumns.Set(0, true)
   335  	insertRows.DataColumns.Set(1, true)
   336  
   337  	// Do an update packet with all fields set.
   338  	updateRows := mysql.Rows{
   339  		Flags:           0x1234,
   340  		IdentifyColumns: mysql.NewServerBitmap(2),
   341  		DataColumns:     mysql.NewServerBitmap(2),
   342  		Rows: []mysql.Row{
   343  			{
   344  				NullIdentifyColumns: mysql.NewServerBitmap(2),
   345  				NullColumns:         mysql.NewServerBitmap(2),
   346  				Identify: []byte{
   347  					0x10, 0x20, 0x30, 0x40, // long
   348  					0x03, 0x00, // len('abc')
   349  					'a', 'b', 'c', // 'abc'
   350  				},
   351  				Data: []byte{
   352  					0x10, 0x20, 0x30, 0x40, // long
   353  					0x04, 0x00, // len('abcd')
   354  					'a', 'b', 'c', 'd', // 'abcd'
   355  				},
   356  			},
   357  		},
   358  	}
   359  	updateRows.IdentifyColumns.Set(0, true)
   360  	updateRows.IdentifyColumns.Set(1, true)
   361  	updateRows.DataColumns.Set(0, true)
   362  	updateRows.DataColumns.Set(1, true)
   363  
   364  	// Do an update packet with an identify set to NULL, and a
   365  	// value set to NULL.
   366  	updateRowsNull := mysql.Rows{
   367  		Flags:           0x1234,
   368  		IdentifyColumns: mysql.NewServerBitmap(2),
   369  		DataColumns:     mysql.NewServerBitmap(2),
   370  		Rows: []mysql.Row{
   371  			{
   372  				NullIdentifyColumns: mysql.NewServerBitmap(2),
   373  				NullColumns:         mysql.NewServerBitmap(2),
   374  				Identify: []byte{
   375  					0x10, 0x20, 0x30, 0x40, // long
   376  				},
   377  				Data: []byte{
   378  					0x10, 0x20, 0x30, 0x40, // long
   379  				},
   380  			},
   381  		},
   382  	}
   383  	updateRowsNull.IdentifyColumns.Set(0, true)
   384  	updateRowsNull.IdentifyColumns.Set(1, true)
   385  	updateRowsNull.DataColumns.Set(0, true)
   386  	updateRowsNull.DataColumns.Set(1, true)
   387  	updateRowsNull.Rows[0].NullIdentifyColumns.Set(1, true)
   388  	updateRowsNull.Rows[0].NullColumns.Set(1, true)
   389  
   390  	// Do a delete packet with all fields set.
   391  	deleteRows := mysql.Rows{
   392  		Flags:           0x1234,
   393  		IdentifyColumns: mysql.NewServerBitmap(2),
   394  		Rows: []mysql.Row{
   395  			{
   396  				NullIdentifyColumns: mysql.NewServerBitmap(2),
   397  				Identify: []byte{
   398  					0x10, 0x20, 0x30, 0x40, // long
   399  					0x03, 0x00, // len('abc')
   400  					'a', 'b', 'c', // 'abc'
   401  				},
   402  			},
   403  		},
   404  	}
   405  	deleteRows.IdentifyColumns.Set(0, true)
   406  	deleteRows.IdentifyColumns.Set(1, true)
   407  
   408  	input := []mysql.BinlogEvent{
   409  		mysql.NewRotateEvent(f, s, 0, ""),
   410  		mysql.NewFormatDescriptionEvent(f, s),
   411  		mysql.NewTableMapEvent(f, s, tableID, tm),
   412  		mysql.NewMariaDBGTIDEvent(f, s, mysql.MariadbGTID{Domain: 0, Sequence: 0xd}, false /* hasBegin */),
   413  		mysql.NewQueryEvent(f, s, mysql.Query{
   414  			Database: "vt_test_keyspace",
   415  			SQL:      "BEGIN"}),
   416  		mysql.NewWriteRowsEvent(f, s, tableID, insertRows),
   417  		mysql.NewUpdateRowsEvent(f, s, tableID, updateRows),
   418  		mysql.NewUpdateRowsEvent(f, s, tableID, updateRowsNull),
   419  		mysql.NewDeleteRowsEvent(f, s, tableID, deleteRows),
   420  		mysql.NewXIDEvent(f, s),
   421  	}
   422  
   423  	events := make(chan mysql.BinlogEvent)
   424  	errs := make(chan error)
   425  
   426  	want := []fullBinlogTransaction{
   427  		{
   428  			statements: []FullBinlogStatement{
   429  				{
   430  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   431  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   432  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   433  					},
   434  				},
   435  				{
   436  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   437  						Category: binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
   438  						Sql:      []byte("INSERT INTO `insert` SET `update`=1076895760, `delete`='abcd'"),
   439  					},
   440  					Table: "insert",
   441  				},
   442  				{
   443  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   444  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   445  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   446  					},
   447  				},
   448  				{
   449  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   450  						Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
   451  						Sql:      []byte("UPDATE `insert` SET `update`=1076895760, `delete`='abcd' WHERE `update`=1076895760 AND `delete`='abc'"),
   452  					},
   453  					Table: "insert",
   454  				},
   455  				{
   456  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   457  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   458  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   459  					},
   460  				},
   461  				{
   462  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   463  						Category: binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
   464  						Sql:      []byte("UPDATE `insert` SET `update`=1076895760, `delete`=NULL WHERE `update`=1076895760 AND `delete` IS NULL"),
   465  					},
   466  					Table: "insert",
   467  				},
   468  				{
   469  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   470  						Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
   471  						Sql:      []byte("SET TIMESTAMP=1407805592"),
   472  					},
   473  				},
   474  				{
   475  					Statement: &binlogdatapb.BinlogTransaction_Statement{
   476  						Category: binlogdatapb.BinlogTransaction_Statement_BL_DELETE,
   477  						Sql:      []byte("DELETE FROM `insert` WHERE `update`=1076895760 AND `delete`='abc'"),
   478  					},
   479  					Table: "insert",
   480  				},
   481  			},
   482  			eventToken: &querypb.EventToken{
   483  				Timestamp: 1407805592,
   484  				Position: mysql.EncodePosition(mysql.Position{
   485  					GTIDSet: mysql.MariadbGTIDSet{
   486  						0: mysql.MariadbGTID{
   487  							Domain:   0,
   488  							Server:   62344,
   489  							Sequence: 0x0d,
   490  						},
   491  					},
   492  				}),
   493  			},
   494  		},
   495  	}
   496  	var got []fullBinlogTransaction
   497  	sendTransaction := func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
   498  		got = append(got, fullBinlogTransaction{
   499  			eventToken: eventToken,
   500  			statements: statements,
   501  		})
   502  		return nil
   503  	}
   504  	// Set mock mysql.ConnParams and dbconfig
   505  	mcp := &mysql.ConnParams{
   506  		DbName: "vt_test_keyspace",
   507  	}
   508  	dbcfgs := dbconfigs.New(mcp)
   509  
   510  	bls := NewStreamer(dbcfgs, se, nil, mysql.Position{}, 0, sendTransaction)
   511  
   512  	go sendTestEvents(events, input)
   513  	_, err := bls.parseEvents(context.Background(), events, errs)
   514  	if err != ErrServerEOF {
   515  		t.Errorf("unexpected error: %v", err)
   516  	}
   517  
   518  	if !reflect.DeepEqual(got, want) {
   519  		t.Errorf("binlogConnStreamer.parseEvents(): got:\n%+v\nwant:\n%+v", got, want)
   520  		for i, fbt := range got {
   521  			t.Errorf("Got (%v)=%v", i, fbt.statements)
   522  		}
   523  		for i, fbt := range want {
   524  			t.Errorf("Want(%v)=%v", i, fbt.statements)
   525  		}
   526  	}
   527  }