github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/cdc/sink/codec/canal_flat_test.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package codec
    15  
    16  import (
    17  	"encoding/json"
    18  
    19  	"github.com/pingcap/check"
    20  	mm "github.com/pingcap/parser/model"
    21  	"github.com/pingcap/parser/mysql"
    22  	"github.com/pingcap/ticdc/cdc/model"
    23  	"github.com/pingcap/ticdc/pkg/util/testleak"
    24  	"golang.org/x/text/encoding/charmap"
    25  )
    26  
    27  type canalFlatSuite struct {
    28  }
    29  
    30  var _ = check.Suite(&canalFlatSuite{})
    31  
    32  func (s *canalFlatSuite) TestNewCanalFlatMessageFromDML(c *check.C) {
    33  	defer testleak.AfterTest(c)()
    34  	encoder := &CanalFlatEventBatchEncoder{builder: NewCanalEntryBuilder()}
    35  	c.Assert(encoder, check.NotNil)
    36  	msg, err := encoder.newFlatMessageForDML(testCaseUpdate)
    37  	c.Assert(err, check.IsNil)
    38  
    39  	c.Assert(msg.EventType, check.Equals, "UPDATE")
    40  	c.Assert(msg.ExecutionTime, check.Equals, convertToCanalTs(testCaseUpdate.CommitTs))
    41  	c.Assert(msg.tikvTs, check.Equals, testCaseUpdate.CommitTs)
    42  	c.Assert(msg.Schema, check.Equals, "cdc")
    43  	c.Assert(msg.Table, check.Equals, "person")
    44  	c.Assert(msg.IsDDL, check.IsFalse)
    45  	c.Assert(msg.SQLType, check.DeepEquals, map[string]int32{
    46  		"id":      int32(JavaSQLTypeBIGINT),
    47  		"name":    int32(JavaSQLTypeVARCHAR),
    48  		"tiny":    int32(JavaSQLTypeSMALLINT),
    49  		"comment": int32(JavaSQLTypeVARCHAR),
    50  		"blob":    int32(JavaSQLTypeBLOB),
    51  	})
    52  	c.Assert(msg.MySQLType, check.DeepEquals, map[string]string{
    53  		"id":      "int",
    54  		"name":    "varchar",
    55  		"tiny":    "tinyint",
    56  		"comment": "text",
    57  		"blob":    "blob",
    58  	})
    59  	encodedBytes, err := charmap.ISO8859_1.NewDecoder().Bytes([]byte("测试blob"))
    60  	c.Assert(err, check.IsNil)
    61  	c.Assert(msg.Data, check.DeepEquals, []map[string]interface{}{
    62  		{
    63  			"id":      "1",
    64  			"name":    "Bob",
    65  			"tiny":    "255",
    66  			"comment": "测试",
    67  			"blob":    string(encodedBytes),
    68  		},
    69  	})
    70  	c.Assert(msg.Old, check.DeepEquals, []map[string]interface{}{
    71  		{
    72  			"id":      "1",
    73  			"name":    "Alice",
    74  			"tiny":    "255",
    75  			"comment": "测试",
    76  			"blob":    string(encodedBytes),
    77  		},
    78  	})
    79  }
    80  
    81  func (s *canalFlatSuite) TestNewCanalFlatMessageFromDDL(c *check.C) {
    82  	defer testleak.AfterTest(c)()
    83  	encoder := &CanalFlatEventBatchEncoder{builder: NewCanalEntryBuilder()}
    84  	c.Assert(encoder, check.NotNil)
    85  
    86  	msg := encoder.newFlatMessageForDDL(testCaseDdl)
    87  	c.Assert(msg, check.NotNil)
    88  
    89  	c.Assert(msg.tikvTs, check.Equals, testCaseDdl.CommitTs)
    90  	c.Assert(msg.ExecutionTime, check.Equals, convertToCanalTs(testCaseDdl.CommitTs))
    91  	c.Assert(msg.IsDDL, check.IsTrue)
    92  	c.Assert(msg.Schema, check.Equals, "cdc")
    93  	c.Assert(msg.Table, check.Equals, "person")
    94  	c.Assert(msg.Query, check.Equals, testCaseDdl.Query)
    95  	c.Assert(msg.EventType, check.Equals, "CREATE")
    96  }
    97  
    98  func (s *canalFlatSuite) TestBatching(c *check.C) {
    99  	defer testleak.AfterTest(c)()
   100  	encoder := &CanalFlatEventBatchEncoder{builder: NewCanalEntryBuilder()}
   101  	c.Assert(encoder, check.NotNil)
   102  
   103  	updateCase := *testCaseUpdate
   104  	lastResolved := uint64(0)
   105  	for i := 1; i < 1000; i++ {
   106  		ts := uint64(i)
   107  		updateCase.CommitTs = ts
   108  		result, err := encoder.AppendRowChangedEvent(&updateCase)
   109  		c.Assert(err, check.IsNil)
   110  		c.Assert(result, check.Equals, EncoderNoOperation)
   111  
   112  		if i >= 100 && (i%100 == 0 || i == 999) {
   113  			resolvedTs := uint64(i - 50)
   114  			if i == 999 {
   115  				resolvedTs = 999
   116  			}
   117  			result, err := encoder.AppendResolvedEvent(resolvedTs)
   118  
   119  			c.Assert(err, check.IsNil)
   120  			c.Assert(result, check.Equals, EncoderNeedAsyncWrite)
   121  
   122  			msgs := encoder.Build()
   123  			c.Assert(msgs, check.NotNil)
   124  			c.Assert(msgs, check.HasLen, int(resolvedTs-lastResolved))
   125  
   126  			for j := range msgs {
   127  				var msg canalFlatMessage
   128  				err := json.Unmarshal(msgs[j].Value, &msg)
   129  				c.Assert(err, check.IsNil)
   130  				c.Assert(msg.EventType, check.Equals, "UPDATE")
   131  				c.Assert(msg.ExecutionTime, check.Equals, convertToCanalTs(lastResolved+uint64(i)))
   132  			}
   133  
   134  			lastResolved = resolvedTs
   135  		}
   136  	}
   137  
   138  	c.Assert(encoder.unresolvedBuf, check.HasLen, 0)
   139  	c.Assert(encoder.resolvedBuf, check.HasLen, 0)
   140  }
   141  
   142  var testCaseUpdate = &model.RowChangedEvent{
   143  	CommitTs: 417318403368288260,
   144  	Table: &model.TableName{
   145  		Schema: "cdc",
   146  		Table:  "person",
   147  	},
   148  	Columns: []*model.Column{
   149  		{Name: "id", Type: mysql.TypeLong, Flag: model.PrimaryKeyFlag, Value: 1},
   150  		{Name: "name", Type: mysql.TypeVarchar, Value: "Bob"},
   151  		{Name: "tiny", Type: mysql.TypeTiny, Value: 255},
   152  		{Name: "comment", Type: mysql.TypeBlob, Value: []byte("测试")},
   153  		{Name: "blob", Type: mysql.TypeBlob, Value: []byte("测试blob"), Flag: model.BinaryFlag},
   154  	},
   155  	PreColumns: []*model.Column{
   156  		{Name: "id", Type: mysql.TypeLong, Flag: model.HandleKeyFlag, Value: 1},
   157  		{Name: "name", Type: mysql.TypeVarchar, Value: "Alice"},
   158  		{Name: "tiny", Type: mysql.TypeTiny, Value: 255},
   159  		{Name: "comment", Type: mysql.TypeBlob, Value: []byte("测试")},
   160  		{Name: "blob", Type: mysql.TypeBlob, Value: []byte("测试blob"), Flag: model.BinaryFlag},
   161  	},
   162  }
   163  
   164  var testCaseDdl = &model.DDLEvent{
   165  	CommitTs: 417318403368288260,
   166  	TableInfo: &model.SimpleTableInfo{
   167  		Schema: "cdc", Table: "person",
   168  	},
   169  	Query: "create table person(id int, name varchar(32), tiny tinyint unsigned, comment text, primary key(id))",
   170  	Type:  mm.ActionCreateTable,
   171  }