github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/unit/unit_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 unit
    15  
    16  import (
    17  	"context"
    18  	"database/sql/driver"
    19  	"testing"
    20  
    21  	"github.com/go-sql-driver/mysql"
    22  	"github.com/pingcap/check"
    23  	"github.com/pingcap/errors"
    24  	"github.com/pingcap/tidb/pkg/lightning/common"
    25  	tmysql "github.com/pingcap/tidb/pkg/parser/mysql"
    26  	"github.com/pingcap/tiflow/dm/pb"
    27  	"github.com/pingcap/tiflow/dm/pkg/terror"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestSuite(t *testing.T) {
    32  	check.TestingT(t)
    33  }
    34  
    35  var _ = check.Suite(&testUnitSuite{})
    36  
    37  type testUnitSuite struct{}
    38  
    39  func (t *testUnitSuite) TestIsCtxCanceledProcessErr(c *check.C) {
    40  	err := NewProcessError(context.Canceled)
    41  	c.Assert(IsCtxCanceledProcessErr(err), check.IsTrue)
    42  
    43  	err = NewProcessError(errors.New("123"))
    44  	c.Assert(IsCtxCanceledProcessErr(err), check.IsFalse)
    45  
    46  	terr := terror.ErrDBBadConn
    47  	err = NewProcessError(terror.ErrDBBadConn)
    48  	c.Assert(err.GetErrCode(), check.Equals, int32(terr.Code()))
    49  	c.Assert(err.GetErrClass(), check.Equals, terr.Class().String())
    50  	c.Assert(err.GetErrLevel(), check.Equals, terr.Level().String())
    51  	c.Assert(err.GetMessage(), check.Equals, terr.Message())
    52  	c.Assert(err.GetRawCause(), check.Equals, "")
    53  }
    54  
    55  func (t *testUnitSuite) TestJoinProcessErrors(c *check.C) {
    56  	errs := []*pb.ProcessError{
    57  		NewProcessError(terror.ErrDBDriverError.Generate()),
    58  		NewProcessError(terror.ErrSyncUnitDMLStatementFound.Generate()),
    59  	}
    60  
    61  	c.Assert(JoinProcessErrors(errs), check.Equals,
    62  		`ErrCode:10001 ErrClass:"database" ErrScope:"not-set" ErrLevel:"high" Message:"database driver error" Workaround:"Please check the database connection and the database config in configuration file." , ErrCode:36014 ErrClass:"sync-unit" ErrScope:"internal" ErrLevel:"high" Message:"only support ROW format binlog, unexpected DML statement found in query event" `)
    63  }
    64  
    65  func TestIsResumableError(t *testing.T) {
    66  	testCases := []struct {
    67  		err       error
    68  		resumable bool
    69  	}{
    70  		// only DM new error is checked
    71  		{&tmysql.SQLError{Code: 1105, Message: "unsupported modify column length 20 is less than origin 40", State: tmysql.DefaultMySQLState}, true},
    72  		{&tmysql.SQLError{Code: 1105, Message: "unsupported drop integer primary key", State: tmysql.DefaultMySQLState}, true},
    73  		{&tmysql.SQLError{Code: 1072, Message: "column c id 3 does not exist, this column may have been updated by other DDL ran in parallel", State: tmysql.DefaultMySQLState}, true},
    74  		{terror.ErrDBExecuteFailed.Generate("file test.t3.sql: execute statement failed: USE `test_abc`;: context canceled"), true},
    75  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1105, Message: "unsupported modify column length 20 is less than origin 40", State: tmysql.DefaultMySQLState}, "alter table t modify col varchar(20)"), false},
    76  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1105, Message: "unsupported drop integer primary key", State: tmysql.DefaultMySQLState}, "alter table t drop column id"), false},
    77  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1067, Message: "Invalid default value for 'ct'", State: tmysql.DefaultMySQLState}, "CREATE TABLE `tbl` (`c1` int(11) NOT NULL,`ct` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',PRIMARY KEY (`c1`)) ENGINE=InnoDB DEFAULT CHARSET=latin1"), false},
    78  		{terror.ErrDBExecuteFailed.Delegate(errors.New("Error 1062 (23000): Duplicate entry '5' for key 'PRIMARY'")), false},
    79  		{terror.ErrDBExecuteFailed.Delegate(errors.New("INSERT INTO `db`.`tbl` (`c1`,`c2`) VALUES (?,?);: Error 1406: Data too long for column 'c2' at row 1")), false},
    80  		// real error is generated by `Delegate` and multiple `Annotatef`, we use `New` to simplify it
    81  		{terror.ErrParserParseRelayLog.New("parse relay log file bin.000018 from offset 555 in dir /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004: parse relay log file bin.000018 from offset 0 in dir /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004: parse relay log file /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004/bin.000018: binlog checksum mismatch, data may be corrupted"), false},
    82  		{terror.ErrParserParseRelayLog.New("parse relay log file bin.000018 from offset 500 in dir /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004: parse relay log file bin.000018 from offset 0 in dir /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004: parse relay log file /home/tidb/deploy/relay_log/d2e831df-b4ec-11e9-9237-0242ac110008.000004/bin.000018: get event err EOF, need 1567488104 but got 316323"), false},
    83  		{terror.ErrSyncUnitDDLWrongSequence.Generate("wrong sequence", "right sequence"), false},
    84  		{terror.ErrSyncerShardDDLConflict.Generate("conflict DDL", "conflict"), true},
    85  		// others
    86  		{nil, true},
    87  		{errors.New("unknown error"), true},
    88  		{terror.ErrNotSet.Delegate(&tmysql.SQLError{Code: 1236, Message: "Could not find first log file name in binary log index file", State: tmysql.DefaultMySQLState}), false},
    89  		{terror.ErrNotSet.Delegate(&tmysql.SQLError{Code: 1236, Message: "The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires", State: tmysql.DefaultMySQLState}), false},
    90  		{terror.ErrLoadLightningRuntime.Delegate(common.ErrDBConnect), false},
    91  	}
    92  
    93  	for _, tc := range testCases {
    94  		err := NewProcessError(tc.err)
    95  		require.Equal(t, tc.resumable, IsResumableError(err))
    96  	}
    97  }
    98  
    99  func TestIsResumableDBError(t *testing.T) {
   100  	testCases := []struct {
   101  		err       error
   102  		resumable bool
   103  	}{
   104  		// only DM new error is checked
   105  		{&tmysql.SQLError{Code: 1105, Message: "unsupported modify column length 20 is less than origin 40", State: tmysql.DefaultMySQLState}, false},
   106  		{&tmysql.SQLError{Code: 1105, Message: "unsupported drop integer primary key", State: tmysql.DefaultMySQLState}, false},
   107  		{terror.ErrDBExecuteFailed.Generate("file test.t3.sql: execute statement failed: USE `test_abc`;: context canceled"), true},
   108  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1105, Message: "unsupported modify column length 20 is less than origin 40", State: tmysql.DefaultMySQLState}, "alter table t modify col varchar(20)"), false},
   109  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1105, Message: "unsupported drop integer primary key", State: tmysql.DefaultMySQLState}, "alter table t drop column id"), false},
   110  		{terror.ErrDBExecuteFailed.Delegate(&tmysql.SQLError{Code: 1067, Message: "Invalid default value for 'ct'", State: tmysql.DefaultMySQLState}, "CREATE TABLE `tbl` (`c1` int(11) NOT NULL,`ct` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' COMMENT '创建时间',PRIMARY KEY (`c1`)) ENGINE=InnoDB DEFAULT CHARSET=latin1"), false},
   111  		{terror.ErrDBExecuteFailed.Delegate(errors.New("Error 1062 (23000): Duplicate entry '5' for key 'PRIMARY'")), false},
   112  		{terror.ErrDBExecuteFailed.Delegate(errors.New("INSERT INTO `db`.`tbl` (`c1`,`c2`) VALUES (?,?);: Error 1406: Data too long for column 'c2' at row 1")), false},
   113  		// others
   114  		{nil, true},
   115  		{errors.New("unknown error"), true},
   116  		{driver.ErrBadConn, true},
   117  		{mysql.ErrInvalidConn, true},
   118  		{context.Canceled, false},
   119  	}
   120  
   121  	for i, tc := range testCases {
   122  		require.Equal(t, tc.resumable, IsResumableDBError(tc.err), "case %d", i)
   123  	}
   124  }
   125  
   126  func TestIsResumableRelayError(t *testing.T) {
   127  	testCases := []struct {
   128  		err       error
   129  		resumable bool
   130  	}{
   131  		{terror.ErrRelayUUIDSuffixNotValid, false},
   132  		{terror.ErrRelayUUIDSuffixLessThanPrev, false},
   133  		{terror.ErrRelayBinlogNameNotValid, false},
   134  		{terror.ErrRelayNoCurrentUUID, false},
   135  		{terror.ErrDBBadConn, true},
   136  	}
   137  
   138  	for _, tc := range testCases {
   139  		err := NewProcessError(tc.err)
   140  		require.Equal(t, tc.resumable, IsResumableRelayError(err))
   141  	}
   142  }