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  }