github.com/XiaoMi/Gaea@v1.2.5/mysql/field.go (about) 1 // Copyright 2016 The kingshard Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // 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, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 // Copyright 2019 The Gaea Authors. All Rights Reserved. 16 // 17 // Licensed under the Apache License, Version 2.0 (the "License"); 18 // you may not use this file except in compliance with the License. 19 // You may obtain a copy of the License at 20 // 21 // http://www.apache.org/licenses/LICENSE-2.0 22 // 23 // Unless required by applicable law or agreed to in writing, software 24 // distributed under the License is distributed on an "AS IS" BASIS, 25 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 26 // See the License for the specific language governing permissions and 27 // limitations under the License. 28 29 package mysql 30 31 import ( 32 "encoding/binary" 33 "errors" 34 "fmt" 35 "strconv" 36 "strings" 37 "time" 38 39 "github.com/XiaoMi/Gaea/util/hack" 40 ) 41 42 // FieldData means filed data, is []byte 43 type FieldData []byte 44 45 // Field to represent column field 46 type Field struct { 47 Data FieldData 48 Schema []byte 49 Table []byte 50 OrgTable []byte 51 Name []byte 52 OrgName []byte 53 Charset uint16 54 ColumnLength uint32 55 Type uint8 56 Flag uint16 57 Decimal uint8 58 59 DefaultValueLength uint64 60 DefaultValue []byte 61 } 62 63 // TimeValue mysql time value 64 type TimeValue struct { 65 IsNegative bool 66 Day int 67 Hour int 68 Minute int 69 Second int 70 Microsecond int 71 } 72 73 // IsNull check TimeValue if null 74 func (m *TimeValue) IsNull() bool { 75 return m.Day == 0 && m.Hour == 0 && m.Minute == 0 && m.Second == 0 && m.Microsecond == 0 76 } 77 78 // Parse parse []byte to Field 79 func (p FieldData) Parse() (f *Field, err error) { 80 f = new(Field) 81 82 data := make([]byte, len(p)) 83 copy(data, p) 84 f.Data = data 85 86 pos := 0 87 ok := false 88 //skip catelog, always def 89 pos, ok = skipLenEncString(p, pos) 90 if !ok { 91 return f, errors.New("skipLenEncString in Parse failed") 92 } 93 94 //schema 95 f.Schema, pos, _, ok = ReadLenEncStringAsBytes(p, pos) 96 if !ok { 97 return f, errors.New("read Schema failed") 98 } 99 100 //table 101 f.Table, pos, _, ok = ReadLenEncStringAsBytes(p, pos) 102 if !ok { 103 return f, errors.New("read Table failed") 104 } 105 106 //org_table 107 f.OrgTable, pos, _, ok = ReadLenEncStringAsBytes(p, pos) 108 if !ok { 109 return f, errors.New("read OrgTable failed") 110 } 111 112 //name 113 f.Name, pos, _, ok = ReadLenEncStringAsBytes(p, pos) 114 if !ok { 115 return f, errors.New("read Name failed") 116 } 117 118 //org_name 119 f.OrgName, pos, _, ok = ReadLenEncStringAsBytes(p, pos) 120 if !ok { 121 return f, errors.New("read OrgName failed") 122 } 123 124 //skip oc 125 pos++ 126 127 //charset 128 f.Charset = binary.LittleEndian.Uint16(p[pos:]) 129 pos += 2 130 131 //column length 132 f.ColumnLength = binary.LittleEndian.Uint32(p[pos:]) 133 pos += 4 134 135 //type 136 f.Type = p[pos] 137 pos++ 138 139 //flag 140 f.Flag = binary.LittleEndian.Uint16(p[pos:]) 141 pos += 2 142 143 //decimals 1 144 f.Decimal = p[pos] 145 pos++ 146 147 //filter [0x00][0x00] 148 pos += 2 149 150 f.DefaultValue = nil 151 //if more data, command was field list 152 if len(p) > pos { 153 //length of default value lenenc-int 154 f.DefaultValueLength, pos, _, _ = ReadLenEncInt(p, pos) 155 156 if pos+int(f.DefaultValueLength) > len(p) { 157 err = ErrMalformPacket 158 return 159 } 160 161 //default value string[$len] 162 f.DefaultValue = p[pos:(pos + int(f.DefaultValueLength))] 163 } 164 165 return 166 } 167 168 // Dump dume field into binary []byte 169 func (f *Field) Dump() []byte { 170 if f.Data != nil { 171 return []byte(f.Data) 172 } 173 174 l := len(f.Schema) + len(f.Table) + len(f.OrgTable) + len(f.Name) + len(f.OrgName) + len(f.DefaultValue) + 48 175 176 data := make([]byte, 0, l) 177 178 data = AppendLenEncStringBytes(data, []byte("def")) 179 data = AppendLenEncStringBytes(data, f.Schema) 180 data = AppendLenEncStringBytes(data, f.Table) 181 data = AppendLenEncStringBytes(data, f.OrgTable) 182 data = AppendLenEncStringBytes(data, f.Name) 183 data = AppendLenEncStringBytes(data, f.OrgName) 184 185 data = append(data, 0x0c) 186 187 data = AppendUint16(data, f.Charset) 188 data = AppendUint32(data, f.ColumnLength) 189 data = append(data, f.Type) 190 data = AppendUint16(data, f.Flag) 191 data = append(data, f.Decimal) 192 data = append(data, 0, 0) 193 194 if f.DefaultValue != nil { 195 data = AppendUint64(data, f.DefaultValueLength) 196 data = append(data, f.DefaultValue...) 197 } 198 199 return data 200 } 201 202 // FieldType return type of field 203 func FieldType(value interface{}) (typ uint8, err error) { 204 switch value.(type) { 205 case int8, int16, int32, int64, int: 206 typ = TypeLonglong 207 case uint8, uint16, uint32, uint64, uint: 208 typ = TypeLonglong 209 case float32, float64: 210 typ = TypeDouble 211 case string, []byte: 212 typ = TypeVarString 213 case nil: 214 typ = TypeNull 215 default: 216 err = fmt.Errorf("unsupport type %T for resultset", value) 217 } 218 return 219 } 220 221 func stringToMysqlTime(s string) (TimeValue, error) { 222 var v TimeValue 223 224 timeFields := strings.SplitN(s, ":", 2) 225 if len(timeFields) != 2 { 226 return v, fmt.Errorf("invalid TypeDuration %s", s) 227 } 228 229 hour, err := strconv.ParseInt(timeFields[0], 10, 64) 230 if err != nil { 231 return v, fmt.Errorf("invalid TypeDuration %s", s) 232 } 233 234 if strings.HasPrefix(timeFields[0], "-") { 235 v.IsNegative = true 236 hour = hack.Abs(hour) 237 } 238 239 day := int(hour / 24) 240 hourRest := int(hour % 24) 241 242 timeRest := strconv.Itoa(hourRest) + ":" + timeFields[1] 243 ts, err := time.Parse("15:04:05", timeRest) 244 if err != nil { 245 return v, fmt.Errorf("invalid TypeDuration %s", s) 246 } 247 if ts.Nanosecond()%1000 != 0 { 248 return v, fmt.Errorf("invalid TypeDuration %s", s) 249 } 250 251 v.Day = day 252 v.Hour = ts.Hour() 253 v.Minute = ts.Minute() 254 v.Second = ts.Second() 255 v.Microsecond = ts.Nanosecond() / 1000 256 return v, nil 257 } 258 259 func mysqlTimeToBinaryResult(v TimeValue) []byte { 260 var t []byte 261 var length uint8 262 if v.IsNull() { 263 length = 0 264 t = append(t, length) 265 } else { 266 if v.Microsecond == 0 { 267 length = 8 268 } else { 269 length = 12 270 } 271 t = append(t, length) 272 if v.IsNegative { 273 t = append(t, 1) 274 } else { 275 t = append(t, 0) 276 } 277 t = AppendUint32(t, uint32(v.Day)) 278 t = append(t, uint8(v.Hour)) 279 t = append(t, uint8(v.Minute)) 280 t = append(t, uint8(v.Second)) 281 if v.Microsecond != 0 { 282 t = AppendUint32(t, uint32(v.Microsecond)) 283 } 284 } 285 return t 286 }