github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdcv2/metadata/sql/client_test.go (about) 1 // Copyright 2023 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 sql 15 16 import ( 17 "database/sql/driver" 18 "strings" 19 "testing" 20 "time" 21 22 "github.com/DATA-DOG/go-sqlmock" 23 "github.com/pingcap/tiflow/cdc/model" 24 "github.com/pingcap/tiflow/cdcv2/metadata" 25 "github.com/pingcap/tiflow/pkg/config" 26 "github.com/pingcap/tiflow/pkg/errors" 27 "github.com/pingcap/tiflow/pkg/security" 28 "github.com/stretchr/testify/require" 29 ) 30 31 // ================================ Test Create/Update/Delete ================================= 32 33 // Note that updateAt is not included in the test because it is automatically updated by gorm. 34 // TODO(CharlesCheung): add test to verify the correctness of updateAt. 35 func runMockExecTest( 36 t *testing.T, mock sqlmock.Sqlmock, 37 expectedSQL string, args []driver.Value, 38 fn func() error, 39 skipCheck ...bool, /* 0: test ErrMetaRowsAffectedNotMatch, 1: test ErrMetaOpFailed */ 40 ) { 41 testErr := errors.New("test error") 42 43 // Test normal execution 44 mock.ExpectExec(expectedSQL).WithArgs(args...).WillReturnResult(sqlmock.NewResult(1, 1)) 45 err := fn() 46 require.NoError(t, err) 47 48 // Test rows affected not match 49 mock.ExpectExec(expectedSQL).WithArgs(args...).WillReturnResult(sqlmock.NewResult(1, 0)) 50 err = fn() 51 if len(skipCheck) < 1 || !skipCheck[0] { 52 require.ErrorIs(t, err, errors.ErrMetaRowsAffectedNotMatch) 53 } 54 55 // Test op failed 56 mock.ExpectExec(expectedSQL).WithArgs(args...).WillReturnError(testErr) 57 err = fn() 58 if len(skipCheck) < 2 || !skipCheck[1] { 59 require.ErrorIs(t, err, errors.ErrMetaOpFailed) 60 require.ErrorContains(t, err, testErr.Error()) 61 } 62 } 63 64 func TestUpstreamClientExecSQL(t *testing.T) { 65 t.Parallel() 66 67 backendDB, db, mock := newMockDB(t) 68 defer backendDB.Close() 69 cient := NewORMClient("test-upstream-client", db) 70 71 up := &UpstreamDO{ 72 ID: 1, 73 Endpoints: strings.Join([]string{"endpoint1", "endpoint2"}, ","), 74 Config: &security.Credential{ 75 CAPath: "ca-path", 76 }, 77 Version: 1, 78 } 79 config, err := up.Config.Value() 80 require.NoError(t, err) 81 82 // Test createUpstream 83 runMockExecTest( 84 t, mock, 85 "INSERT INTO `upstream` (`endpoints`,`config`,`version`,`update_at`,`id`) VALUES (?,?,?,?,?)", 86 []driver.Value{up.Endpoints, up.Config, up.Version, sqlmock.AnyArg(), up.ID}, 87 func() error { 88 return cient.createUpstream(db, up) 89 }, 90 ) 91 92 // Test deleteUpstream 93 runMockExecTest( 94 t, mock, 95 // TODO(CharlesCheung): delete statement should be optimized, such as remove duplicated fields. 96 // Note: should the version be checked? 97 "DELETE FROM `upstream` WHERE `upstream`.`id` = ?", 98 []driver.Value{up.ID}, 99 func() error { 100 return cient.deleteUpstream(db, up) 101 }, 102 ) 103 104 // Test updateUpstream 105 runMockExecTest( 106 t, mock, 107 "UPDATE `upstream` SET `endpoints`=?,`config`=?,`version`=?,`update_at`=? WHERE id = ? and version = ?", 108 []driver.Value{up.Endpoints, config, up.Version + 1, sqlmock.AnyArg(), up.ID, up.Version}, 109 func() error { 110 return cient.updateUpstream(db, up) 111 }, 112 ) 113 114 // Test updateUpstream with nil config 115 up.Config = nil 116 runMockExecTest( 117 t, mock, 118 "UPDATE `upstream` SET `endpoints`=?,`version`=?,`update_at`=? WHERE id = ? and version = ?", 119 []driver.Value{up.Endpoints, up.Version + 1, sqlmock.AnyArg(), up.ID, up.Version}, 120 func() error { 121 return cient.updateUpstream(db, up) 122 }, 123 ) 124 } 125 126 func TestChangefeedInfoClientExecSQL(t *testing.T) { 127 t.Parallel() 128 129 backendDB, db, mock := newMockDB(t) 130 defer backendDB.Close() 131 client := NewORMClient("test-changefeed-info-client", db) 132 133 info := &ChangefeedInfoDO{ 134 ChangefeedInfo: metadata.ChangefeedInfo{ 135 ChangefeedIdent: metadata.ChangefeedIdent{ 136 UUID: 1, 137 Namespace: "namespace", 138 ID: "id", 139 }, 140 UpstreamID: 1, 141 SinkURI: "sinkURI", 142 StartTs: 1, 143 TargetTs: 1, 144 Config: &config.ReplicaConfig{}, 145 }, 146 RemovedAt: nil, 147 Version: 1, 148 } 149 configValue, err := info.Config.Value() 150 require.NoError(t, err) 151 152 // Test createChangefeedInfo 153 runMockExecTest( 154 t, mock, 155 "INSERT INTO `changefeed_info` ("+ 156 "`namespace`,`id`,`upstream_id`,`sink_uri`,"+ 157 "`start_ts`,`target_ts`,`config`,`removed_at`,"+ 158 "`version`,`update_at`,`uuid`) VALUES (?,?,?,?,?,?,?,?,?,?,?)", 159 []driver.Value{ 160 info.Namespace, info.ID, info.UpstreamID, info.SinkURI, 161 info.StartTs, info.TargetTs, configValue, info.RemovedAt, 162 info.Version, sqlmock.AnyArg(), info.UUID, 163 }, 164 func() error { 165 return client.createChangefeedInfo(db, info) 166 }, 167 ) 168 169 // Test deleteChangefeedInfo 170 runMockExecTest( 171 t, mock, 172 "DELETE FROM `changefeed_info` WHERE `changefeed_info`.`uuid` = ?", 173 []driver.Value{info.UUID}, 174 func() error { 175 return client.deleteChangefeedInfo(db, info) 176 }, 177 ) 178 179 // Test updateChangefeedInfo 180 runMockExecTest( 181 t, mock, 182 "UPDATE `changefeed_info` SET `sink_uri`=?,`start_ts`=?,`target_ts`=?,`config`=?,`version`=?,`update_at`=? WHERE uuid = ? and version = ?", 183 []driver.Value{info.SinkURI, info.StartTs, info.TargetTs, configValue, info.Version + 1, sqlmock.AnyArg(), info.UUID, info.Version}, 184 func() error { 185 return client.updateChangefeedInfo(db, info) 186 }, 187 ) 188 189 // Test updateChangefeedInfo with nil config 190 info.Config = nil 191 runMockExecTest( 192 t, mock, 193 "UPDATE `changefeed_info` SET `sink_uri`=?,`start_ts`=?,`target_ts`=?,`version`=?,`update_at`=? WHERE uuid = ? and version = ?", 194 []driver.Value{info.SinkURI, info.StartTs, info.TargetTs, info.Version + 1, sqlmock.AnyArg(), info.UUID, info.Version}, 195 func() error { 196 return client.updateChangefeedInfo(db, info) 197 }, 198 ) 199 200 // Test markChangefeedRemoved 201 runMockExecTest( 202 t, mock, 203 "UPDATE `changefeed_info` SET `removed_at`=?,`version`=?,`update_at`=? WHERE uuid = ? and version = ?", 204 []driver.Value{sqlmock.AnyArg(), info.Version + 1, sqlmock.AnyArg(), info.UUID, info.Version}, 205 func() error { 206 return client.markChangefeedRemoved(db, info) 207 }, 208 ) 209 } 210 211 func TestChangefeedStateClientExecSQL(t *testing.T) { 212 t.Parallel() 213 214 backendDB, db, mock := newMockDB(t) 215 defer backendDB.Close() 216 cient := NewORMClient("test-changefeed-state-client", db) 217 218 state := &ChangefeedStateDO{ 219 ChangefeedState: metadata.ChangefeedState{ 220 ChangefeedUUID: 1, 221 State: "state", 222 // Note that warning and error could be nil. 223 Warning: nil, 224 Error: &model.RunningError{ 225 Time: time.Now(), 226 Addr: "addr", 227 Code: "code", 228 }, 229 }, 230 Version: 1, 231 } 232 233 errVal, err := state.Error.Value() 234 require.NoError(t, err) 235 236 // Test createChangefeedState 237 runMockExecTest( 238 t, mock, 239 "INSERT INTO `changefeed_state` (`state`,`warning`,`error`,`version`,`update_at`,`changefeed_uuid`) VALUES (?,?,?,?,?,?)", 240 []driver.Value{state.State, state.Warning, errVal, state.Version, sqlmock.AnyArg(), state.ChangefeedUUID}, 241 func() error { 242 return cient.createChangefeedState(db, state) 243 }, 244 ) 245 246 // Test deleteChangefeedState 247 runMockExecTest( 248 t, mock, 249 "DELETE FROM `changefeed_state` WHERE `changefeed_state`.`changefeed_uuid` = ?", 250 []driver.Value{state.ChangefeedUUID}, 251 func() error { 252 return cient.deleteChangefeedState(db, state) 253 }, 254 ) 255 256 // Test updateChangefeedState 257 // Note that a nil error or warning will also be updated. 258 runMockExecTest( 259 t, mock, 260 "UPDATE `changefeed_state` SET `state`=?,`warning`=?,`error`=?,`version`=?,`update_at`=? WHERE changefeed_uuid = ? and version = ?", 261 []driver.Value{state.State, state.Warning, errVal, state.Version + 1, sqlmock.AnyArg(), state.ChangefeedUUID, state.Version}, 262 func() error { 263 return cient.updateChangefeedState(db, state) 264 }, 265 ) 266 } 267 268 func TestScheduleClientExecSQL(t *testing.T) { 269 t.Parallel() 270 271 backendDB, db, mock := newMockDB(t) 272 defer backendDB.Close() 273 cient := NewORMClient("test-schedule-client", db) 274 275 ownerCapture := "test-owner" 276 schedule := &ScheduleDO{ 277 ScheduledChangefeed: metadata.ScheduledChangefeed{ 278 ChangefeedUUID: 1, 279 Owner: &ownerCapture, 280 OwnerState: metadata.SchedRemoved, 281 Processors: nil, 282 TaskPosition: metadata.ChangefeedProgress{ 283 CheckpointTs: 1, 284 }, 285 }, 286 Version: 1, 287 } 288 289 // Test createSchedule 290 runMockExecTest( 291 t, mock, 292 "INSERT INTO `schedule` (`owner`,`owner_state`,`processors`,`task_position`,`version`,`update_at`,`changefeed_uuid`) VALUES (?,?,?,?,?,?,?)", 293 []driver.Value{schedule.Owner, schedule.OwnerState, schedule.Processors, schedule.TaskPosition, schedule.Version, sqlmock.AnyArg(), schedule.ChangefeedUUID}, 294 func() error { 295 return cient.createSchedule(db, schedule) 296 }, 297 ) 298 299 // Test deleteSchedule 300 runMockExecTest( 301 t, mock, 302 "DELETE FROM `schedule` WHERE `schedule`.`changefeed_uuid` = ?", 303 []driver.Value{schedule.ChangefeedUUID}, 304 func() error { 305 return cient.deleteSchedule(db, schedule) 306 }, 307 ) 308 309 // Test updateSchedule with non-empty task position. 310 runMockExecTest( 311 t, mock, 312 "UPDATE `schedule` SET `owner`=?,`owner_state`=?,`task_position`=?,`version`=?,`update_at`=? WHERE changefeed_uuid = ? and version = ?", 313 []driver.Value{schedule.Owner, schedule.OwnerState, schedule.TaskPosition, schedule.Version + 1, sqlmock.AnyArg(), schedule.ChangefeedUUID, schedule.Version}, 314 func() error { 315 return cient.updateSchedule(db, schedule) 316 }, 317 ) 318 319 // Test updateSchedule with empty task position. 320 schedule.TaskPosition = metadata.ChangefeedProgress{} 321 runMockExecTest( 322 t, mock, 323 "UPDATE `schedule` SET `owner`=?,`owner_state`=?,`version`=?,`update_at`=? WHERE changefeed_uuid = ? and version = ?", 324 []driver.Value{schedule.Owner, schedule.OwnerState, schedule.Version + 1, sqlmock.AnyArg(), schedule.ChangefeedUUID, schedule.Version}, 325 func() error { 326 return cient.updateSchedule(db, schedule) 327 }, 328 ) 329 330 // Test updateScheduleOwnerState 331 runMockExecTest( 332 t, mock, 333 "UPDATE `schedule` SET `owner_state`=?,`version`=?,`update_at`=? WHERE changefeed_uuid = ? and version = ?", 334 []driver.Value{schedule.OwnerState, schedule.Version + 1, sqlmock.AnyArg(), schedule.ChangefeedUUID, schedule.Version}, 335 func() error { 336 return cient.updateScheduleOwnerState(db, schedule) 337 }, 338 ) 339 340 // Test updateScheduleOwnerStateByOwnerID 341 runMockExecTest( 342 t, mock, 343 "UPDATE `schedule` SET `owner`=?,`owner_state`=?,`processors`=?,`version`=version + ?,`update_at`=? WHERE owner = ?", 344 []driver.Value{nil, metadata.SchedRemoved, nil, 1, sqlmock.AnyArg(), *schedule.Owner}, 345 func() error { 346 return cient.updateScheduleOwnerStateByOwnerID(db, metadata.SchedRemoved, *schedule.Owner) 347 }, 348 true, // skip check ErrMetaRowsAffectedNotMatch since multiple rows would be affected. 349 ) 350 } 351 352 func TestProgressClientExecSQL(t *testing.T) { 353 t.Parallel() 354 355 backendDB, db, mock := newMockDB(t) 356 defer backendDB.Close() 357 cient := NewORMClient("test-progress-client", db) 358 359 progress := &ProgressDO{ 360 CaptureID: "test-captureID", 361 Progress: nil, 362 Version: 1, 363 } 364 365 // Test createProgress 366 runMockExecTest( 367 t, mock, 368 "INSERT INTO `progress` (`capture_id`,`progress`,`version`,`update_at`) VALUES (?,?,?,?)", 369 []driver.Value{progress.CaptureID, progress.Progress, progress.Version, sqlmock.AnyArg()}, 370 func() error { 371 return cient.createProgress(db, progress) 372 }, 373 ) 374 375 // Test deleteProgress 376 runMockExecTest( 377 t, mock, 378 "DELETE FROM `progress` WHERE `progress`.`capture_id` = ?", 379 []driver.Value{progress.CaptureID}, 380 func() error { 381 return cient.deleteProgress(db, progress) 382 }, 383 ) 384 385 // Test updateProgress 386 progress.Progress = &metadata.CaptureProgress{} 387 runMockExecTest( 388 t, mock, 389 "UPDATE `progress` SET `progress`=?,`version`=?,`update_at`=? WHERE capture_id = ? and version = ?", 390 []driver.Value{progress.Progress, progress.Version + 1, sqlmock.AnyArg(), progress.CaptureID, progress.Version}, 391 func() error { 392 return cient.updateProgress(db, progress) 393 }, 394 ) 395 } 396 397 // ================================ Test Query ================================= 398 399 type queryType int32 400 401 const ( 402 queryTypePoint queryType = iota 403 queryTypeRange 404 queryTypeFullTable 405 ) 406 407 func runMockQueryTest( 408 _ *testing.T, mock sqlmock.Sqlmock, 409 expectedSQL string, args []driver.Value, 410 columns []string, rows []interface{}, 411 getValue func(interface{}) []driver.Value, 412 runQuery func(expectedRowsCnt int, expectedError error), 413 queryTpye queryType, 414 ) { 415 // Test normal execution 416 returnRows := sqlmock.NewRows(columns) 417 for _, row := range rows { 418 returnRows.AddRow(getValue(row)...) 419 } 420 mock.ExpectQuery(expectedSQL).WithArgs(args...).WillReturnRows(returnRows) 421 runQuery(len(rows), nil) 422 423 // Test return empty rows 424 mock.ExpectQuery(expectedSQL).WithArgs(args...).WillReturnRows(sqlmock.NewRows(columns)) 425 if queryTpye == queryTypePoint { 426 runQuery(0, errors.ErrMetaRowsAffectedNotMatch) 427 } else { 428 runQuery(0, nil) 429 } 430 431 // Test return error 432 testErr := errors.New("test error") 433 mock.ExpectQuery(expectedSQL).WithArgs(args...).WillReturnError(testErr) 434 runQuery(0, testErr) 435 } 436 437 func TestUpstreamClientQuerySQL(t *testing.T) { 438 t.Parallel() 439 440 backendDB, db, mock := newMockDB(t) 441 defer backendDB.Close() 442 client := NewORMClient("test-upstream-client-query", db) 443 444 rows := []*UpstreamDO{ 445 { 446 ID: 1, 447 Endpoints: strings.Join([]string{"endpoint1", "endpoint2"}, ","), 448 Config: nil, /* test nil */ 449 Version: 1, 450 UpdateAt: time.Now(), 451 }, 452 { 453 ID: 2, 454 Endpoints: strings.Join([]string{"endpoint3", "endpoint4"}, ","), 455 Config: &security.Credential{}, /* test empty */ 456 Version: 2, 457 UpdateAt: time.Now(), 458 }, 459 } 460 461 // Test queryUpstreams 462 expectedQueryUpstreams := rows 463 queryUpstreamsRows := []interface{}{expectedQueryUpstreams[0], expectedQueryUpstreams[1]} 464 runMockQueryTest(t, mock, 465 "SELECT * FROM `upstream`", nil, 466 []string{"id", "endpoints", "config", "version", "update_at"}, 467 queryUpstreamsRows, 468 func(r interface{}) []driver.Value { 469 row, ok := r.(*UpstreamDO) 470 require.True(t, ok) 471 return []driver.Value{row.ID, row.Endpoints, row.Config, row.Version, row.UpdateAt} 472 }, 473 func(expectedRowsCnt int, expectedError error) { 474 upstreams, err := client.queryUpstreams(db) 475 require.ErrorIs(t, err, expectedError) 476 require.Len(t, upstreams, expectedRowsCnt) 477 if expectedRowsCnt != 0 { 478 require.Equal(t, expectedQueryUpstreams, upstreams) 479 } 480 }, 481 queryTypeFullTable, 482 ) 483 484 // Test queryUpstreamsByUpdateAt 485 expectedQueryUpstreamsByUpdateAt := rows 486 queryUpstreamsByUpdateAtRows := []interface{}{expectedQueryUpstreamsByUpdateAt[0], expectedQueryUpstreamsByUpdateAt[1]} 487 queryAt := time.Now() 488 runMockQueryTest(t, mock, 489 "SELECT * FROM `upstream` WHERE update_at > ?", []driver.Value{queryAt}, 490 []string{"id", "endpoints", "config", "version", "update_at"}, 491 queryUpstreamsByUpdateAtRows, 492 func(r interface{}) []driver.Value { 493 row, ok := r.(*UpstreamDO) 494 require.True(t, ok) 495 return []driver.Value{row.ID, row.Endpoints, row.Config, row.Version, row.UpdateAt} 496 }, 497 func(expectedRowsCnt int, expectedError error) { 498 upstreams, err := client.queryUpstreamsByUpdateAt(db, queryAt) 499 require.ErrorIs(t, err, expectedError) 500 require.Len(t, upstreams, expectedRowsCnt) 501 if expectedRowsCnt != 0 { 502 require.Equal(t, expectedQueryUpstreamsByUpdateAt, upstreams) 503 } 504 }, 505 queryTypeRange, 506 ) 507 508 // Test queryUpstreamByID 509 for _, row := range rows { 510 expectedQueryUpstreamByID := row 511 queryUpstreamByIDRows := []interface{}{row} 512 runMockQueryTest(t, mock, 513 "SELECT * FROM `upstream` WHERE id = ? LIMIT 1", 514 []driver.Value{expectedQueryUpstreamByID.ID}, 515 []string{"id", "endpoints", "config", "version", "update_at"}, 516 queryUpstreamByIDRows, 517 func(r interface{}) []driver.Value { 518 row, ok := r.(*UpstreamDO) 519 require.True(t, ok) 520 return []driver.Value{row.ID, row.Endpoints, row.Config, row.Version, row.UpdateAt} 521 }, 522 func(expectedRowsCnt int, expectedError error) { 523 upstream, err := client.queryUpstreamByID(db, expectedQueryUpstreamByID.ID) 524 require.ErrorIs(t, err, expectedError) 525 if expectedRowsCnt != 0 { 526 require.Equal(t, expectedQueryUpstreamByID, upstream) 527 } else { 528 require.Nil(t, upstream) 529 } 530 }, 531 queryTypePoint, 532 ) 533 } 534 } 535 536 func TestChangefeedInfoClientQuerySQL(t *testing.T) { 537 t.Parallel() 538 539 backendDB, db, mock := newMockDB(t) 540 defer backendDB.Close() 541 client := NewORMClient("test-changefeed-info-client-query", db) 542 543 rows := []*ChangefeedInfoDO{ 544 { 545 ChangefeedInfo: metadata.ChangefeedInfo{ 546 ChangefeedIdent: metadata.ChangefeedIdent{ 547 UUID: 1, 548 Namespace: "namespace", 549 ID: "id", 550 }, 551 UpstreamID: 1, 552 SinkURI: "sinkURI", 553 StartTs: 1, 554 TargetTs: 1, 555 Config: nil, /* test nil */ 556 }, 557 RemovedAt: nil, /* test nil */ 558 Version: 1, 559 UpdateAt: time.Now(), 560 }, 561 { 562 ChangefeedInfo: metadata.ChangefeedInfo{ 563 ChangefeedIdent: metadata.ChangefeedIdent{ 564 UUID: 2, 565 Namespace: "namespace", 566 ID: "id", 567 }, 568 UpstreamID: 2, 569 SinkURI: "sinkURI", 570 StartTs: 2, 571 TargetTs: 2, 572 Config: &config.ReplicaConfig{}, /* test empty */ 573 }, 574 RemovedAt: &time.Time{}, /* test empty */ 575 Version: 2, 576 UpdateAt: time.Now(), 577 }, 578 } 579 580 // Test queryChangefeedInfos 581 expectedQueryChangefeedInfos := rows 582 queryChangefeedInfosRows := []interface{}{expectedQueryChangefeedInfos[0], expectedQueryChangefeedInfos[1]} 583 runMockQueryTest(t, mock, 584 "SELECT * FROM `changefeed_info`", nil, 585 []string{ 586 "uuid", "namespace", "id", "upstream_id", "sink_uri", 587 "start_ts", "target_ts", "config", "removed_at", 588 "version", "update_at", 589 }, 590 queryChangefeedInfosRows, 591 func(r interface{}) []driver.Value { 592 row, ok := r.(*ChangefeedInfoDO) 593 require.True(t, ok) 594 return []driver.Value{ 595 row.UUID, row.Namespace, row.ID, row.UpstreamID, row.SinkURI, 596 row.StartTs, row.TargetTs, row.Config, row.RemovedAt, 597 row.Version, row.UpdateAt, 598 } 599 }, 600 func(expectedRowsCnt int, expectedError error) { 601 changefeedInfos, err := client.queryChangefeedInfos(db) 602 require.ErrorIs(t, err, expectedError) 603 require.Len(t, changefeedInfos, expectedRowsCnt) 604 if expectedRowsCnt != 0 { 605 require.Equal(t, expectedQueryChangefeedInfos, changefeedInfos) 606 } 607 }, 608 queryTypeFullTable, 609 ) 610 611 // Test queryChangefeedInfosByUpdateAt 612 expectedQueryChangefeedInfosByUpdateAt := rows 613 queryChangefeedInfosByUpdateAtRows := []interface{}{ 614 expectedQueryChangefeedInfosByUpdateAt[0], 615 expectedQueryChangefeedInfosByUpdateAt[1], 616 } 617 queryAt := time.Now() 618 runMockQueryTest(t, mock, 619 "SELECT * FROM `changefeed_info` WHERE update_at > ?", []driver.Value{queryAt}, 620 []string{ 621 "uuid", "namespace", "id", "upstream_id", "sink_uri", 622 "start_ts", "target_ts", "config", "removed_at", 623 "version", "update_at", 624 }, 625 queryChangefeedInfosByUpdateAtRows, 626 func(r interface{}) []driver.Value { 627 row, ok := r.(*ChangefeedInfoDO) 628 require.True(t, ok) 629 return []driver.Value{ 630 row.UUID, row.Namespace, row.ID, row.UpstreamID, row.SinkURI, 631 row.StartTs, row.TargetTs, row.Config, row.RemovedAt, 632 row.Version, row.UpdateAt, 633 } 634 }, 635 func(expectedRowsCnt int, expectedError error) { 636 changefeedInfos, err := client.queryChangefeedInfosByUpdateAt(db, queryAt) 637 require.ErrorIs(t, err, expectedError) 638 require.Len(t, changefeedInfos, expectedRowsCnt) 639 if expectedRowsCnt != 0 { 640 require.Equal(t, expectedQueryChangefeedInfosByUpdateAt, changefeedInfos) 641 } 642 }, 643 queryTypeRange, 644 ) 645 646 // Test queryChangefeedInfosByUUIDs 647 expectedQueryChangefeedInfosByUUIDs := rows 648 queryChangefeedInfosByUUIDsRows := []interface{}{ 649 expectedQueryChangefeedInfosByUUIDs[0], 650 expectedQueryChangefeedInfosByUUIDs[1], 651 } 652 runMockQueryTest(t, mock, 653 "SELECT * FROM `changefeed_info` WHERE uuid IN (?,?)", []driver.Value{1, 2}, 654 []string{ 655 "uuid", "namespace", "id", "upstream_id", "sink_uri", 656 "start_ts", "target_ts", "config", "removed_at", 657 "version", "update_at", 658 }, 659 queryChangefeedInfosByUUIDsRows, 660 func(r interface{}) []driver.Value { 661 row, ok := r.(*ChangefeedInfoDO) 662 require.True(t, ok) 663 return []driver.Value{ 664 row.UUID, row.Namespace, row.ID, row.UpstreamID, row.SinkURI, 665 row.StartTs, row.TargetTs, row.Config, row.RemovedAt, 666 row.Version, row.UpdateAt, 667 } 668 }, 669 func(expectedRowsCnt int, expectedError error) { 670 changefeedInfos, err := client.queryChangefeedInfosByUUIDs(db, 1, 2) 671 require.ErrorIs(t, err, expectedError) 672 require.Len(t, changefeedInfos, expectedRowsCnt) 673 if expectedRowsCnt != 0 { 674 require.Equal(t, expectedQueryChangefeedInfosByUUIDs, changefeedInfos) 675 } 676 }, 677 queryTypePoint, 678 ) 679 680 // Test queryChangefeedInfoByUUID 681 for _, row := range rows { 682 expectedQueryChangefeedInfoByUUID := row 683 queryChangefeedInfoByUUIDRows := []interface{}{row} 684 runMockQueryTest(t, mock, 685 "SELECT * FROM `changefeed_info` WHERE uuid = ? LIMIT 1", 686 []driver.Value{expectedQueryChangefeedInfoByUUID.UUID}, 687 []string{ 688 "uuid", "namespace", "id", "upstream_id", "sink_uri", 689 "start_ts", "target_ts", "config", "removed_at", 690 "version", "update_at", 691 }, 692 queryChangefeedInfoByUUIDRows, 693 func(r interface{}) []driver.Value { 694 row, ok := r.(*ChangefeedInfoDO) 695 require.True(t, ok) 696 return []driver.Value{ 697 row.UUID, row.Namespace, row.ID, row.UpstreamID, row.SinkURI, 698 row.StartTs, row.TargetTs, row.Config, row.RemovedAt, 699 row.Version, row.UpdateAt, 700 } 701 }, 702 func(expectedRowsCnt int, expectedError error) { 703 changefeedInfo, err := client.queryChangefeedInfoByUUID(db, expectedQueryChangefeedInfoByUUID.UUID) 704 require.ErrorIs(t, err, expectedError) 705 if expectedRowsCnt != 0 { 706 require.Equal(t, expectedQueryChangefeedInfoByUUID, changefeedInfo) 707 } else { 708 require.Nil(t, changefeedInfo) 709 } 710 }, 711 queryTypePoint, 712 ) 713 } 714 } 715 716 func TestChangefeedStateClientQuerySQL(t *testing.T) { 717 t.Parallel() 718 719 backendDB, db, mock := newMockDB(t) 720 defer backendDB.Close() 721 client := NewORMClient("test-changefeed-state-client-query", db) 722 723 rows := []*ChangefeedStateDO{ 724 { 725 ChangefeedState: metadata.ChangefeedState{ 726 ChangefeedUUID: 1, 727 State: "state", 728 // Note that warning and error could be nil. 729 Warning: nil, /* test nil */ 730 Error: &model.RunningError{}, /* test empty*/ 731 }, 732 Version: 1, 733 UpdateAt: time.Now(), 734 }, 735 { 736 ChangefeedState: metadata.ChangefeedState{ 737 ChangefeedUUID: 2, 738 State: "state", 739 Warning: &model.RunningError{ 740 // ref: TestRunningErrorScan 741 // Time: time.Now(), 742 Addr: "addr", 743 Code: "warn", 744 }, 745 Error: &model.RunningError{ 746 // Time: time.Now(), 747 Addr: "addr", 748 Code: "error", 749 }, 750 }, 751 Version: 2, 752 UpdateAt: time.Now(), 753 }, 754 } 755 756 // Test queryChangefeedStates 757 expectedQueryChangefeedStates := rows 758 queryChangefeedStatesRows := []interface{}{expectedQueryChangefeedStates[0], expectedQueryChangefeedStates[1]} 759 runMockQueryTest(t, mock, 760 "SELECT * FROM `changefeed_state`", nil, 761 []string{"changefeed_uuid", "state", "warning", "error", "version", "update_at"}, 762 queryChangefeedStatesRows, 763 func(r interface{}) []driver.Value { 764 row, ok := r.(*ChangefeedStateDO) 765 require.True(t, ok) 766 return []driver.Value{row.ChangefeedUUID, row.State, row.Warning, row.Error, row.Version, row.UpdateAt} 767 }, 768 func(expectedRowsCnt int, expectedError error) { 769 changefeedStates, err := client.queryChangefeedStates(db) 770 require.ErrorIs(t, err, expectedError) 771 require.Len(t, changefeedStates, expectedRowsCnt) 772 if expectedRowsCnt != 0 { 773 require.Equal(t, expectedQueryChangefeedStates, changefeedStates) 774 } 775 }, 776 queryTypeFullTable, 777 ) 778 779 // Test queryChangefeedStatesByUpdateAt 780 expectedQueryChangefeedStatesByUpdateAt := rows 781 queryChangefeedStatesByUpdateAtRows := []interface{}{ 782 expectedQueryChangefeedStatesByUpdateAt[0], 783 expectedQueryChangefeedStatesByUpdateAt[1], 784 } 785 queryAt := time.Now() 786 runMockQueryTest(t, mock, 787 "SELECT * FROM `changefeed_state` WHERE update_at > ?", []driver.Value{queryAt}, 788 []string{"changefeed_uuid", "state", "warning", "error", "version", "update_at"}, 789 queryChangefeedStatesByUpdateAtRows, 790 func(r interface{}) []driver.Value { 791 row, ok := r.(*ChangefeedStateDO) 792 require.True(t, ok) 793 return []driver.Value{row.ChangefeedUUID, row.State, row.Warning, row.Error, row.Version, row.UpdateAt} 794 }, 795 func(expectedRowsCnt int, expectedError error) { 796 changefeedStates, err := client.queryChangefeedStatesByUpdateAt(db, queryAt) 797 require.ErrorIs(t, err, expectedError) 798 require.Len(t, changefeedStates, expectedRowsCnt) 799 if expectedRowsCnt != 0 { 800 require.Equal(t, expectedQueryChangefeedStatesByUpdateAt, changefeedStates) 801 } 802 }, 803 queryTypeRange, 804 ) 805 806 // Test queryChangefeedStateByUUID 807 for _, row := range rows { 808 expectedQueryChangefeedStateByUUID := row 809 queryChangefeedStateByUUIDRows := []interface{}{row} 810 runMockQueryTest(t, mock, 811 "SELECT * FROM `changefeed_state` WHERE changefeed_uuid = ? LIMIT 1", 812 []driver.Value{expectedQueryChangefeedStateByUUID.ChangefeedUUID}, 813 []string{"changefeed_uuid", "state", "warning", "error", "version", "update_at"}, 814 queryChangefeedStateByUUIDRows, 815 func(r interface{}) []driver.Value { 816 row, ok := r.(*ChangefeedStateDO) 817 require.True(t, ok) 818 return []driver.Value{row.ChangefeedUUID, row.State, row.Warning, row.Error, row.Version, row.UpdateAt} 819 }, 820 func(expectedRowsCnt int, expectedError error) { 821 changefeedState, err := client.queryChangefeedStateByUUID(db, expectedQueryChangefeedStateByUUID.ChangefeedUUID) 822 require.ErrorIs(t, err, expectedError) 823 if expectedRowsCnt != 0 { 824 require.Equal(t, expectedQueryChangefeedStateByUUID, changefeedState) 825 } else { 826 require.Nil(t, changefeedState) 827 } 828 }, 829 queryTypePoint, 830 ) 831 } 832 833 // Test queryChangefeedStateByUUIDWithLock 834 for _, row := range rows { 835 expectedQueryChangefeedStateByUUIDWithLock := row 836 queryChangefeedStateByUUIDWithLockRows := []interface{}{row} 837 runMockQueryTest(t, mock, 838 "SELECT * FROM `changefeed_state` WHERE changefeed_uuid = ? LIMIT 1 LOCK IN SHARE MODE", 839 []driver.Value{expectedQueryChangefeedStateByUUIDWithLock.ChangefeedUUID}, 840 []string{"changefeed_uuid", "state", "warning", "error", "version", "update_at"}, 841 queryChangefeedStateByUUIDWithLockRows, 842 func(r interface{}) []driver.Value { 843 row, ok := r.(*ChangefeedStateDO) 844 require.True(t, ok) 845 return []driver.Value{row.ChangefeedUUID, row.State, row.Warning, row.Error, row.Version, row.UpdateAt} 846 }, 847 func(expectedRowsCnt int, expectedError error) { 848 changefeedState, err := client.queryChangefeedStateByUUIDWithLock(db, expectedQueryChangefeedStateByUUIDWithLock.ChangefeedUUID) 849 require.ErrorIs(t, err, expectedError) 850 if expectedRowsCnt != 0 { 851 require.Equal(t, expectedQueryChangefeedStateByUUIDWithLock, changefeedState) 852 } else { 853 require.Nil(t, changefeedState) 854 } 855 }, 856 queryTypePoint, 857 ) 858 } 859 } 860 861 func TestScheduleClientQuerySQL(t *testing.T) { 862 t.Parallel() 863 864 backendDB, db, mock := newMockDB(t) 865 defer backendDB.Close() 866 client := NewORMClient("test-schedule-client-query", db) 867 868 ownerCapture := "test-schedule-client-query" 869 rows := []*ScheduleDO{ 870 { 871 ScheduledChangefeed: metadata.ScheduledChangefeed{ 872 ChangefeedUUID: 1, 873 Owner: nil, /* test nil */ 874 OwnerState: metadata.SchedRemoved, 875 Processors: nil, /* test nil */ 876 TaskPosition: metadata.ChangefeedProgress{ 877 CheckpointTs: 1, 878 }, 879 }, 880 Version: 1, 881 UpdateAt: time.Now(), 882 }, 883 { 884 ScheduledChangefeed: metadata.ScheduledChangefeed{ 885 ChangefeedUUID: 2, 886 Owner: &ownerCapture, 887 OwnerState: metadata.SchedRemoved, 888 Processors: &ownerCapture, 889 TaskPosition: metadata.ChangefeedProgress{ 890 CheckpointTs: 2, 891 }, 892 }, 893 Version: 2, 894 UpdateAt: time.Now(), 895 }, 896 } 897 898 // Test querySchedules 899 expectedQuerySchedules := rows 900 querySchedulesRows := []interface{}{expectedQuerySchedules[0], expectedQuerySchedules[1]} 901 runMockQueryTest(t, mock, 902 "SELECT * FROM `schedule`", nil, 903 []string{ 904 "changefeed_uuid", "owner", "owner_state", "processors", "task_position", 905 "version", "update_at", 906 }, 907 querySchedulesRows, 908 func(r interface{}) []driver.Value { 909 row, ok := r.(*ScheduleDO) 910 require.True(t, ok) 911 return []driver.Value{ 912 row.ChangefeedUUID, row.Owner, row.OwnerState, row.Processors, row.TaskPosition, 913 row.Version, row.UpdateAt, 914 } 915 }, 916 func(expectedRowsCnt int, expectedError error) { 917 schedules, err := client.querySchedules(db) 918 require.ErrorIs(t, err, expectedError) 919 require.Len(t, schedules, expectedRowsCnt) 920 if expectedRowsCnt != 0 { 921 require.Equal(t, expectedQuerySchedules, schedules) 922 } 923 }, 924 queryTypeFullTable, 925 ) 926 927 // Test querySchedulesByUpdateAt 928 expectedQuerySchedulesByUpdateAt := rows 929 querySchedulesByUpdateAtRows := []interface{}{ 930 expectedQuerySchedulesByUpdateAt[0], 931 expectedQuerySchedulesByUpdateAt[1], 932 } 933 queryAt := time.Now() 934 runMockQueryTest(t, mock, 935 "SELECT * FROM `schedule` WHERE update_at > ?", []driver.Value{queryAt}, 936 []string{ 937 "changefeed_uuid", "owner", "owner_state", "processors", "task_position", 938 "version", "update_at", 939 }, 940 querySchedulesByUpdateAtRows, 941 func(r interface{}) []driver.Value { 942 row, ok := r.(*ScheduleDO) 943 require.True(t, ok) 944 return []driver.Value{ 945 row.ChangefeedUUID, row.Owner, row.OwnerState, row.Processors, row.TaskPosition, 946 row.Version, row.UpdateAt, 947 } 948 }, 949 func(expectedRowsCnt int, expectedError error) { 950 schedules, err := client.querySchedulesByUpdateAt(db, queryAt) 951 require.ErrorIs(t, err, expectedError) 952 require.Len(t, schedules, expectedRowsCnt) 953 if expectedRowsCnt != 0 { 954 require.Equal(t, expectedQuerySchedulesByUpdateAt, schedules) 955 } 956 }, 957 queryTypeRange, 958 ) 959 960 // Test querySchedulesByOwnerIDAndUpdateAt 961 expectedQuerySchedulesByOwnerIDAndUpdateAt := rows 962 querySchedulesByOwnerIDAndUpdateAtRows := []interface{}{ 963 expectedQuerySchedulesByOwnerIDAndUpdateAt[0], 964 expectedQuerySchedulesByOwnerIDAndUpdateAt[1], 965 } 966 queryAt = time.Now() 967 runMockQueryTest(t, mock, 968 "SELECT * FROM `schedule` WHERE owner = ? and update_at > ?", []driver.Value{ownerCapture, queryAt}, 969 []string{ 970 "changefeed_uuid", "owner", "owner_state", "processors", "task_position", 971 "version", "update_at", 972 }, 973 querySchedulesByOwnerIDAndUpdateAtRows, 974 func(r interface{}) []driver.Value { 975 row, ok := r.(*ScheduleDO) 976 require.True(t, ok) 977 return []driver.Value{ 978 row.ChangefeedUUID, row.Owner, row.OwnerState, row.Processors, row.TaskPosition, 979 row.Version, row.UpdateAt, 980 } 981 }, 982 func(expectedRowsCnt int, expectedError error) { 983 schedules, err := client.querySchedulesByOwnerIDAndUpdateAt(db, ownerCapture, queryAt) 984 require.ErrorIs(t, err, expectedError) 985 require.Len(t, schedules, expectedRowsCnt) 986 if expectedRowsCnt != 0 { 987 require.Equal(t, expectedQuerySchedulesByOwnerIDAndUpdateAt, schedules) 988 } 989 }, 990 queryTypeRange, 991 ) 992 993 // Test queryScheduleByUUID 994 for _, row := range rows { 995 expectedQueryScheduleByUUID := row 996 queryScheduleByUUIDRows := []interface{}{row} 997 runMockQueryTest(t, mock, 998 "SELECT * FROM `schedule` WHERE changefeed_uuid = ? LIMIT 1", 999 []driver.Value{expectedQueryScheduleByUUID.ChangefeedUUID}, 1000 []string{ 1001 "changefeed_uuid", "owner", "owner_state", "processors", "task_position", 1002 "version", "update_at", 1003 }, 1004 queryScheduleByUUIDRows, 1005 func(r interface{}) []driver.Value { 1006 row, ok := r.(*ScheduleDO) 1007 require.True(t, ok) 1008 return []driver.Value{ 1009 row.ChangefeedUUID, row.Owner, row.OwnerState, row.Processors, row.TaskPosition, 1010 row.Version, row.UpdateAt, 1011 } 1012 }, 1013 func(expectedRowsCnt int, expectedError error) { 1014 schedule, err := client.queryScheduleByUUID(db, expectedQueryScheduleByUUID.ChangefeedUUID) 1015 require.ErrorIs(t, err, expectedError) 1016 if expectedRowsCnt != 0 { 1017 require.Equal(t, expectedQueryScheduleByUUID, schedule) 1018 } else { 1019 require.Nil(t, schedule) 1020 } 1021 }, 1022 queryTypePoint, 1023 ) 1024 } 1025 1026 // Test querySchedulesUinqueOwnerIDs 1027 expectedQuerySchedulesUinqueOwnerIDs := []string{"owner1", "owner2"} 1028 querySchedulesUinqueOwnerIDsRows := []interface{}{ 1029 expectedQuerySchedulesUinqueOwnerIDs[0], 1030 expectedQuerySchedulesUinqueOwnerIDs[1], 1031 } 1032 runMockQueryTest(t, mock, 1033 "SELECT DISTINCT `owner` FROM `schedule` WHERE owner IS NOT NULL", nil, 1034 []string{"owner"}, 1035 querySchedulesUinqueOwnerIDsRows, 1036 func(r interface{}) []driver.Value { 1037 row, ok := r.(string) 1038 require.True(t, ok) 1039 return []driver.Value{row} 1040 }, 1041 func(expectedRowsCnt int, expectedError error) { 1042 ownerIDs, err := client.querySchedulesUinqueOwnerIDs(db) 1043 require.ErrorIs(t, err, expectedError) 1044 require.Len(t, ownerIDs, expectedRowsCnt) 1045 if expectedRowsCnt != 0 { 1046 require.Equal(t, expectedQuerySchedulesUinqueOwnerIDs, ownerIDs) 1047 } 1048 }, 1049 queryTypeFullTable, 1050 ) 1051 } 1052 1053 func TestProgressClientQuerySQL(t *testing.T) { 1054 t.Parallel() 1055 1056 backendDB, db, mock := newMockDB(t) 1057 defer backendDB.Close() 1058 client := NewORMClient("test-progress-client-query", db) 1059 1060 rows := []*ProgressDO{ 1061 { 1062 CaptureID: "captureID-1", 1063 Progress: &metadata.CaptureProgress{ 1064 1: { 1065 CheckpointTs: 1, 1066 MinTableBarrierTs: 1, 1067 }, 1068 2: { 1069 CheckpointTs: 2, 1070 MinTableBarrierTs: 2, 1071 }, 1072 }, 1073 Version: 1, 1074 UpdateAt: time.Now(), 1075 }, 1076 { 1077 CaptureID: "captureID-2", 1078 Progress: &metadata.CaptureProgress{}, 1079 Version: 2, 1080 UpdateAt: time.Now(), 1081 }, 1082 } 1083 1084 // Test queryProgresses 1085 expectedqueryProgresses := rows 1086 queryProgressesRows := []interface{}{expectedqueryProgresses[0], expectedqueryProgresses[1]} 1087 runMockQueryTest(t, mock, 1088 "SELECT * FROM `progress`", nil, 1089 []string{"capture_id", "progress", "version", "update_at"}, 1090 queryProgressesRows, 1091 func(r interface{}) []driver.Value { 1092 row, ok := r.(*ProgressDO) 1093 require.True(t, ok) 1094 return []driver.Value{row.CaptureID, row.Progress, row.Version, row.UpdateAt} 1095 }, 1096 func(expectedRowsCnt int, expectedError error) { 1097 progresses, err := client.queryProgresses(db) 1098 require.ErrorIs(t, err, expectedError) 1099 require.Len(t, progresses, expectedRowsCnt) 1100 if expectedRowsCnt != 0 { 1101 require.Equal(t, expectedqueryProgresses, progresses) 1102 } 1103 }, 1104 queryTypeFullTable, 1105 ) 1106 1107 // Test queryProgressesByUpdateAt 1108 expectedqueryProgressesByUpdateAt := rows 1109 queryProgressesByUpdateAtRows := []interface{}{ 1110 expectedqueryProgressesByUpdateAt[0], 1111 expectedqueryProgressesByUpdateAt[1], 1112 } 1113 queryAt := time.Now() 1114 runMockQueryTest(t, mock, 1115 "SELECT * FROM `progress` WHERE update_at > ?", []driver.Value{queryAt}, 1116 []string{"capture_id", "progress", "version", "update_at"}, 1117 queryProgressesByUpdateAtRows, 1118 func(r interface{}) []driver.Value { 1119 row, ok := r.(*ProgressDO) 1120 require.True(t, ok) 1121 return []driver.Value{row.CaptureID, row.Progress, row.Version, row.UpdateAt} 1122 }, 1123 func(expectedRowsCnt int, expectedError error) { 1124 progresses, err := client.queryProgressesByUpdateAt(db, queryAt) 1125 require.ErrorIs(t, err, expectedError) 1126 require.Len(t, progresses, expectedRowsCnt) 1127 if expectedRowsCnt != 0 { 1128 require.Equal(t, expectedqueryProgressesByUpdateAt, progresses) 1129 } 1130 }, 1131 queryTypeRange, 1132 ) 1133 1134 // Test queryProgressByCaptureID 1135 for _, row := range rows { 1136 expectedqueryProgressByCaptureID := row 1137 queryProgressByCaptureIDRows := []interface{}{row} 1138 runMockQueryTest(t, mock, 1139 "SELECT * FROM `progress` WHERE capture_id = ? LIMIT 1", 1140 []driver.Value{expectedqueryProgressByCaptureID.CaptureID}, 1141 []string{"capture_id", "progress", "version", "update_at"}, 1142 queryProgressByCaptureIDRows, 1143 func(r interface{}) []driver.Value { 1144 row, ok := r.(*ProgressDO) 1145 require.True(t, ok) 1146 return []driver.Value{row.CaptureID, row.Progress, row.Version, row.UpdateAt} 1147 }, 1148 func(expectedRowsCnt int, expectedError error) { 1149 progress, err := client.queryProgressByCaptureID(db, expectedqueryProgressByCaptureID.CaptureID) 1150 require.ErrorIs(t, err, expectedError) 1151 if expectedRowsCnt != 0 { 1152 require.Equal(t, expectedqueryProgressByCaptureID, progress) 1153 } else { 1154 require.Nil(t, progress) 1155 } 1156 }, 1157 queryTypePoint, 1158 ) 1159 } 1160 1161 // Test queryProgressByCaptureIDsWithLock 1162 expectedqueryProgressByCaptureIDsWithLock := rows 1163 queryProgressByCaptureIDsWithLockRows := []interface{}{rows[0], rows[1]} 1164 captureIDs := []string{expectedqueryProgressByCaptureIDsWithLock[0].CaptureID, expectedqueryProgressByCaptureIDsWithLock[1].CaptureID} 1165 runMockQueryTest(t, mock, 1166 "SELECT * FROM `progress` WHERE capture_id in (?,?) LOCK IN SHARE MODE", 1167 []driver.Value{expectedqueryProgressByCaptureIDsWithLock[0].CaptureID, expectedqueryProgressByCaptureIDsWithLock[1].CaptureID}, 1168 []string{"capture_id", "progress", "version", "update_at"}, 1169 queryProgressByCaptureIDsWithLockRows, 1170 func(r interface{}) []driver.Value { 1171 row, ok := r.(*ProgressDO) 1172 require.True(t, ok) 1173 return []driver.Value{row.CaptureID, row.Progress, row.Version, row.UpdateAt} 1174 }, 1175 func(expectedRowsCnt int, expectedError error) { 1176 progress, err := client.queryProgressByCaptureIDsWithLock(db, captureIDs) 1177 require.ErrorIs(t, err, expectedError) 1178 require.Len(t, progress, expectedRowsCnt) 1179 if expectedRowsCnt != 0 { 1180 require.Equal(t, expectedqueryProgressByCaptureIDsWithLock, progress) 1181 } 1182 }, 1183 queryTypeRange, 1184 ) 1185 }