@@ -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