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  }