@@ -24,7 +24,7 @@ import (
2424 "github.com/PeerDB-io/peerdb/flow/shared/types"
2525)
2626
27- func mysqlEnumUsesOrdinals (source * MySqlSource ) bool {
27+ func mysqlUsesOrdinals (source * MySqlSource ) bool {
2828 return source .Config .Flavor == protos .MySqlFlavor_MYSQL_MYSQL &&
2929 source .Config .ReplicationMechanism == protos .MySqlReplicationMechanism_MYSQL_FILEPOS
3030}
@@ -794,26 +794,30 @@ func (s ClickHouseSuite) Test_MySQL_Enum() {
794794 RequireEnvCanceled (s .t , env )
795795}
796796
797- func (s ClickHouseSuite ) Test_MySQL_Enum_Consistency () {
797+ // Test_MySQL_Enum_Set_Consistency verifies that ENUM and SET columns produce identical values
798+ // via snapshot and CDC. On servers without binlog row metadata, both are emitted as integers
799+ // (enum ordinal, set bitmask); otherwise as labels.
800+ func (s ClickHouseSuite ) Test_MySQL_Enum_Set_Consistency () {
798801 mySource , ok := s .source .(* MySqlSource )
799802 if ! ok {
800803 s .t .Skip ("only applies to mysql" )
801804 }
802805
803- srcTableName := "test_my_enum_consistency "
806+ srcTableName := "test_my_enum_set_consistency "
804807 srcFullName := s .attachSchemaSuffix (srcTableName )
805- dstTableName := "test_my_enum_consistency_dst "
808+ dstTableName := "test_my_enum_set_consistency_dst "
806809
807810 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (`
808811 CREATE TABLE IF NOT EXISTS %s (
809812 id SERIAL PRIMARY KEY,
810- status ENUM('active', 'inactive', 'pending') NOT NULL
813+ status ENUM('active', 'inactive', 'pending') NOT NULL,
814+ tags SET('a', 'b', 'c') NOT NULL
811815 )
812816 ` , srcFullName )))
813817
814818 // Insert row before snapshot
815819 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (
816- `INSERT INTO %s (status) VALUES ('active')` , srcFullName )))
820+ `INSERT INTO %s (status, tags ) VALUES ('active', 'a,b ')` , srcFullName )))
817821
818822 connectionGen := FlowConnectionGenerationConfig {
819823 FlowJobName : s .attachSuffix (srcTableName ),
@@ -828,50 +832,58 @@ func (s ClickHouseSuite) Test_MySQL_Enum_Consistency() {
828832 SetupCDCFlowStatusQuery (s .t , env , flowConnConfig )
829833
830834 // Wait for snapshot row to appear in destination
831- EnvWaitForCount (env , s , "waiting on snapshot" , dstTableName , "id,status" , 1 )
835+ EnvWaitForCount (env , s , "waiting on snapshot" , dstTableName , "id,status,tags " , 1 )
832836
833837 // Insert row via CDC — on old MySQL this comes as integer from binlog
834838 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (
835- `INSERT INTO %s (status) VALUES ('active')` , srcFullName )))
839+ `INSERT INTO %s (status, tags ) VALUES ('active', 'a,b ')` , srcFullName )))
836840
837841 // Wait for CDC row
838- EnvWaitForCount (env , s , "waiting on cdc" , dstTableName , "id,status" , 2 )
842+ EnvWaitForCount (env , s , "waiting on cdc" , dstTableName , "id,status,tags " , 2 )
839843
840- // Verify both rows have the same status value (consistency between snapshot and CDC)
841- rows , err := s .GetRows (dstTableName , "id,status" )
844+ // Verify snapshot and CDC produce the same values for both columns
845+ rows , err := s .GetRows (dstTableName , "id,status,tags " )
842846 require .NoError (s .t , err )
843847 require .Len (s .t , rows .Records , 2 )
844848 require .Equal (s .t , rows .Records [0 ][1 ].Value (), rows .Records [1 ][1 ].Value (),
845849 "snapshot and CDC enum values should be consistent" )
846- if mysqlEnumUsesOrdinals (mySource ) {
850+ require .Equal (s .t , rows .Records [0 ][2 ].Value (), rows .Records [1 ][2 ].Value (),
851+ "snapshot and CDC set values should be consistent" )
852+ if mysqlUsesOrdinals (mySource ) {
847853 require .EqualValues (s .t , 1 , rows .Records [0 ][1 ].Value ())
854+ require .EqualValues (s .t , 3 , rows .Records [0 ][2 ].Value ()) // 'a,b' bitmask = 1|2
848855 } else {
849856 require .Equal (s .t , "active" , rows .Records [0 ][1 ].Value ())
857+ require .Equal (s .t , "a,b" , rows .Records [0 ][2 ].Value ())
850858 }
851859
852860 env .Cancel (s .t .Context ())
853861 RequireEnvCanceled (s .t , env )
854862}
855863
856- func (s ClickHouseSuite ) Test_MySQL_Enum_Consistency_Version0 () {
864+ // Test_MySQL_Enum_Set_Consistency_Version0 documents the legacy divergence: on pre-metadata
865+ // servers, mirrors created before the enum/set-to-int versions emit labels via snapshot but
866+ // ordinals/bitmasks via CDC. Existing mirrors deliberately keep this behavior for stability.
867+ func (s ClickHouseSuite ) Test_MySQL_Enum_Set_Consistency_Version0 () {
857868 mySource , ok := s .source .(* MySqlSource )
858869 if ! ok {
859870 s .t .Skip ("only applies to mysql" )
860871 }
861872
862- srcTableName := "test_my_enum_consistency_v0 "
873+ srcTableName := "test_my_enum_set_consistency_v0 "
863874 srcFullName := s .attachSchemaSuffix (srcTableName )
864- dstTableName := "test_my_enum_consistency_v0_dst "
875+ dstTableName := "test_my_enum_set_consistency_v0_dst "
865876
866877 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (`
867878 CREATE TABLE IF NOT EXISTS %s (
868879 id SERIAL PRIMARY KEY,
869- status ENUM('active', 'inactive', 'pending') NOT NULL
880+ status ENUM('active', 'inactive', 'pending') NOT NULL,
881+ tags SET('a', 'b', 'c') NOT NULL
870882 )
871883 ` , srcFullName )))
872884
873885 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (
874- `INSERT INTO %s (status) VALUES ('active')` , srcFullName )))
886+ `INSERT INTO %s (status, tags ) VALUES ('active', 'a,b ')` , srcFullName )))
875887
876888 connectionGen := FlowConnectionGenerationConfig {
877889 FlowJobName : s .attachSuffix (srcTableName ),
@@ -887,23 +899,26 @@ func (s ClickHouseSuite) Test_MySQL_Enum_Consistency_Version0() {
887899 env := ExecutePeerflow (s .t , tc , flowConnConfig )
888900 SetupCDCFlowStatusQuery (s .t , env , flowConnConfig )
889901
890- EnvWaitForCount (env , s , "waiting on snapshot" , dstTableName , "id,status" , 1 )
902+ EnvWaitForCount (env , s , "waiting on snapshot" , dstTableName , "id,status,tags " , 1 )
891903
892904 require .NoError (s .t , s .source .Exec (s .t .Context (), fmt .Sprintf (
893- `INSERT INTO %s (status) VALUES ('active')` , srcFullName )))
905+ `INSERT INTO %s (status, tags ) VALUES ('active', 'a,b ')` , srcFullName )))
894906
895- EnvWaitForCount (env , s , "waiting on cdc" , dstTableName , "id,status" , 2 )
907+ EnvWaitForCount (env , s , "waiting on cdc" , dstTableName , "id,status,tags " , 2 )
896908
897- rows , err := s .GetRows (dstTableName , "id,status" )
909+ rows , err := s .GetRows (dstTableName , "id,status,tags " )
898910 require .NoError (s .t , err )
899911 require .Len (s .t , rows .Records , 2 )
900912 require .EqualValues (s .t , 1 , rows .Records [0 ][0 ].Value ())
901913 require .EqualValues (s .t , 2 , rows .Records [1 ][0 ].Value ())
902914 require .Equal (s .t , "active" , rows .Records [0 ][1 ].Value ())
903- if mysqlEnumUsesOrdinals (mySource ) {
915+ require .Equal (s .t , "a,b" , rows .Records [0 ][2 ].Value ())
916+ if mysqlUsesOrdinals (mySource ) {
904917 require .Equal (s .t , "1" , rows .Records [1 ][1 ].Value ())
918+ require .Equal (s .t , "3" , rows .Records [1 ][2 ].Value ())
905919 } else {
906920 require .Equal (s .t , "active" , rows .Records [1 ][1 ].Value ())
921+ require .Equal (s .t , "a,b" , rows .Records [1 ][2 ].Value ())
907922 }
908923
909924 env .Cancel (s .t .Context ())
0 commit comments