Skip to content

Commit 8514199

Browse files
authored
Decode BINARY(N) as Bytes on the binlog-metadata add-column path (#4457)
Resolves DBI-856
1 parent 7f72af5 commit 8514199

2 files changed

Lines changed: 51 additions & 5 deletions

File tree

flow/connectors/mysql/qvalue_convert.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ func qkindFromMysqlType(mytype byte, unsigned bool, charset uint16, version uint
8686
return types.QValueKindString, nil
8787
}
8888
case mysql.MYSQL_TYPE_VAR_STRING, mysql.MYSQL_TYPE_STRING, mysql.MYSQL_TYPE_VARCHAR:
89+
if charset == 0x3f {
90+
return types.QValueKindBytes, nil
91+
}
8992
return types.QValueKindString, nil
9093
case mysql.MYSQL_TYPE_GEOMETRY:
9194
return types.QValueKindGeometry, nil

flow/e2e/clickhouse_mysql_test.go

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,10 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
11481148
t.Skip("skipping test because destination connector does not implement GetTableSchemaConnector")
11491149
}
11501150

1151+
fixedBinaryInsertHex := "FF000102030405060708090A0B0C"
1152+
fixedBinaryWantHex := fixedBinaryInsertHex + "0000"
1153+
varBinaryWantHex := "FE000102030405060708090A0B0C0D00"
1154+
11511155
srcTable := "test_mysql_ghost_schema"
11521156
dstTable := "test_mysql_ghost_schema_dst"
11531157
srcTableName := AttachSchema(s, srcTable)
@@ -1222,6 +1226,8 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
12221226
// - c6: DECIMAL (no precision/scale -> typmod -1)
12231227
// - c7: DECIMAL(10,2) (tests precision/scale extraction)
12241228
// - c8: DECIMAL(18,6) (tests different precision/scale)
1229+
// - c9: BINARY(16) (tests binary charset on STRING metadata type)
1230+
// - c10: VARBINARY(16) (tests binary charset on VAR_STRING metadata type)
12251231
// ============================================
12261232

12271233
// 1. gh-ost creates ghost table with new schema (original + new columns)
@@ -1235,20 +1241,23 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
12351241
c5 TEXT,
12361242
c6 DECIMAL,
12371243
c7 DECIMAL(10,2),
1238-
c8 DECIMAL(18,6)
1244+
c8 DECIMAL(18,6),
1245+
c9 BINARY(16),
1246+
c10 VARBINARY(16)
12391247
)
12401248
`, ghostTableName)))
12411249

12421250
// 2. gh-ost copies existing data to ghost table (we simulate this)
12431251
EnvNoError(t, env, s.Source().Exec(t.Context(), fmt.Sprintf(`
1244-
INSERT INTO %s (id, c1, c2, c3, c4, c5, c6, c7, c8) SELECT id, c1, NULL, NULL, NULL, NULL, NULL, NULL, NULL FROM %s
1252+
INSERT INTO %s (id, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)
1253+
SELECT id, c1, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL FROM %s
12451254
`, ghostTableName, srcTableName)))
12461255

12471256
// 3. Insert another row into original table (gh-ost would capture this via binlog and apply to ghost)
12481257
EnvNoError(t, env, s.Source().Exec(t.Context(), fmt.Sprintf(`INSERT INTO %s(c1) VALUES(2)`, srcTableName)))
12491258
// Simulate gh-ost applying it to ghost table
12501259
EnvNoError(t, env, s.Source().Exec(t.Context(), fmt.Sprintf(
1251-
`INSERT INTO %s(c1, c2, c3, c4, c5, c6, c7, c8) VALUES(2, NULL, NULL, NULL, NULL, NULL, NULL, NULL)`,
1260+
`INSERT INTO %s(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10) VALUES(2, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL)`,
12521261
ghostTableName)))
12531262
EnvWaitForEqualTablesWithNames(env, s, "pre-cutover row", srcTable, dstTable, "id,c1")
12541263

@@ -1259,8 +1268,9 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
12591268

12601269
// 5. Insert a row with the new columns populated (this goes to the new table, formerly ghost)
12611270
EnvNoError(t, env, s.Source().Exec(t.Context(), fmt.Sprintf(
1262-
`INSERT INTO %s(c1, c2, c3, c4, c5, c6, c7, c8) VALUES(3, 300, 400, x'deadbeef', 'hello text', 123.45, 12345.67, 123456.789012)`,
1263-
srcTableName)))
1271+
`INSERT INTO %s(c1, c2, c3, c4, c5, c6, c7, c8, c9, c10)
1272+
VALUES(3, 300, 400, x'deadbeef', 'hello text', 123.45, 12345.67, 123456.789012, UNHEX('%s'), UNHEX('%s'))`,
1273+
srcTableName, fixedBinaryInsertHex, varBinaryWantHex)))
12641274
EnvWaitForEqualTablesWithNames(env, s, "post-cutover row", srcTable, dstTable,
12651275
"id,c1,coalesce(c2,0) c2,coalesce(c4,'') c4,coalesce(c5,'') c5,coalesce(c6,0) c6,coalesce(c7,0) c7,coalesce(c8,0) c8")
12661276

@@ -1270,6 +1280,29 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
12701280
require.Len(t, dstRows.Records, 3)
12711281
require.Equal(t, uint32(400), dstRows.Records[2][1].Value())
12721282

1283+
dstRows, err = s.GetRows(dstTable, "id,c9,c10")
1284+
require.NoError(t, err)
1285+
require.Len(t, dstRows.Records, 3)
1286+
1287+
qvalueBytes := func(qv types.QValue) []byte {
1288+
switch v := qv.Value().(type) {
1289+
case string:
1290+
return []byte(v)
1291+
case []byte:
1292+
return v
1293+
default:
1294+
require.Failf(t, "unexpected binary column type", "type=%T value=%v", qv.Value(), qv.Value())
1295+
return nil
1296+
}
1297+
}
1298+
1299+
fixedBinaryWant, err := hex.DecodeString(fixedBinaryWantHex)
1300+
require.NoError(t, err)
1301+
varBinaryWant, err := hex.DecodeString(varBinaryWantHex)
1302+
require.NoError(t, err)
1303+
require.Equal(t, fixedBinaryWant, qvalueBytes(dstRows.Records[2][1]))
1304+
require.Equal(t, varBinaryWant, qvalueBytes(dstRows.Records[2][2]))
1305+
12731306
// Verify schema was updated to include new columns with correct types and typmods
12741307
expectedTableSchema = &protos.TableSchema{
12751308
TableIdentifier: ExpectedDestinationTableName(s, dstTable),
@@ -1324,6 +1357,16 @@ func (s ClickHouseSuite) Test_MySQL_GhOst_Schema_Changes() {
13241357
Type: string(types.QValueKindNumeric), // DECIMAL(18,6)
13251358
TypeModifier: datatypes.MakeNumericTypmod(18, 6),
13261359
},
1360+
{
1361+
Name: ExpectedDestinationIdentifier(s, "c9"),
1362+
Type: string(types.QValueKindBytes), // BINARY(16)
1363+
TypeModifier: -1,
1364+
},
1365+
{
1366+
Name: ExpectedDestinationIdentifier(s, "c10"),
1367+
Type: string(types.QValueKindBytes), // VARBINARY(16)
1368+
TypeModifier: -1,
1369+
},
13271370
},
13281371
}
13291372
output, err = destinationSchemaConnector.GetTableSchema(t.Context(), nil, shared.InternalVersion_Latest, protos.TypeSystem_Q,

0 commit comments

Comments
 (0)