github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/pkg/sink/codec/internal/column.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 internal 15 16 import ( 17 "encoding/base64" 18 "encoding/json" 19 "strconv" 20 21 "github.com/pingcap/log" 22 "github.com/pingcap/tidb/pkg/parser/mysql" 23 "github.com/pingcap/tiflow/cdc/model" 24 "go.uber.org/zap" 25 ) 26 27 // Column is a type only used in codec internally. 28 type Column struct { 29 Type byte `json:"t"` 30 // Deprecated: please use Flag instead. 31 WhereHandle *bool `json:"h,omitempty"` 32 Flag model.ColumnFlagType `json:"f"` 33 Value any `json:"v"` 34 } 35 36 // FromRowChangeColumn converts from a row changed column to a codec column. 37 func (c *Column) FromRowChangeColumn(col *model.Column) { 38 c.Type = col.Type 39 c.Flag = col.Flag 40 if c.Flag.IsHandleKey() { 41 whereHandle := true 42 c.WhereHandle = &whereHandle 43 } 44 if col.Value == nil { 45 c.Value = nil 46 return 47 } 48 switch col.Type { 49 case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar: 50 var str string 51 switch col.Value.(type) { 52 case []byte: 53 str = string(col.Value.([]byte)) 54 case string: 55 str = col.Value.(string) 56 default: 57 log.Panic("invalid column value, please report a bug", zap.Any("col", col)) 58 } 59 if c.Flag.IsBinary() { 60 str = strconv.Quote(str) 61 str = str[1 : len(str)-1] 62 } 63 c.Value = str 64 default: 65 c.Value = col.Value 66 } 67 } 68 69 // ToRowChangeColumn converts from a codec column to a row changed column. 70 func (c *Column) ToRowChangeColumn(name string) *model.Column { 71 col := new(model.Column) 72 col.Type = c.Type 73 col.Flag = c.Flag 74 col.Name = name 75 col.Value = c.Value 76 if c.Value == nil { 77 return col 78 } 79 switch col.Type { 80 case mysql.TypeString, mysql.TypeVarString, mysql.TypeVarchar: 81 str := col.Value.(string) 82 var err error 83 if c.Flag.IsBinary() { 84 str, err = strconv.Unquote("\"" + str + "\"") 85 if err != nil { 86 log.Panic("invalid column value, please report a bug", zap.Any("col", c), zap.Error(err)) 87 } 88 } 89 col.Value = []byte(str) 90 case mysql.TypeFloat: 91 col.Value = float32(col.Value.(float64)) 92 case mysql.TypeYear: 93 col.Value = int64(col.Value.(uint64)) 94 case mysql.TypeEnum, mysql.TypeSet: 95 val, err := col.Value.(json.Number).Int64() 96 if err != nil { 97 log.Panic("invalid column value for enum, please report a bug", 98 zap.Any("col", c), zap.Error(err)) 99 } 100 col.Value = uint64(val) 101 default: 102 col.Value = c.Value 103 } 104 return col 105 } 106 107 // FormatColumn formats a codec column. 108 func FormatColumn(c Column) Column { 109 switch c.Type { 110 case mysql.TypeTinyBlob, mysql.TypeMediumBlob, 111 mysql.TypeLongBlob, mysql.TypeBlob: 112 if s, ok := c.Value.(string); ok { 113 var err error 114 c.Value, err = base64.StdEncoding.DecodeString(s) 115 if err != nil { 116 log.Panic("invalid column value, please report a bug", zap.Any("col", c), zap.Error(err)) 117 } 118 } 119 case mysql.TypeFloat, mysql.TypeDouble: 120 if s, ok := c.Value.(json.Number); ok { 121 f64, err := s.Float64() 122 if err != nil { 123 log.Panic("invalid column value, please report a bug", zap.Any("col", c), zap.Error(err)) 124 } 125 c.Value = f64 126 } 127 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeInt24, mysql.TypeYear: 128 if s, ok := c.Value.(json.Number); ok { 129 var err error 130 if c.Flag.IsUnsigned() { 131 c.Value, err = strconv.ParseUint(s.String(), 10, 64) 132 } else { 133 c.Value, err = strconv.ParseInt(s.String(), 10, 64) 134 } 135 if err != nil { 136 log.Panic("invalid column value, please report a bug", zap.Any("col", c), zap.Error(err)) 137 } 138 } else if f, ok := c.Value.(float64); ok { 139 if c.Flag.IsUnsigned() { 140 c.Value = uint64(f) 141 } else { 142 c.Value = int64(f) 143 } 144 } 145 case mysql.TypeBit: 146 if s, ok := c.Value.(json.Number); ok { 147 intNum, err := s.Int64() 148 if err != nil { 149 log.Panic("invalid column value, please report a bug", zap.Any("col", c), zap.Error(err)) 150 } 151 c.Value = uint64(intNum) 152 } 153 } 154 return c 155 }