github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sqlmodel/multirow_test.go (about)

     1  // Copyright 2022 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package sqlmodel
    15  
    16  import (
    17  	"testing"
    18  
    19  	cdcmodel "github.com/pingcap/tiflow/cdc/model"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  type genSQLFunc func(changes ...*RowChange) (string, []interface{})
    24  
    25  func TestGenDeleteMultiRows(t *testing.T) {
    26  	t.Parallel()
    27  
    28  	source1 := &cdcmodel.TableName{Schema: "db", Table: "tb1"}
    29  	source2 := &cdcmodel.TableName{Schema: "db", Table: "tb2"}
    30  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
    31  
    32  	sourceTI1 := mockTableInfo(t, "CREATE TABLE tb1 (c INT PRIMARY KEY, c2 INT)")
    33  	sourceTI2 := mockTableInfo(t, "CREATE TABLE tb2 (c INT PRIMARY KEY, c2 INT)")
    34  	targetTI := mockTableInfo(t, "CREATE TABLE tb (c INT PRIMARY KEY, c2 INT)")
    35  
    36  	change1 := NewRowChange(source1, target, []interface{}{1, 2}, nil, sourceTI1, targetTI, nil)
    37  	change2 := NewRowChange(source2, target, []interface{}{3, 4}, nil, sourceTI2, targetTI, nil)
    38  	sql, args := GenDeleteSQL(change1, change2)
    39  
    40  	require.Equal(t, "DELETE FROM `db`.`tb` WHERE (`c` = ?) OR (`c` = ?)", sql)
    41  	require.Equal(t, []interface{}{1, 3}, args)
    42  }
    43  
    44  func TestGenUpdateMultiRows(t *testing.T) {
    45  	t.Parallel()
    46  	testGenUpdateMultiRows(t, GenUpdateSQL)
    47  }
    48  
    49  func TestGenUpdateMultiRowsOneColPK(t *testing.T) {
    50  	t.Parallel()
    51  	testGenUpdateMultiRowsOneColPK(t, GenUpdateSQL)
    52  }
    53  
    54  func TestGenUpdateMultiRowsWithVirtualGeneratedColumn(t *testing.T) {
    55  	t.Parallel()
    56  	testGenUpdateMultiRowsWithVirtualGeneratedColumn(t, GenUpdateSQL)
    57  	testGenUpdateMultiRowsWithVirtualGeneratedColumns(t, GenUpdateSQL)
    58  }
    59  
    60  func TestGenUpdateMultiRowsWithStoredGeneratedColumn(t *testing.T) {
    61  	t.Parallel()
    62  	testGenUpdateMultiRowsWithStoredGeneratedColumn(t, GenUpdateSQL)
    63  }
    64  
    65  func testGenUpdateMultiRows(t *testing.T, genUpdate genSQLFunc) {
    66  	source1 := &cdcmodel.TableName{Schema: "db", Table: "tb1"}
    67  	source2 := &cdcmodel.TableName{Schema: "db", Table: "tb2"}
    68  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
    69  
    70  	sourceTI1 := mockTableInfo(t, "CREATE TABLE tb1 (c INT, c2 INT, c3 INT, UNIQUE KEY (c, c2))")
    71  	sourceTI2 := mockTableInfo(t, "CREATE TABLE tb2 (c INT, c2 INT, c3 INT, UNIQUE KEY (c, c2))")
    72  	targetTI := mockTableInfo(t, "CREATE TABLE tb (c INT, c2 INT, c3 INT, UNIQUE KEY (c, c2))")
    73  
    74  	change1 := NewRowChange(source1, target, []interface{}{1, 2, 3}, []interface{}{10, 20, 30}, sourceTI1, targetTI, nil)
    75  	change2 := NewRowChange(source2, target, []interface{}{4, 5, 6}, []interface{}{40, 50, 60}, sourceTI2, targetTI, nil)
    76  	sql, args := genUpdate(change1, change2)
    77  
    78  	expectedSQL := "UPDATE `db`.`tb` SET " +
    79  		"`c`=CASE WHEN `c` = ? AND `c2` = ? THEN ? WHEN `c` = ? AND `c2` = ? THEN ? END, " +
    80  		"`c2`=CASE WHEN `c` = ? AND `c2` = ? THEN ? WHEN `c` = ? AND `c2` = ? THEN ? END, " +
    81  		"`c3`=CASE WHEN `c` = ? AND `c2` = ? THEN ? WHEN `c` = ? AND `c2` = ? THEN ? END " +
    82  		"WHERE (`c` = ? AND `c2` = ?) OR (`c` = ? AND `c2` = ?)"
    83  	expectedArgs := []interface{}{
    84  		1, 2, 10, 4, 5, 40,
    85  		1, 2, 20, 4, 5, 50,
    86  		1, 2, 30, 4, 5, 60,
    87  		1, 2, 4, 5,
    88  	}
    89  
    90  	require.Equal(t, expectedSQL, sql)
    91  	require.Equal(t, expectedArgs, args)
    92  }
    93  
    94  func testGenUpdateMultiRowsOneColPK(t *testing.T, genUpdate genSQLFunc) {
    95  	source1 := &cdcmodel.TableName{Schema: "db", Table: "tb1"}
    96  	source2 := &cdcmodel.TableName{Schema: "db", Table: "tb2"}
    97  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
    98  
    99  	sourceTI1 := mockTableInfo(t, "CREATE TABLE tb1 (c INT, c2 INT, c3 INT, PRIMARY KEY (c))")
   100  	sourceTI2 := mockTableInfo(t, "CREATE TABLE tb2 (c INT, c2 INT, c3 INT, PRIMARY KEY (c))")
   101  	targetTI := mockTableInfo(t, "CREATE TABLE tb (c INT, c2 INT, c3 INT, PRIMARY KEY (c))")
   102  
   103  	change1 := NewRowChange(source1, target, []interface{}{1, 2, 3}, []interface{}{10, 20, 30}, sourceTI1, targetTI, nil)
   104  	change2 := NewRowChange(source2, target, []interface{}{4, 5, 6}, []interface{}{40, 50, 60}, sourceTI2, targetTI, nil)
   105  	sql, args := genUpdate(change1, change2)
   106  
   107  	expectedSQL := "UPDATE `db`.`tb` SET " +
   108  		"`c`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END, " +
   109  		"`c2`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END, " +
   110  		"`c3`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END " +
   111  		"WHERE (`c` = ?) OR (`c` = ?)"
   112  	expectedArgs := []interface{}{
   113  		1, 10, 4, 40,
   114  		1, 20, 4, 50,
   115  		1, 30, 4, 60,
   116  		1, 4,
   117  	}
   118  
   119  	require.Equal(t, expectedSQL, sql)
   120  	require.Equal(t, expectedArgs, args)
   121  }
   122  
   123  func testGenUpdateMultiRowsWithVirtualGeneratedColumn(t *testing.T, genUpdate genSQLFunc) {
   124  	source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   125  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   126  
   127  	sourceTI := mockTableInfo(t, "CREATE TABLE tb1 (c INT, c1 int as (c+100) virtual not null, c2 INT, c3 INT, PRIMARY KEY (c))")
   128  	targetTI := mockTableInfo(t, "CREATE TABLE tb (c INT, c1 int as (c+100) virtual not null, c2 INT, c3 INT, PRIMARY KEY (c))")
   129  
   130  	change1 := NewRowChange(source, target, []interface{}{1, 101, 2, 3}, []interface{}{10, 110, 20, 30}, sourceTI, targetTI, nil)
   131  	change2 := NewRowChange(source, target, []interface{}{4, 104, 5, 6}, []interface{}{40, 140, 50, 60}, sourceTI, targetTI, nil)
   132  	change3 := NewRowChange(source, target, []interface{}{7, 107, 8, 9}, []interface{}{70, 170, 80, 90}, sourceTI, targetTI, nil)
   133  	sql, args := genUpdate(change1, change2, change3)
   134  
   135  	expectedSQL := "UPDATE `db`.`tb` SET " +
   136  		"`c`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END, " +
   137  		"`c2`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END, " +
   138  		"`c3`=CASE WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? WHEN `c` = ? THEN ? END " +
   139  		"WHERE (`c` = ?) OR (`c` = ?) OR (`c` = ?)"
   140  	expectedArgs := []interface{}{
   141  		1, 10, 4, 40, 7, 70,
   142  		1, 20, 4, 50, 7, 80,
   143  		1, 30, 4, 60, 7, 90,
   144  		1, 4, 7,
   145  	}
   146  
   147  	require.Equal(t, expectedSQL, sql)
   148  	require.Equal(t, expectedArgs, args)
   149  }
   150  
   151  // multiple generated columns test case
   152  func testGenUpdateMultiRowsWithVirtualGeneratedColumns(t *testing.T, genUpdate genSQLFunc) {
   153  	source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   154  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   155  
   156  	sourceTI := mockTableInfo(t, `CREATE TABLE tb1 (c0 int as (c4*c4) virtual not null,
   157  	c1 int as (c+100) virtual not null, c2 INT, c3 INT, c4 INT, PRIMARY KEY (c4))`)
   158  	targetTI := mockTableInfo(t, `CREATE TABLE tb (c0 int as (c4*c4) virtual not null,
   159  	c1 int as (c+100) virtual not null, c2 INT, c3 INT, c4 INT, PRIMARY KEY (c4))`)
   160  
   161  	change1 := NewRowChange(source, target, []interface{}{1, 101, 2, 3, 1}, []interface{}{100, 110, 20, 30, 10}, sourceTI, targetTI, nil)
   162  	change2 := NewRowChange(source, target, []interface{}{16, 104, 5, 6, 4}, []interface{}{1600, 140, 50, 60, 40}, sourceTI, targetTI, nil)
   163  	change3 := NewRowChange(source, target, []interface{}{49, 107, 8, 9, 7}, []interface{}{4900, 170, 80, 90, 70}, sourceTI, targetTI, nil)
   164  	sql, args := genUpdate(change1, change2, change3)
   165  
   166  	expectedSQL := "UPDATE `db`.`tb` SET " +
   167  		"`c2`=CASE WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? END, " +
   168  		"`c3`=CASE WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? END, " +
   169  		"`c4`=CASE WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? WHEN `c4` = ? THEN ? END " +
   170  		"WHERE (`c4` = ?) OR (`c4` = ?) OR (`c4` = ?)"
   171  	expectedArgs := []interface{}{
   172  		1, 20, 4, 50, 7, 80,
   173  		1, 30, 4, 60, 7, 90,
   174  		1, 10, 4, 40, 7, 70,
   175  		1, 4, 7,
   176  	}
   177  
   178  	require.Equal(t, expectedSQL, sql)
   179  	require.Equal(t, expectedArgs, args)
   180  }
   181  
   182  func testGenUpdateMultiRowsWithStoredGeneratedColumn(t *testing.T, genUpdate genSQLFunc) {
   183  	source := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   184  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   185  
   186  	sourceTI := mockTableInfo(t, "CREATE TABLE tb1 (c INT, c1 int as (c+100) stored, c2 INT, c3 INT, PRIMARY KEY (c1))")
   187  	targetTI := mockTableInfo(t, "CREATE TABLE tb (c INT, c1 int as (c+100) stored, c2 INT, c3 INT, PRIMARY KEY (c1))")
   188  
   189  	change1 := NewRowChange(source, target, []interface{}{1, 101, 2, 3}, []interface{}{10, 110, 20, 30}, sourceTI, targetTI, nil)
   190  	change2 := NewRowChange(source, target, []interface{}{4, 104, 5, 6}, []interface{}{40, 140, 50, 60}, sourceTI, targetTI, nil)
   191  	change3 := NewRowChange(source, target, []interface{}{7, 107, 8, 9}, []interface{}{70, 170, 80, 90}, sourceTI, targetTI, nil)
   192  	sql, args := genUpdate(change1, change2, change3)
   193  
   194  	expectedSQL := "UPDATE `db`.`tb` SET " +
   195  		"`c`=CASE WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? END, " +
   196  		"`c2`=CASE WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? END, " +
   197  		"`c3`=CASE WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? WHEN `c1` = ? THEN ? END " +
   198  		"WHERE (`c1` = ?) OR (`c1` = ?) OR (`c1` = ?)"
   199  	expectedArgs := []interface{}{
   200  		101, 10, 104, 40, 107, 70,
   201  		101, 20, 104, 50, 107, 80,
   202  		101, 30, 104, 60, 107, 90,
   203  		101, 104, 107,
   204  	}
   205  
   206  	require.Equal(t, expectedSQL, sql)
   207  	require.Equal(t, expectedArgs, args)
   208  }
   209  
   210  func TestGenInsertMultiRows(t *testing.T) {
   211  	t.Parallel()
   212  
   213  	source1 := &cdcmodel.TableName{Schema: "db", Table: "tb1"}
   214  	source2 := &cdcmodel.TableName{Schema: "db", Table: "tb2"}
   215  	target := &cdcmodel.TableName{Schema: "db", Table: "tb"}
   216  
   217  	sourceTI1 := mockTableInfo(t, "CREATE TABLE tb1 (gen INT AS (c+1), c INT PRIMARY KEY, c2 INT)")
   218  	sourceTI2 := mockTableInfo(t, "CREATE TABLE tb2 (gen INT AS (c+1), c INT PRIMARY KEY, c2 INT)")
   219  	targetTI := mockTableInfo(t, "CREATE TABLE tb (gen INT AS (c+1), c INT PRIMARY KEY, c2 INT)")
   220  
   221  	change1 := NewRowChange(source1, target, nil, []interface{}{2, 1, 2}, sourceTI1, targetTI, nil)
   222  	change2 := NewRowChange(source2, target, nil, []interface{}{4, 3, 4}, sourceTI2, targetTI, nil)
   223  
   224  	sql, args := GenInsertSQL(DMLInsert, change1, change2)
   225  	require.Equal(t, "INSERT INTO `db`.`tb` (`c`,`c2`) VALUES (?,?),(?,?)", sql)
   226  	require.Equal(t, []interface{}{1, 2, 3, 4}, args)
   227  
   228  	sql, args = GenInsertSQL(DMLReplace, change1, change2)
   229  	require.Equal(t, "REPLACE INTO `db`.`tb` (`c`,`c2`) VALUES (?,?),(?,?)", sql)
   230  	require.Equal(t, []interface{}{1, 2, 3, 4}, args)
   231  
   232  	sql, args = GenInsertSQL(DMLInsertOnDuplicateUpdate, change1, change2)
   233  	require.Equal(t, "INSERT INTO `db`.`tb` (`c`,`c2`) VALUES (?,?),(?,?) ON DUPLICATE KEY UPDATE `c`=VALUES(`c`),`c2`=VALUES(`c2`)", sql)
   234  	require.Equal(t, []interface{}{1, 2, 3, 4}, args)
   235  }