github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/binlog-filter/filter_test.go (about)

     1  // Copyright 2018 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 filter
    15  
    16  import (
    17  	"testing"
    18  
    19  	"github.com/pingcap/errors"
    20  	selector "github.com/pingcap/tidb/pkg/util/table-rule-selector"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestFilter(t *testing.T) {
    25  	rules := []*BinlogEventRule{
    26  		{"Test_1_*", "abc*", []EventType{DeleteEvent, InsertEvent, CreateIndex, DropIndex, DropView}, []string{"^DROP\\s+PROCEDURE", "^CREATE\\s+PROCEDURE"}, nil, Ignore},
    27  		{"xxx_*", "abc_*", []EventType{AllDML, NoneDDL}, nil, nil, Ignore},
    28  		{"yyy_*", "abc_*", []EventType{EventType("ALL DML")}, nil, nil, Do},
    29  		{"Test_1_*", "abc*", []EventType{"wrong event"}, []string{"^DROP\\s+PROCEDURE", "^CREATE\\s+PROCEDURE"}, nil, Ignore},
    30  		{"cdc", "t1", []EventType{RebaseAutoID}, nil, nil, Ignore},
    31  	}
    32  
    33  	cases := []struct {
    34  		schema, table string
    35  		event         EventType
    36  		sql           string
    37  		action        ActionType
    38  	}{
    39  		{"test_1_a", "abc1", DeleteEvent, "", Ignore},
    40  		{"test_1_a", "abc1", InsertEvent, "", Ignore},
    41  		{"test_1_a", "abc1", UpdateEvent, "", Do},
    42  		{"test_1_a", "abc1", CreateIndex, "", Ignore},
    43  		{"test_1_a", "abc1", RenameTable, "", Do},
    44  		{"test_1_a", "abc1", NullEvent, "drop procedure abc", Ignore},
    45  		{"test_1_a", "abc1", NullEvent, "create procedure abc", Ignore},
    46  		{"test_1_a", "abc1", NullEvent, "create function abc", Do},
    47  		{"xxx_1", "abc_1", NullEvent, "create function abc", Do},
    48  		{"xxx_1", "abc_1", InsertEvent, "", Ignore},
    49  		{"xxx_1", "abc_1", CreateIndex, "", Do},
    50  		{"yyy_1", "abc_1", InsertEvent, "", Do},
    51  		{"yyy_1", "abc_1", CreateIndex, "", Ignore},
    52  		{"test_1_a", "abc1", DropView, "", Ignore},
    53  		{"cdc", "t1", RebaseAutoID, "", Ignore},
    54  	}
    55  
    56  	// initial binlog event filter
    57  	filter, err := NewBinlogEvent(false, rules)
    58  	require.NoError(t, err)
    59  
    60  	// insert duplicate rules
    61  	for _, rule := range rules {
    62  		err = filter.AddRule(rule)
    63  		require.Error(t, err)
    64  	}
    65  	for _, cs := range cases {
    66  		action, err := filter.Filter(cs.schema, cs.table, cs.event, cs.sql)
    67  		require.NoError(t, err)
    68  		require.Equal(t, cs.action, action)
    69  	}
    70  
    71  	// update rules
    72  	rules[0].Events = []EventType{}
    73  	rules[1].Action = Do
    74  	rules[2].Events = []EventType{"ALL DDL"}
    75  	rules = rules[:3]
    76  	for _, rule := range rules {
    77  		err = filter.UpdateRule(rule)
    78  		require.NoError(t, err)
    79  	}
    80  
    81  	cases[0].action = Do      // delete
    82  	cases[1].action = Do      // insert
    83  	cases[3].action = Do      // create index
    84  	cases[9].action = Do      // match all event and insert
    85  	cases[10].action = Ignore // match none event and create index
    86  	cases[11].action = Ignore // no match
    87  	cases[12].action = Do     // match all ddl
    88  	cases[13].action = Do     // match all ddl
    89  	for _, cs := range cases {
    90  		action, err := filter.Filter(cs.schema, cs.table, cs.event, cs.sql)
    91  		require.NoError(t, err)
    92  		require.Equal(t, cs.action, action)
    93  	}
    94  
    95  	// test multiple rules
    96  	rule := &BinlogEventRule{"test_*", "ab*", []EventType{InsertEvent, AllDDL}, []string{"^DROP\\s+PROCEDURE"}, nil, Do}
    97  	err = filter.AddRule(rule)
    98  	require.NoError(t, err)
    99  	cases[0].action = Ignore // delete
   100  	cases[2].action = Ignore // update
   101  	cases[4].action = Do     // rename table
   102  	cases[7].action = Ignore // create function
   103  	for _, cs := range cases {
   104  		action, err := filter.Filter(cs.schema, cs.table, cs.event, cs.sql)
   105  		require.NoError(t, err)
   106  		require.Equal(t, cs.action, action)
   107  	}
   108  
   109  	// remove rule
   110  	err = filter.RemoveRule(rules[0])
   111  	require.NoError(t, err)
   112  	// remove not existing rule
   113  	err = filter.RemoveRule(rules[0])
   114  	require.Error(t, err)
   115  	cases[3].action = Do // create index
   116  	cases[5].action = Do // drop procedure
   117  	for _, cs := range cases {
   118  		action, err := filter.Filter(cs.schema, cs.table, cs.event, cs.sql)
   119  		require.NoError(t, err)
   120  		require.Equal(t, cs.action, action)
   121  	}
   122  
   123  	// mismatched
   124  	action, err := filter.Filter("xxx_a", "", InsertEvent, "")
   125  	require.NoError(t, err)
   126  	require.Equal(t, Do, action)
   127  
   128  	// invalid rule
   129  	err = filter.Selector.Insert("test_1_*", "abc*", "error", selector.Insert)
   130  	require.NoError(t, err)
   131  	_, err = filter.Filter("test_1_a", "abc", InsertEvent, "")
   132  	require.Error(t, err)
   133  }
   134  
   135  func TestCaseSensitive(t *testing.T) {
   136  	// we test case insensitive in TestFilter
   137  	rules := []*BinlogEventRule{
   138  		{"Test_1_*", "abc*", []EventType{DeleteEvent, InsertEvent, CreateIndex, DropIndex}, []string{"^DROP\\s+PROCEDURE", "^CREATE\\s+PROCEDURE"}, nil, Ignore},
   139  		{"xxx_*", "abc_*", []EventType{AllDML, NoneDDL}, nil, nil, Ignore},
   140  	}
   141  
   142  	cases := []struct {
   143  		schema, table string
   144  		event         EventType
   145  		sql           string
   146  		action        ActionType
   147  	}{
   148  		{"test_1_a", "abc1", DeleteEvent, "", Do},
   149  		{"test_1_a", "abc1", InsertEvent, "", Do},
   150  		{"test_1_a", "abc1", UpdateEvent, "", Do},
   151  		{"test_1_a", "abc1", CreateIndex, "", Do},
   152  		{"test_1_a", "abc1", RenameTable, "", Do},
   153  		{"test_1_a", "abc1", NullEvent, "drop procedure abc", Do},
   154  		{"test_1_a", "abc1", NullEvent, "create procedure abc", Do},
   155  		{"test_1_a", "abc1", NullEvent, "create function abc", Do},
   156  		{"xxx_1", "abc_1", NullEvent, "create function abc", Do},
   157  		{"xxx_1", "abc_1", InsertEvent, "", Ignore},
   158  		{"xxx_1", "abc_1", CreateIndex, "", Do},
   159  	}
   160  
   161  	// initial binlog event filter
   162  	filter, err := NewBinlogEvent(true, rules)
   163  	require.NoError(t, err)
   164  
   165  	// insert duplicate rules
   166  	for _, rule := range rules {
   167  		err = filter.AddRule(rule)
   168  		require.Error(t, err)
   169  	}
   170  	for _, cs := range cases {
   171  		action, err := filter.Filter(cs.schema, cs.table, cs.event, cs.sql)
   172  		require.NoError(t, err)
   173  		require.Equal(t, cs.action, action)
   174  	}
   175  }
   176  
   177  func TestGlobalFilter(t *testing.T) {
   178  	schemaRule := &BinlogEventRule{
   179  		SchemaPattern: "*",
   180  		SQLPattern:    []string{"^FLUSH"},
   181  		Action:        Ignore,
   182  	}
   183  	tableRule := &BinlogEventRule{
   184  		SchemaPattern: "*",
   185  		TablePattern:  "*",
   186  		SQLPattern:    []string{"^FLUSH"},
   187  		Action:        Ignore,
   188  	}
   189  
   190  	cases := []struct {
   191  		schema string
   192  		table  string
   193  		sql    string
   194  		action ActionType
   195  	}{
   196  		{
   197  			schema: "db",
   198  			table:  "tbl",
   199  			sql:    "FLUSH ENGINE LOGS",
   200  			action: Ignore,
   201  		},
   202  		{
   203  			schema: "db",
   204  			table:  "",
   205  			sql:    "FLUSH ENGINE LOGS",
   206  			action: Ignore,
   207  		},
   208  		{
   209  			schema: "",
   210  			table:  "tbl",
   211  			sql:    "FLUSH ENGINE LOGS",
   212  			action: Ignore,
   213  		},
   214  		{
   215  			schema: "",
   216  			table:  "",
   217  			sql:    "FLUSH ENGINE LOGS",
   218  			action: Ignore,
   219  		},
   220  	}
   221  
   222  	// initial binlog event filter with schema rule
   223  	filter, err := NewBinlogEvent(false, []*BinlogEventRule{schemaRule})
   224  	require.NoError(t, err)
   225  
   226  	for _, cs := range cases {
   227  		action, err := filter.Filter(cs.schema, cs.table, NullEvent, cs.sql)
   228  		require.NoError(t, err)
   229  		require.Equal(t, cs.action, action)
   230  	}
   231  
   232  	// remove schema rule
   233  	err = filter.RemoveRule(schemaRule)
   234  	require.NoError(t, err)
   235  
   236  	// add table rule
   237  	err = filter.AddRule(tableRule)
   238  	require.NoError(t, err)
   239  
   240  	for _, cs := range cases {
   241  		action, err := filter.Filter(cs.schema, cs.table, NullEvent, cs.sql)
   242  		require.NoError(t, err)
   243  		require.Equal(t, cs.action, action)
   244  	}
   245  }
   246  
   247  func TestToEventType(t *testing.T) {
   248  	cases := []struct {
   249  		eventStr string
   250  		event    EventType
   251  		err      error
   252  	}{
   253  		{"", NullEvent, nil},
   254  		{"insert", InsertEvent, nil},
   255  		{"Insert", InsertEvent, nil},
   256  		{"update", UpdateEvent, nil},
   257  		{"UPDATE", UpdateEvent, nil},
   258  		{"delete", DeleteEvent, nil},
   259  		{"create", NullEvent, errors.NotValidf("event type %s", "create")},
   260  		{"create schema", CreateDatabase, nil},
   261  		{"create SCHEMA", CreateDatabase, nil},
   262  		{"create database", CreateDatabase, nil},
   263  		{"drop schema", DropDatabase, nil},
   264  		{"drop Schema", DropDatabase, nil},
   265  		{"drop database", DropDatabase, nil},
   266  		{"alter database", AlterDatabase, nil},
   267  		{"alter schema", AlterDatabase, nil},
   268  		{"create index", CreateIndex, nil},
   269  		{"add table partition", AddTablePartition, nil},
   270  		{"drop taBle partition", DropTablePartition, nil},
   271  		{"truncate tablE parTition", TruncateTablePartition, nil},
   272  		{"rebase auto id", RebaseAutoID, nil},
   273  		{"xxx", NullEvent, errors.NotValidf("event type %s", "xxx")},
   274  		{"I don't know", NullEvent, errors.NotValidf("event type %s", "I don't know")},
   275  	}
   276  
   277  	for _, cs := range cases {
   278  		event, err := toEventType(cs.eventStr)
   279  		require.Equal(t, cs.event, event)
   280  		if err != nil {
   281  			require.ErrorContains(t, err, cs.err.Error())
   282  		} else {
   283  			require.NoError(t, err)
   284  		}
   285  	}
   286  }
   287  
   288  func TestClassifyEvent(t *testing.T) {
   289  	cases := []struct {
   290  		event    EventType
   291  		evenType EventType
   292  		err      error
   293  	}{
   294  		{NullEvent, NullEvent, nil},
   295  		// dml
   296  		{InsertEvent, dml, nil},
   297  		{UpdateEvent, dml, nil},
   298  		{DeleteEvent, dml, nil},
   299  		// ddl
   300  		{CreateDatabase, ddl, nil},
   301  		{CreateSchema, ddl, nil},
   302  		{DropDatabase, incompatibleDDL, nil},
   303  		{DropSchema, incompatibleDDL, nil},
   304  		{AlterSchema, ddl, nil},
   305  		{CreateTable, ddl, nil},
   306  		{DropTable, incompatibleDDL, nil},
   307  		{TruncateTable, incompatibleDDL, nil},
   308  		{RenameTable, incompatibleDDL, nil},
   309  		{CreateIndex, ddl, nil},
   310  		{DropIndex, incompatibleDDL, nil},
   311  		{CreateView, ddl, nil},
   312  		{DropView, ddl, nil},
   313  		{AlterTable, ddl, nil},
   314  		{AddTablePartition, ddl, nil},
   315  		{DropTablePartition, incompatibleDDL, nil},
   316  		{RebaseAutoID, incompatibleDDL, nil},
   317  		{TruncateTablePartition, incompatibleDDL, nil},
   318  		{"create", NullEvent, errors.NotValidf("event type %s", "create")},
   319  		{EventType("xxx"), NullEvent, errors.NotValidf("event type %s", "xxx")},
   320  		{EventType("I don't know"), NullEvent, errors.NotValidf("event type %s", "I don't know")},
   321  	}
   322  
   323  	for _, cs := range cases {
   324  		et, err := ClassifyEvent(cs.event)
   325  		require.Equal(t, cs.evenType, et)
   326  		if err != nil {
   327  			require.ErrorContains(t, err, cs.err.Error())
   328  		} else {
   329  			require.NoError(t, err)
   330  		}
   331  	}
   332  }