Skip to content

Commit de3eff2

Browse files
committed
add e2e test to exercise alter table for mysql
1 parent e0224fc commit de3eff2

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

flow/e2e/clickhouse_mysql_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,115 @@ func (s ClickHouseSuite) Test_MySQL_MariaDB_UUID_INET() {
927927
RequireEnvCanceled(s.t, env)
928928
}
929929

930+
// Test_MySQL_AlterTableAddColumnTypes exercises the binlog ALTER TABLE ADD COLUMN path
931+
// (processAlterTableQuery -> QkindFromMysqlColumnType) together with the row-metadata decode
932+
// path for a broad spectrum of MySQL/MariaDB column types. Every type is added in a single
933+
// multi-column ALTER while the mirror is running, then one row carries every value end-to-end
934+
// into ClickHouse. Integer cases use the signed minimum and unsigned maximum to pin the
935+
// width/signedness mapping, which no other MySQL e2e test covers.
936+
//
937+
// The type list mirrors the spellings the parser-level TestParseSQLAlterTableAddColumnTypes
938+
// checks, so the unit test (parse -> qkind) and this test (qkind -> value over the wire) stay
939+
// aligned. Types the TiDB parser rejects in ADD COLUMN (spatial types and MariaDB's native
940+
// UUID/INET4/INET6) cannot travel this path and are covered separately by snapshot-based tests
941+
// (Test_MySQL_Geometric_Types, Test_MySQL_MariaDB_UUID_INET).
942+
func (s ClickHouseSuite) Test_MySQL_AlterTableAddColumnTypes() {
943+
mysource, ok := s.source.(*MySqlSource)
944+
if !ok {
945+
s.t.Skip("only applies to mysql")
946+
}
947+
// ENUM/SET columns added during CDC need binlog row metadata to resolve their member names.
948+
cmp, err := mysource.CompareServerVersion(s.t.Context(), mysql_validation.MySQLMinVersionForBinlogRowMetadata)
949+
require.NoError(s.t, err)
950+
if cmp < 0 {
951+
s.t.Skip("ALTER ADD COLUMN enum/set decoding requires binlog row metadata")
952+
}
953+
954+
srcTableName := "test_add_col_types"
955+
srcFullName := s.attachSchemaSuffix(srcTableName)
956+
dstTableName := "test_add_col_types_dst"
957+
958+
require.NoError(s.t, s.source.Exec(s.t.Context(),
959+
fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (id INT PRIMARY KEY)", srcFullName)))
960+
961+
connectionGen := FlowConnectionGenerationConfig{
962+
FlowJobName: s.attachSuffix(srcTableName),
963+
TableNameMapping: map[string]string{srcFullName: dstTableName},
964+
Destination: s.Peer().Name,
965+
}
966+
flowConnConfig := connectionGen.GenerateFlowConnectionConfigs(s)
967+
flowConnConfig.DoInitialSnapshot = true
968+
969+
tc := NewTemporalClient(s.t)
970+
env := ExecutePeerflow(s.t, tc, flowConnConfig)
971+
SetupCDCFlowStatusQuery(s.t, env, flowConnConfig)
972+
973+
cols := []struct{ name, def, val string }{
974+
// Integer widths: the signed minimum and unsigned maximum pin width + signedness.
975+
{"c_tinyint", "TINYINT", "-128"},
976+
{"c_tinyint_u", "TINYINT UNSIGNED", "255"},
977+
{"c_bool", "TINYINT(1)", "1"},
978+
{"c_smallint", "SMALLINT", "-32768"},
979+
{"c_smallint_u", "SMALLINT UNSIGNED", "65535"},
980+
{"c_mediumint", "MEDIUMINT", "-8388608"},
981+
{"c_mediumint_u", "MEDIUMINT UNSIGNED", "16777215"},
982+
{"c_int", "INT", "-2147483648"},
983+
{"c_int_u", "INT UNSIGNED", "4294967295"},
984+
{"c_bigint", "BIGINT", "-9223372036854775808"},
985+
{"c_bigint_u", "BIGINT UNSIGNED", "18446744073709551615"},
986+
{"c_integer", "INTEGER", "7"}, // synonym; normalizes to int through the parser
987+
// Approximate + exact numeric.
988+
{"c_float", "FLOAT", "1.5"},
989+
{"c_double", "DOUBLE PRECISION", "2.5"}, // synonym; normalizes to double
990+
{"c_decimal", "DECIMAL(10,2)", "123.45"},
991+
{"c_decimal_big", "DECIMAL(60,3)", "780780780.780"},
992+
// Bit.
993+
{"c_bit", "BIT(20)", "b'11100011100011100011'"},
994+
// Date and time.
995+
{"c_date", "DATE", "'2020-01-02'"},
996+
{"c_datetime", "DATETIME(3)", "'2020-01-02 03:04:05.678'"},
997+
{"c_timestamp", "TIMESTAMP(6)", "'2020-01-02 03:04:05.678901'"},
998+
{"c_time", "TIME", "'13:14:15'"},
999+
{"c_year", "YEAR", "2021"},
1000+
// Strings.
1001+
{"c_char", "CHAR(10)", "'abc'"},
1002+
{"c_varchar", "VARCHAR(20)", "'hello world'"},
1003+
{"c_text", "TEXT", "'some text'"},
1004+
{"c_enum", "ENUM('a','b','c')", "'b'"},
1005+
{"c_set", "SET('x','y','z')", "'x,z'"},
1006+
// Binary. BINARY(4) holds exactly 4 bytes, so there is no trailing-zero padding to
1007+
// reconcile (that case has its own test, Test_MySQL_Binary_Trailing_Zeros).
1008+
{"c_binary", "BINARY(4)", "'abcd'"},
1009+
{"c_varbinary", "VARBINARY(10)", "'binblob'"},
1010+
{"c_blob", "BLOB", "'someblob'"},
1011+
// JSON.
1012+
{"c_json", "JSON", `'{"a":2}'`},
1013+
}
1014+
1015+
adds := make([]string, len(cols))
1016+
names := make([]string, 0, len(cols)+1)
1017+
vals := make([]string, len(cols))
1018+
names = append(names, "id")
1019+
for i, c := range cols {
1020+
adds[i] = "ADD COLUMN " + c.name + " " + c.def
1021+
names = append(names, c.name)
1022+
vals[i] = c.val
1023+
}
1024+
1025+
// Single multi-column ALTER: processAlterTableQuery iterates every ADD COLUMN spec, so this
1026+
// exercises the DDL parse for all types at once.
1027+
require.NoError(s.t, s.source.Exec(s.t.Context(),
1028+
fmt.Sprintf("ALTER TABLE %s %s", srcFullName, strings.Join(adds, ", "))))
1029+
1030+
require.NoError(s.t, s.source.Exec(s.t.Context(),
1031+
fmt.Sprintf("INSERT INTO %s (%s) VALUES (1, %s)", srcFullName, strings.Join(names, ", "), strings.Join(vals, ", "))))
1032+
1033+
EnvWaitForEqualTablesWithNames(env, s, "waiting on cdc add column", srcTableName, dstTableName, strings.Join(names, ","))
1034+
1035+
env.Cancel(s.t.Context())
1036+
RequireEnvCanceled(s.t, env)
1037+
}
1038+
9301039
func (s ClickHouseSuite) Test_MySQL_Numbers() {
9311040
if mysource, ok := s.source.(*MySqlSource); !ok || mysource.Config.Flavor != protos.MySqlFlavor_MYSQL_MYSQL {
9321041
s.t.Skip("only applies to mysql")

0 commit comments

Comments
 (0)