github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/entry/schema/snapshot_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 schema 15 16 import ( 17 "fmt" 18 "testing" 19 20 timodel "github.com/pingcap/tidb/pkg/parser/model" 21 "github.com/pingcap/tiflow/cdc/model" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestTablesInSchema(t *testing.T) { 26 snap := NewEmptySnapshot(true) 27 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 100)) 28 var vname versionedEntityName 29 30 vname = newVersionedEntityName(1, "tb1", negative(80)) 31 vname.target = 1 32 snap.inner.tableNameToID.ReplaceOrInsert(vname) 33 34 vname = newVersionedEntityName(1, "tb1", negative(90)) 35 vname.target = 2 36 snap.inner.tableNameToID.ReplaceOrInsert(vname) 37 38 vname = newVersionedEntityName(1, "tb1", negative(110)) 39 vname.target = 3 40 snap.inner.tableNameToID.ReplaceOrInsert(vname) 41 42 vname = newVersionedEntityName(1, "tb2", negative(100)) 43 vname.target = 4 44 snap.inner.tableNameToID.ReplaceOrInsert(vname) 45 46 vname = newVersionedEntityName(1, "tb3", negative(120)) 47 vname.target = 5 48 snap.inner.tableNameToID.ReplaceOrInsert(vname) 49 50 vname = newVersionedEntityName(2, "tb1", negative(80)) 51 vname.target = 6 52 snap.inner.tableNameToID.ReplaceOrInsert(vname) 53 54 require.Equal(t, []int64{2, 4}, snap.inner.tablesInSchema("DB_1")) 55 56 vname = newVersionedEntityName(1, "tb1", negative(130)) 57 vname.target = -1 58 snap.inner.tableNameToID.ReplaceOrInsert(vname) 59 snap.inner.currentTs = 130 60 require.Equal(t, []int64{4, 5}, snap.inner.tablesInSchema("DB_1")) 61 } 62 63 func TestIterSchemas(t *testing.T) { 64 snap := NewEmptySnapshot(true) 65 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 90)) 66 require.Nil(t, snap.inner.replaceSchema(newDBInfo(1), 100)) 67 require.Nil(t, snap.inner.createSchema(newDBInfo(2), 110)) 68 require.Nil(t, snap.inner.createSchema(newDBInfo(3), 90)) 69 snap.inner.currentTs = 100 70 71 var schemas []int64 = make([]int64, 0, 3) 72 snap.IterSchemas(func(i *timodel.DBInfo) { 73 schemas = append(schemas, i.ID) 74 }) 75 require.Equal(t, []int64{1, 3}, schemas) 76 } 77 78 func TestSchema(t *testing.T) { 79 snap := NewEmptySnapshot(true) 80 81 // createSchema fails if the schema ID or name already exist. 82 dbName := timodel.CIStr{O: "DB_1", L: "db_1"} 83 require.Nil(t, snap.inner.createSchema(&timodel.DBInfo{ID: 1, Name: dbName}, 100)) 84 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 100)) 85 require.Error(t, snap.inner.createSchema(&timodel.DBInfo{ID: 1}, 110)) 86 require.Error(t, snap.inner.createSchema(&timodel.DBInfo{ID: 2, Name: dbName}, 120)) 87 snap1 := snap.Copy() 88 89 // replaceSchema only success if the schema ID exists. 90 dbName = timodel.CIStr{O: "DB_2", L: "db_2"} 91 require.Error(t, snap.inner.replaceSchema(&timodel.DBInfo{ID: 2}, 130)) 92 require.Nil(t, snap.inner.replaceSchema(&timodel.DBInfo{ID: 1, Name: dbName}, 140)) 93 snap2 := snap.Copy() 94 95 // dropSchema only success if the schema ID exists. 96 require.Error(t, snap.inner.dropSchema(2, 150)) 97 require.Nil(t, snap.inner.dropSchema(1, 170)) 98 snap3 := snap.Copy() 99 100 var db *timodel.DBInfo 101 var ok bool 102 103 // The schema and table should be available based on snap1. 104 db, ok = snap1.SchemaByID(1) 105 require.True(t, ok) 106 require.Equal(t, db.Name.O, "DB_1") 107 _, ok = snap1.TableIDByName("DB_1", "TB_11") 108 require.True(t, ok) 109 _, ok = snap1.PhysicalTableByID(11) 110 require.True(t, ok) 111 112 // The schema and table should be available based on snap2, but with a different schema name. 113 db, ok = snap2.SchemaByID(1) 114 require.True(t, ok) 115 require.Equal(t, db.Name.O, "DB_2") 116 _, ok = snap2.TableIDByName("DB_2", "TB_11") 117 require.True(t, ok) 118 _, ok = snap2.PhysicalTableByID(11) 119 require.True(t, ok) 120 _, ok = snap2.TableIDByName("DB_1", "TB_11") 121 require.False(t, ok) 122 123 // The schema and table should be unavailable based on snap3. 124 _, ok = snap3.SchemaByID(1) 125 require.False(t, ok) 126 _, ok = snap3.PhysicalTableByID(11) 127 require.False(t, ok) 128 _, ok = snap3.TableIDByName("DB_2", "TB_11") 129 require.False(t, ok) 130 } 131 132 func TestTable(t *testing.T) { 133 var ok bool 134 for _, forceReplicate := range []bool{true, false} { 135 snap := NewEmptySnapshot(forceReplicate) 136 137 // createTable should check whether the schema or table exist or not. 138 require.Error(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 100)) 139 snap.inner.createSchema(newDBInfo(1), 110) 140 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 120)) 141 require.Error(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 130)) 142 _, ok = snap.PhysicalTableByID(11) 143 require.True(t, ok) 144 _, ok = snap.PhysicalTableByID(11 + 65536) 145 require.True(t, ok) 146 _, ok = snap.TableByName("DB_1", "TB_11") 147 require.True(t, ok) 148 if !forceReplicate { 149 require.True(t, snap.IsIneligibleTableID(11)) 150 require.True(t, snap.IsIneligibleTableID(11+65536)) 151 } 152 153 // replaceTable should check whether the schema or table exist or not. 154 require.Error(t, snap.inner.replaceTable(newTbInfo(2, "DB_2", 11), 140)) 155 require.Error(t, snap.inner.replaceTable(newTbInfo(1, "DB_1", 12), 150)) 156 require.Nil(t, snap.inner.replaceTable(newTbInfo(1, "DB_1", 11), 160)) 157 _, ok = snap.PhysicalTableByID(11) 158 require.True(t, ok) 159 _, ok = snap.PhysicalTableByID(11 + 65536) 160 require.True(t, ok) 161 _, ok = snap.TableByName("DB_1", "TB_11") 162 require.True(t, ok) 163 if !forceReplicate { 164 require.True(t, snap.IsIneligibleTableID(11)) 165 require.True(t, snap.IsIneligibleTableID(11+65536)) 166 } 167 168 // truncateTable should replace the old one. 169 require.Error(t, snap.inner.truncateTable(12, newTbInfo(1, "DB_1", 13), 170)) 170 require.Nil(t, snap.inner.truncateTable(11, newTbInfo(1, "DB_1", 12), 180)) 171 _, ok = snap.PhysicalTableByID(11) 172 require.False(t, ok) 173 _, ok = snap.PhysicalTableByID(11 + 65536) 174 require.False(t, ok) 175 require.True(t, snap.IsTruncateTableID(11)) 176 _, ok = snap.PhysicalTableByID(12) 177 require.True(t, ok) 178 _, ok = snap.PhysicalTableByID(12 + 65536) 179 require.True(t, ok) 180 _, ok = snap.TableByName("DB_1", "TB_12") 181 require.True(t, ok) 182 if !forceReplicate { 183 require.False(t, snap.IsIneligibleTableID(11)) 184 require.False(t, snap.IsIneligibleTableID(11+65536)) 185 require.True(t, snap.IsIneligibleTableID(12)) 186 require.True(t, snap.IsIneligibleTableID(12+65536)) 187 } 188 189 // dropTable should check the table exists or not. 190 require.Error(t, snap.inner.dropTable(11, 190)) 191 require.Nil(t, snap.inner.dropTable(12, 200)) 192 _, ok = snap.PhysicalTableByID(12) 193 require.False(t, ok) 194 _, ok = snap.PhysicalTableByID(12 + 65536) 195 require.False(t, ok) 196 _, ok = snap.TableByName("DB_1", "TB_12") 197 require.False(t, ok) 198 if !forceReplicate { 199 require.False(t, snap.IsIneligibleTableID(12)) 200 require.False(t, snap.IsIneligibleTableID(12+65536)) 201 } 202 // IterTables should get no available tables. 203 require.Equal(t, snap.TableCount(true, func(table, schema string) bool { 204 return true 205 }), 0) 206 } 207 } 208 209 func TestUpdatePartition(t *testing.T) { 210 var oldTb, newTb *model.TableInfo 211 var snap1, snap2 *Snapshot 212 var info *model.TableInfo 213 var ok bool 214 215 snap := NewEmptySnapshot(false) 216 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 100)) 217 218 // updatePartition fails if the old table is not partitioned. 219 oldTb = newTbInfo(1, "DB_1", 11) 220 oldTb.Partition = nil 221 require.Nil(t, snap.inner.createTable(oldTb, 110)) 222 require.Error(t, snap.inner.updatePartition(newTbInfo(1, "DB_1", 11), false, 120)) 223 224 // updatePartition fails if the new table is not partitioned. 225 require.Nil(t, snap.inner.dropTable(11, 130)) 226 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 140)) 227 newTb = newTbInfo(1, "DB_1", 11) 228 newTb.Partition = nil 229 require.Error(t, snap.inner.updatePartition(newTb, false, 150)) 230 snap1 = snap.Copy() 231 232 newTb = newTbInfo(1, "DB_1", 11) 233 newTb.Partition.Definitions[0] = timodel.PartitionDefinition{ID: 11 + 65536*2} 234 require.Nil(t, snap.inner.updatePartition(newTb, false, 160)) 235 snap2 = snap.Copy() 236 237 info, _ = snap1.PhysicalTableByID(11) 238 require.Equal(t, info.Partition.Definitions[0].ID, int64(11+65536)) 239 _, ok = snap1.PhysicalTableByID(11 + 65536) 240 require.True(t, ok) 241 require.True(t, snap1.IsIneligibleTableID(11+65536)) 242 _, ok = snap1.PhysicalTableByID(11 + 65536*2) 243 require.False(t, ok) 244 require.False(t, snap1.IsIneligibleTableID(11+65536*2)) 245 246 info, _ = snap2.PhysicalTableByID(11) 247 require.Equal(t, info.Partition.Definitions[0].ID, int64(11+65536*2)) 248 _, ok = snap2.PhysicalTableByID(11 + 65536) 249 require.False(t, ok) 250 require.False(t, snap2.IsIneligibleTableID(11+65536)) 251 _, ok = snap2.PhysicalTableByID(11 + 65536*2) 252 require.True(t, ok) 253 require.True(t, snap2.IsIneligibleTableID(11+65536*2)) 254 } 255 256 func TestTruncateTablePartition(t *testing.T) { 257 var oldTb, newTb *model.TableInfo 258 259 snap := NewEmptySnapshot(false) 260 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 100)) 261 262 // updatePartition fails if the old table is not partitioned. 263 oldTb = newTbInfo(1, "DB_1", 11) 264 oldTb.Partition = nil 265 require.Nil(t, snap.inner.createTable(oldTb, 110)) 266 require.Error(t, snap.inner.updatePartition(newTbInfo(1, "DB_1", 11), false, 120)) 267 268 // updatePartition fails if the new table is not partitioned. 269 require.Nil(t, snap.inner.dropTable(11, 130)) 270 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 11), 140)) 271 newTb = newTbInfo(1, "DB_1", 11) 272 newTb.Partition = nil 273 require.Error(t, snap.inner.updatePartition(newTb, false, 150)) 274 275 newTb = newTbInfo(1, "DB_1", 11) 276 newTb.Partition.Definitions[0] = timodel.PartitionDefinition{ID: 11 + 65536*2} 277 require.Nil(t, snap.inner.updatePartition(newTb, true, 160)) 278 require.True(t, snap.IsTruncateTableID(11+65536)) 279 } 280 281 func TestExchangePartition(t *testing.T) { 282 var targetTb, sourceTb *model.TableInfo 283 284 snap := NewEmptySnapshot(false) 285 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 100)) 286 287 // exchange partition fails if the target table is not a partitioned table. 288 targetTbID := int64(11) 289 targetTb = newTbInfo(1, "DB_1", targetTbID) 290 targetTb.Partition = nil 291 require.Nil(t, snap.inner.createTable(targetTb, 110)) 292 require.Error(t, snap.inner.exchangePartition(newTbInfo(1, "DB_1", 11), 120)) 293 require.Nil(t, snap.inner.dropTable(targetTbID, 125)) 294 295 // prepare the target table. 296 targetTb = newTbInfo(1, "DB_1", targetTbID) 297 p1ID := int64(11 + 65536*1) 298 p2ID := int64(11 + 65536*2) 299 targetTb.Partition.Definitions[0] = timodel.PartitionDefinition{ID: p1ID} 300 targetTb.Partition.Definitions = append(targetTb.Partition.Definitions, timodel.PartitionDefinition{ID: p2ID}) 301 // update the target table to a partition table. 302 require.Nil(t, snap.inner.createTable(targetTb, 140)) 303 304 // create source table. 305 sourceTbID := int64(12) 306 sourceTb = newTbInfo(1, "DB_1", sourceTbID) 307 require.Nil(t, snap.inner.createTable(sourceTb, 150)) 308 309 // exchange partition, p1ID should be exchange by sourceTbID. 310 exchangedTargetTb := newTbInfo(1, "DB_1", targetTbID) 311 exchangedTargetTb.Partition.Definitions[0] = timodel.PartitionDefinition{ID: sourceTbID} 312 exchangedTargetTb.Partition.Definitions = append(exchangedTargetTb.Partition.Definitions, timodel.PartitionDefinition{ID: p2ID}) 313 require.Nil(t, snap.inner.exchangePartition(exchangedTargetTb, 160)) 314 315 // make sure we can use the exchanged source table's id to get the target table info, 316 // since the source table has become a partition of target table. 317 tarInfo1, _ := snap.PhysicalTableByID(targetTbID) 318 require.Equal(t, tarInfo1.Partition.Definitions[0].ID, sourceTbID) 319 tarInfo2, ok := snap.PhysicalTableByID(sourceTbID) 320 require.True(t, ok) 321 require.Equal(t, tarInfo1.Name, tarInfo2.Name) 322 323 // make sure we can use the exchanged partition's id to get the source table info. 324 exchangedSourceTb, ok := snap.PhysicalTableByID(p1ID) 325 require.True(t, ok) 326 require.Equal(t, sourceTb.Name, exchangedSourceTb.Name) 327 } 328 329 func TestDrop(t *testing.T) { 330 snap := NewEmptySnapshot(false) 331 332 require.Nil(t, snap.inner.createSchema(newDBInfo(1), 11)) 333 require.Nil(t, snap.inner.createSchema(newDBInfo(2), 12)) 334 require.Nil(t, snap.inner.replaceSchema(newDBInfo(2), 13)) 335 require.Nil(t, snap.inner.dropSchema(2, 14)) 336 337 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 3), 15)) 338 require.Nil(t, snap.inner.createTable(newTbInfo(1, "DB_1", 4), 16)) 339 require.Nil(t, snap.inner.replaceTable(newTbInfo(1, "DB_1", 4), 17)) 340 require.Nil(t, snap.inner.truncateTable(4, newTbInfo(1, "DB_1", 5), 18)) 341 require.Nil(t, snap.inner.dropTable(5, 19)) 342 snap.Drop() 343 344 // After the latest snapshot is dropped, check schema and table count. 345 require.Equal(t, 1, snap.inner.schemas.Len()) 346 require.Equal(t, 1, snap.inner.tables.Len()) 347 require.Equal(t, 1, snap.inner.schemaNameToID.Len()) 348 require.Equal(t, 1, snap.inner.tableNameToID.Len()) 349 require.Equal(t, 1, snap.inner.partitions.Len()) 350 require.Equal(t, 0, snap.inner.truncatedTables.Len()) 351 require.Equal(t, 2, snap.inner.ineligibleTables.Len()) 352 } 353 354 func newDBInfo(id int64) *timodel.DBInfo { 355 return &timodel.DBInfo{ 356 ID: id, 357 Name: timodel.CIStr{ 358 O: fmt.Sprintf("DB_%d", id), 359 L: fmt.Sprintf("db_%d", id), 360 }, 361 } 362 } 363 364 // newTbInfo constructs a test TableInfo with a partition and a sequence. 365 // The partition ID will be tableID + 65536. 366 func newTbInfo(schemaID int64, schemaName string, tableID int64) *model.TableInfo { 367 return &model.TableInfo{ 368 TableInfo: &timodel.TableInfo{ 369 ID: tableID, 370 Name: timodel.CIStr{ 371 O: fmt.Sprintf("TB_%d", tableID), 372 L: fmt.Sprintf("TB_%d", tableID), 373 }, 374 Partition: &timodel.PartitionInfo{ 375 Enable: true, 376 Definitions: []timodel.PartitionDefinition{{ID: 65536 + tableID}}, 377 }, 378 Sequence: &timodel.SequenceInfo{Start: 0}, 379 }, 380 SchemaID: schemaID, 381 TableName: model.TableName{ 382 Schema: schemaName, 383 Table: fmt.Sprintf("TB_%d", tableID), 384 }, 385 } 386 }