github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/filter/filter_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 filter 15 16 import ( 17 "testing" 18 19 timodel "github.com/pingcap/tidb/pkg/parser/model" 20 "github.com/pingcap/tiflow/cdc/model" 21 bf "github.com/pingcap/tiflow/pkg/binlog-filter" 22 "github.com/pingcap/tiflow/pkg/config" 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestShouldUseDefaultRules(t *testing.T) { 27 t.Parallel() 28 29 filter, err := NewFilter(config.GetDefaultReplicaConfig(), "") 30 require.Nil(t, err) 31 require.True(t, filter.ShouldIgnoreTable("information_schema", "")) 32 require.True(t, filter.ShouldIgnoreTable("information_schema", "statistics")) 33 require.True(t, filter.ShouldIgnoreTable("performance_schema", "")) 34 require.False(t, filter.ShouldIgnoreTable("metric_schema", "query_duration")) 35 require.False(t, filter.ShouldIgnoreTable("sns", "user")) 36 require.True(t, filter.ShouldIgnoreTable("tidb_cdc", "repl_mark_a_a")) 37 } 38 39 func TestShouldUseCustomRules(t *testing.T) { 40 t.Parallel() 41 42 filter, err := NewFilter(&config.ReplicaConfig{ 43 Filter: &config.FilterConfig{ 44 Rules: []string{"sns.*", "ecom.*", "!sns.log", "!ecom.test"}, 45 }, 46 }, "") 47 require.Nil(t, err) 48 require.True(t, filter.ShouldIgnoreTable("other", "")) 49 require.True(t, filter.ShouldIgnoreTable("other", "what")) 50 require.False(t, filter.ShouldIgnoreTable("sns", "")) 51 require.False(t, filter.ShouldIgnoreTable("ecom", "order")) 52 require.False(t, filter.ShouldIgnoreTable("ecom", "order")) 53 require.True(t, filter.ShouldIgnoreTable("ecom", "test")) 54 require.True(t, filter.ShouldIgnoreTable("sns", "log")) 55 require.True(t, filter.ShouldIgnoreTable("information_schema", "")) 56 57 filter, err = NewFilter(&config.ReplicaConfig{ 58 Filter: &config.FilterConfig{ 59 // 1. match all schema and table 60 // 2. do not match test.season 61 // 3. match all table of schema school 62 // 4. do not match table school.teacher 63 Rules: []string{"*.*", "!test.season", "school.*", "!school.teacher"}, 64 }, 65 }, "") 66 require.True(t, filter.ShouldIgnoreTable("test", "season")) 67 require.False(t, filter.ShouldIgnoreTable("other", "")) 68 require.False(t, filter.ShouldIgnoreTable("school", "student")) 69 require.True(t, filter.ShouldIgnoreTable("school", "teacher")) 70 require.Nil(t, err) 71 72 filter, err = NewFilter(&config.ReplicaConfig{ 73 Filter: &config.FilterConfig{ 74 // 1. match all schema and table 75 // 2. do not match test.season 76 // 3. match all table of schema school 77 // 4. do not match table school.teacher 78 Rules: []string{"*.*", "!test.t1", "!test.t2"}, 79 }, 80 }, "") 81 require.False(t, filter.ShouldIgnoreTable("test", "season")) 82 require.True(t, filter.ShouldIgnoreTable("test", "t1")) 83 require.True(t, filter.ShouldIgnoreTable("test", "t2")) 84 require.Nil(t, err) 85 } 86 87 func TestShouldIgnoreDMLEvent(t *testing.T) { 88 t.Parallel() 89 90 testCases := []struct { 91 cases []struct { 92 schema string 93 table string 94 ts uint64 95 ignore bool 96 } 97 ignoreTxnStartTs []uint64 98 rules []string 99 }{ 100 { 101 cases: []struct { 102 schema string 103 table string 104 ts uint64 105 ignore bool 106 }{ 107 {"sns", "ttta", 1, true}, 108 {"ecom", "aabb", 2, false}, 109 {"sns", "log", 3, true}, 110 {"sns", "log", 4, true}, 111 {"ecom", "test", 5, true}, 112 {"test", "test", 6, true}, 113 {"ecom", "log", 6, false}, 114 }, 115 ignoreTxnStartTs: []uint64{1, 3}, 116 rules: []string{"sns.*", "ecom.*", "!sns.log", "!ecom.test"}, 117 }, 118 { 119 cases: []struct { 120 schema string 121 table string 122 ts uint64 123 ignore bool 124 }{ 125 {"S", "D1", 1, true}, 126 {"S", "Da", 1, false}, 127 {"S", "Db", 1, false}, 128 {"S", "Daa", 1, false}, 129 }, 130 ignoreTxnStartTs: []uint64{}, 131 rules: []string{"*.*", "!S.D[!a-d]"}, 132 }, 133 } 134 135 for _, ftc := range testCases { 136 filter, err := NewFilter(&config.ReplicaConfig{ 137 Filter: &config.FilterConfig{ 138 IgnoreTxnStartTs: ftc.ignoreTxnStartTs, 139 Rules: ftc.rules, 140 }, 141 }, "") 142 require.Nil(t, err) 143 for _, tc := range ftc.cases { 144 dml := &model.RowChangedEvent{ 145 TableInfo: &model.TableInfo{ 146 TableName: model.TableName{ 147 Schema: tc.schema, 148 Table: tc.table, 149 }, 150 }, 151 StartTs: tc.ts, 152 } 153 ignoreDML, err := filter.ShouldIgnoreDMLEvent(dml, model.RowChangedDatums{}, nil) 154 require.Nil(t, err) 155 require.Equal(t, ignoreDML, tc.ignore) 156 } 157 } 158 } 159 160 func TestShouldIgnoreDDL(t *testing.T) { 161 t.Parallel() 162 163 testCases := []struct { 164 cases []struct { 165 startTs uint64 166 schema string 167 table string 168 query string 169 ddlType timodel.ActionType 170 ignore bool 171 } 172 rules []string 173 ignoredTs []uint64 174 eventFilters []*config.EventFilterRule 175 }{ 176 { // cases ignore by startTs 177 cases: []struct { 178 startTs uint64 179 schema string 180 table string 181 query string 182 ddlType timodel.ActionType 183 ignore bool 184 }{ 185 {1, "ts", "", "create database test", timodel.ActionCreateSchema, true}, 186 {2, "ts", "student", "drop database test2", timodel.ActionDropSchema, true}, 187 { 188 3, "ts", "teacher", "ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci", 189 timodel.ActionModifySchemaCharsetAndCollate, true, 190 }, 191 {4, "ts", "man", "create table test.t1(a int primary key)", timodel.ActionCreateTable, false}, 192 {5, "ts", "fruit", "create table test.t1(a int primary key)", timodel.ActionCreateTable, false}, 193 {6, "ts", "insect", "create table test.t1(a int primary key)", timodel.ActionCreateTable, false}, 194 }, 195 rules: []string{"*.*"}, 196 ignoredTs: []uint64{1, 2, 3}, 197 }, 198 { // cases ignore by ddl type. 199 cases: []struct { 200 startTs uint64 201 schema string 202 table string 203 query string 204 ddlType timodel.ActionType 205 ignore bool 206 }{ 207 {1, "event", "", "drop table t1", timodel.ActionDropTable, true}, 208 {1, "event", "January", "drop index i on t1", timodel.ActionDropIndex, true}, 209 {1, "event", "February", "drop index x2 on t2", timodel.ActionDropIndex, true}, 210 {1, "event", "March", "create table t2(age int)", timodel.ActionCreateTable, false}, 211 {1, "event", "April", "create table t2(age int)", timodel.ActionCreateTable, false}, 212 {1, "event", "May", "create table t2(age int)", timodel.ActionCreateTable, false}, 213 }, 214 rules: []string{"*.*"}, 215 eventFilters: []*config.EventFilterRule{ 216 { 217 Matcher: []string{"event.*"}, 218 IgnoreEvent: []bf.EventType{ 219 bf.AlterTable, bf.DropTable, 220 }, 221 }, 222 }, 223 ignoredTs: []uint64{}, 224 }, 225 { // cases ignore by ddl query 226 cases: []struct { 227 startTs uint64 228 schema string 229 table string 230 query string 231 ddlType timodel.ActionType 232 ignore bool 233 }{ 234 {1, "sql_pattern", "t1", "CREATE DATABASE sql_pattern", timodel.ActionCreateSchema, false}, 235 {1, "sql_pattern", "t1", "DROP DATABASE sql_pattern", timodel.ActionDropSchema, true}, 236 { 237 1, "sql_pattern", "t1", 238 "ALTER DATABASE `test_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'", 239 timodel.ActionModifySchemaCharsetAndCollate, 240 false, 241 }, 242 {1, "sql_pattern", "t1", "CREATE TABLE t1(id int primary key)", timodel.ActionCreateTable, false}, 243 {1, "sql_pattern", "t1", "DROP TABLE t1", timodel.ActionDropTable, true}, 244 {1, "sql_pattern", "t1", "CREATE VIEW test.v AS SELECT * FROM t", timodel.ActionCreateView, true}, 245 }, 246 rules: []string{"*.*"}, 247 eventFilters: []*config.EventFilterRule{ 248 { 249 Matcher: []string{"sql_pattern.*"}, 250 IgnoreSQL: []string{"^DROP TABLE", "^CREATE VIEW", "^DROP DATABASE"}, 251 }, 252 }, 253 ignoredTs: []uint64{}, 254 }, 255 } 256 257 for _, ftc := range testCases { 258 filter, err := NewFilter(&config.ReplicaConfig{ 259 Filter: &config.FilterConfig{ 260 Rules: ftc.rules, 261 EventFilters: ftc.eventFilters, 262 IgnoreTxnStartTs: ftc.ignoredTs, 263 }, 264 }, "") 265 require.Nil(t, err) 266 for _, tc := range ftc.cases { 267 ddl := &model.DDLEvent{ 268 StartTs: tc.startTs, 269 TableInfo: &model.TableInfo{ 270 TableName: model.TableName{ 271 Schema: tc.schema, 272 Table: tc.table, 273 }, 274 }, 275 Query: tc.query, 276 Type: tc.ddlType, 277 } 278 ignore, err := filter.ShouldIgnoreDDLEvent(ddl) 279 require.NoError(t, err) 280 require.Equal(t, tc.ignore, ignore, "%#v", tc) 281 } 282 } 283 } 284 285 func TestShouldDiscardDDL(t *testing.T) { 286 t.Parallel() 287 testCases := []struct { 288 cases []struct { 289 schema string 290 table string 291 query string 292 ddlType timodel.ActionType 293 ignore bool 294 } 295 rules []string 296 eventFilters []*config.EventFilterRule 297 }{ 298 { 299 // Discard by not allowed DDL type cases. 300 cases: []struct { 301 schema string 302 table string 303 query string 304 ddlType timodel.ActionType 305 ignore bool 306 }{ 307 {"sns", "", "create database test", timodel.ActionCreateSchema, false}, 308 {"sns", "", "drop database test", timodel.ActionDropSchema, false}, 309 {"test", "", "create database test", timodel.ActionCreateSequence, true}, 310 }, 311 rules: []string{"*.*"}, 312 }, 313 { 314 // Discard by table name cases. 315 cases: []struct { 316 schema string 317 table string 318 query string 319 ddlType timodel.ActionType 320 ignore bool 321 }{ 322 {"sns", "", "create database test", timodel.ActionCreateSchema, false}, 323 {"sns", "", "drop database test", timodel.ActionDropSchema, false}, 324 { 325 "sns", "", "ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci", 326 timodel.ActionModifySchemaCharsetAndCollate, false, 327 }, 328 {"ecom", "", "create database test", timodel.ActionCreateSchema, false}, 329 {"ecom", "aa", "create table test.t1(a int primary key)", timodel.ActionCreateTable, false}, 330 {"ecom", "", "create database test", timodel.ActionCreateSchema, false}, 331 {"test", "", "create database test", timodel.ActionCreateSchema, true}, 332 }, 333 rules: []string{"sns.*", "ecom.*", "!sns.log", "!ecom.test"}, 334 }, 335 { 336 // Discard by schema name cases. 337 cases: []struct { 338 schema string 339 table string 340 query string 341 ddlType timodel.ActionType 342 ignore bool 343 }{ 344 {"schema", "C1", "create database test", timodel.ActionCreateSchema, false}, 345 {"test", "", "drop database test1", timodel.ActionDropSchema, true}, 346 { 347 "dbname", "", "ALTER DATABASE dbname CHARACTER SET utf8 COLLATE utf8_general_ci", 348 timodel.ActionModifySchemaCharsetAndCollate, true, 349 }, 350 {"test", "aa", "create table test.t1(a int primary key)", timodel.ActionCreateTable, true}, 351 {"schema", "C1", "create table test.t1(a int primary key)", timodel.ActionCreateTable, false}, 352 {"schema", "", "create table test.t1(a int primary key)", timodel.ActionCreateTable, true}, 353 }, 354 rules: []string{"schema.C1"}, 355 }, 356 } 357 358 for _, ftc := range testCases { 359 filter, err := NewFilter(&config.ReplicaConfig{ 360 Filter: &config.FilterConfig{ 361 Rules: ftc.rules, 362 EventFilters: ftc.eventFilters, 363 }, 364 }, "") 365 require.Nil(t, err) 366 for _, tc := range ftc.cases { 367 ignore := filter.ShouldDiscardDDL(tc.ddlType, tc.schema, tc.table) 368 require.Equal(t, tc.ignore, ignore, "%#v", tc) 369 } 370 } 371 } 372 373 func TestIsAllowedDDL(t *testing.T) { 374 require.Len(t, ddlWhiteListMap, 36) 375 type testCase struct { 376 timodel.ActionType 377 allowed bool 378 } 379 testCases := make([]testCase, 0, len(ddlWhiteListMap)) 380 for ddlType := range ddlWhiteListMap { 381 testCases = append(testCases, testCase{ddlType, true}) 382 } 383 testCases = append(testCases, testCase{timodel.ActionAddForeignKey, false}) 384 testCases = append(testCases, testCase{timodel.ActionDropForeignKey, false}) 385 testCases = append(testCases, testCase{timodel.ActionCreateSequence, false}) 386 testCases = append(testCases, testCase{timodel.ActionAlterSequence, false}) 387 testCases = append(testCases, testCase{timodel.ActionDropSequence, false}) 388 for _, tc := range testCases { 389 require.Equal(t, tc.allowed, isAllowedDDL(tc.ActionType), "%#v", tc) 390 } 391 } 392 393 func TestIsSchemaDDL(t *testing.T) { 394 cases := []struct { 395 actionType timodel.ActionType 396 isSchema bool 397 }{ 398 {timodel.ActionCreateSchema, true}, 399 {timodel.ActionDropSchema, true}, 400 {timodel.ActionModifySchemaCharsetAndCollate, true}, 401 {timodel.ActionCreateTable, false}, 402 {timodel.ActionDropTable, false}, 403 {timodel.ActionTruncateTable, false}, 404 {timodel.ActionAddColumn, false}, 405 } 406 for _, tc := range cases { 407 require.Equal(t, tc.isSchema, IsSchemaDDL(tc.actionType), "%#v", tc) 408 } 409 }