github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sqlmodel/causality_test.go (about) 1 // Copyright 2022 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 sqlmodel 15 16 import ( 17 "sync" 18 "testing" 19 20 cdcmodel "github.com/pingcap/tiflow/cdc/model" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestCausalityKeys(t *testing.T) { 25 t.Parallel() 26 27 source := &cdcmodel.TableName{Schema: "db", Table: "tb1"} 28 29 cases := []struct { 30 createSQL string 31 preValue []interface{} 32 postValue []interface{} 33 34 causalityKeys []string 35 }{ 36 { 37 "CREATE TABLE tb1 (c INT PRIMARY KEY, c2 INT, c3 VARCHAR(10) UNIQUE)", 38 []interface{}{1, 2, "abc"}, 39 []interface{}{3, 4, "abc"}, 40 []string{"abc.c3.db.tb1", "1.c.db.tb1", "abc.c3.db.tb1", "3.c.db.tb1"}, 41 }, 42 { 43 "CREATE TABLE tb1 (c INT PRIMARY KEY, c2 INT, c3 VARCHAR(10), UNIQUE INDEX(c3(1)))", 44 []interface{}{1, 2, "abc"}, 45 []interface{}{3, 4, "adef"}, 46 []string{"a.c3.db.tb1", "1.c.db.tb1", "a.c3.db.tb1", "3.c.db.tb1"}, 47 }, 48 49 // test not string key 50 { 51 "CREATE TABLE tb1 (a INT, b INT, UNIQUE KEY a(a))", 52 []interface{}{100, 200}, 53 nil, 54 []string{"100.a.db.tb1"}, 55 }, 56 57 // test text 58 { 59 "CREATE TABLE tb1 (a INT, b TEXT, UNIQUE KEY b(b(3)))", 60 []interface{}{1, "1234"}, 61 nil, 62 []string{"123.b.db.tb1"}, 63 }, 64 65 // test composite keys 66 { 67 "CREATE TABLE tb1 (a INT, b TEXT, UNIQUE KEY c2(a, b(3)))", 68 []interface{}{1, "1234"}, 69 nil, 70 []string{"1.a.123.b.db.tb1"}, 71 }, 72 73 // test value is null 74 { 75 "CREATE TABLE tb1 (a INT, b TEXT, UNIQUE KEY c2(a, b(3)))", 76 []interface{}{1, nil}, 77 nil, 78 []string{"1.a.db.tb1"}, 79 }, 80 } 81 82 for _, ca := range cases { 83 ti := mockTableInfo(t, ca.createSQL) 84 change := NewRowChange(source, nil, ca.preValue, ca.postValue, ti, nil, nil) 85 require.Equal(t, ca.causalityKeys, change.CausalityKeys()) 86 } 87 } 88 89 func TestCausalityKeysNoRace(t *testing.T) { 90 t.Parallel() 91 92 source := &cdcmodel.TableName{Schema: "db", Table: "tb1"} 93 ti := mockTableInfo(t, "CREATE TABLE tb1 (c INT PRIMARY KEY, c2 INT, c3 VARCHAR(10) UNIQUE)") 94 var wg sync.WaitGroup 95 for i := 0; i < 100; i++ { 96 wg.Add(1) 97 go func() { 98 change := NewRowChange(source, nil, []interface{}{1, 2, "abc"}, []interface{}{3, 4, "abc"}, ti, nil, nil) 99 change.CausalityKeys() 100 wg.Done() 101 }() 102 } 103 wg.Wait() 104 } 105 106 func TestGetCausalityString(t *testing.T) { 107 t.Parallel() 108 109 source := &cdcmodel.TableName{Schema: "db", Table: "tbl"} 110 111 testCases := []struct { 112 schema string 113 values []interface{} 114 keys []string 115 }{ 116 { 117 // test no keys will use full row data instead of table name 118 schema: `create table t1(a int)`, 119 values: []interface{}{10}, 120 keys: []string{"10.a.db.tbl"}, 121 }, 122 { 123 // one primary key 124 schema: `create table t2(a int primary key, b double)`, 125 values: []interface{}{60, 70.5}, 126 keys: []string{"60.a.db.tbl"}, 127 }, 128 { 129 // one unique key 130 schema: `create table t3(a int unique, b double)`, 131 values: []interface{}{60, 70.5}, 132 keys: []string{"60.a.db.tbl"}, 133 }, 134 { 135 // one ordinary key 136 schema: `create table t4(a int, b double, key(b))`, 137 values: []interface{}{60, 70.5}, 138 keys: []string{"60.a.70.5.b.db.tbl"}, 139 }, 140 { 141 // multiple keys 142 schema: `create table t5(a int, b text, c int, key(a), key(b(3)))`, 143 values: []interface{}{13, "abcdef", 15}, 144 keys: []string{"13.a.abcdef.b.15.c.db.tbl"}, 145 }, 146 { 147 // multiple keys with primary key 148 schema: `create table t6(a int primary key, b varchar(16) unique)`, 149 values: []interface{}{16, "xyz"}, 150 keys: []string{"xyz.b.db.tbl", "16.a.db.tbl"}, 151 }, 152 { 153 // non-integer primary key 154 schema: `create table t65(a int unique, b varchar(16) primary key)`, 155 values: []interface{}{16, "xyz"}, 156 keys: []string{"16.a.db.tbl", "xyz.b.db.tbl"}, 157 }, 158 { 159 // case insensitive 160 schema: `create table t_ci(a int unique, b varchar(16) primary key)default charset=utf8 collate=utf8_unicode_ci`, 161 values: []interface{}{16, "XyZ"}, 162 keys: []string{"16.a.db.tbl", "xyz.b.db.tbl"}, 163 }, 164 { 165 // case sensitive 166 schema: `create table t_bin(a int unique, b varchar(16) primary key)default charset=utf8 collate=utf8_bin`, 167 values: []interface{}{16, "XyZ"}, 168 keys: []string{"16.a.db.tbl", "XyZ.b.db.tbl"}, 169 }, 170 { 171 // primary key of multiple columns 172 schema: `create table t7(a int, b int, primary key(a, b))`, 173 values: []interface{}{59, 69}, 174 keys: []string{"59.a.69.b.db.tbl"}, 175 }, 176 { 177 // ordinary key of multiple columns 178 schema: `create table t75(a int, b int, c int, key(a, b), key(c, b))`, 179 values: []interface{}{48, 58, 68}, 180 keys: []string{"48.a.58.b.68.c.db.tbl"}, 181 }, 182 { 183 // so many keys 184 schema: ` 185 create table t8( 186 a int, b int, c int, 187 primary key(a, b), 188 unique key(b, c), 189 key(a, b, c), 190 unique key(c, a) 191 ) 192 `, 193 values: []interface{}{27, 37, 47}, 194 keys: []string{"27.a.37.b.db.tbl", "37.b.47.c.db.tbl", "47.c.27.a.db.tbl"}, 195 }, 196 { 197 // `null` for unique key 198 schema: ` 199 create table t8( 200 a int, b int default null, 201 primary key(a), 202 unique key(b) 203 ) 204 `, 205 values: []interface{}{17, nil}, 206 keys: []string{"17.a.db.tbl"}, 207 }, 208 } 209 210 for _, ca := range testCases { 211 ti := mockTableInfo(t, ca.schema) 212 change := NewRowChange(source, nil, nil, ca.values, ti, nil, nil) 213 change.lazyInitWhereHandle() 214 require.Equal(t, ca.keys, change.getCausalityString(ca.values)) 215 } 216 }