1577 lines
39 KiB
Go
1577 lines
39 KiB
Go
// Copyright 2016 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package types
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"sort"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/juju/errors"
|
|
"github.com/pingcap/tidb/mysql"
|
|
"github.com/pingcap/tidb/sessionctx/variable"
|
|
"github.com/pingcap/tidb/util/charset"
|
|
"github.com/pingcap/tidb/util/hack"
|
|
)
|
|
|
|
// Kind constants.
|
|
const (
|
|
KindNull byte = 0
|
|
KindInt64 byte = iota + 1
|
|
KindUint64
|
|
KindFloat32
|
|
KindFloat64
|
|
KindString
|
|
KindBytes
|
|
KindMysqlBit
|
|
KindMysqlDecimal
|
|
KindMysqlDuration
|
|
KindMysqlEnum
|
|
KindMysqlHex
|
|
KindMysqlSet
|
|
KindMysqlTime
|
|
KindRow
|
|
KindInterface
|
|
KindMinNotNull
|
|
KindMaxValue
|
|
)
|
|
|
|
// Datum is a data box holds different kind of data.
|
|
// It has better performance and is easier to use than `interface{}`.
|
|
type Datum struct {
|
|
k byte // datum kind.
|
|
collation uint8 // collation can hold uint8 values.
|
|
decimal uint16 // decimal can hold uint16 values.
|
|
length uint32 // length can hold uint32 values.
|
|
i int64 // i can hold int64 uint64 float64 values.
|
|
b []byte // b can hold string or []byte values.
|
|
x interface{} // f hold all other types.
|
|
}
|
|
|
|
// Kind gets the kind of the datum.
|
|
func (d *Datum) Kind() byte {
|
|
return d.k
|
|
}
|
|
|
|
// Collation gets the collation of the datum.
|
|
func (d *Datum) Collation() byte {
|
|
return d.collation
|
|
}
|
|
|
|
// SetCollation sets the collation of the datum.
|
|
func (d *Datum) SetCollation(collation byte) {
|
|
d.collation = collation
|
|
}
|
|
|
|
// Frac gets the frac of the datum.
|
|
func (d *Datum) Frac() int {
|
|
return int(d.decimal)
|
|
}
|
|
|
|
// SetFrac sets the frac of the datum.
|
|
func (d *Datum) SetFrac(frac int) {
|
|
d.decimal = uint16(frac)
|
|
}
|
|
|
|
// Length gets the length of the datum.
|
|
func (d *Datum) Length() int {
|
|
return int(d.length)
|
|
}
|
|
|
|
// SetLength sets the length of the datum
|
|
func (d *Datum) SetLength(l int) {
|
|
d.length = uint32(l)
|
|
}
|
|
|
|
// IsNull checks if datum is null.
|
|
func (d *Datum) IsNull() bool {
|
|
return d.k == KindNull
|
|
}
|
|
|
|
// GetInt64 gets int64 value.
|
|
func (d *Datum) GetInt64() int64 {
|
|
return d.i
|
|
}
|
|
|
|
// SetInt64 sets int64 value.
|
|
func (d *Datum) SetInt64(i int64) {
|
|
d.k = KindInt64
|
|
d.i = i
|
|
}
|
|
|
|
// GetUint64 gets uint64 value.
|
|
func (d *Datum) GetUint64() uint64 {
|
|
return uint64(d.i)
|
|
}
|
|
|
|
// SetUint64 sets uint64 value.
|
|
func (d *Datum) SetUint64(i uint64) {
|
|
d.k = KindUint64
|
|
d.i = int64(i)
|
|
}
|
|
|
|
// GetFloat64 gets float64 value.
|
|
func (d *Datum) GetFloat64() float64 {
|
|
return math.Float64frombits(uint64(d.i))
|
|
}
|
|
|
|
// SetFloat64 sets float64 value.
|
|
func (d *Datum) SetFloat64(f float64) {
|
|
d.k = KindFloat64
|
|
d.i = int64(math.Float64bits(f))
|
|
}
|
|
|
|
// GetFloat32 gets float32 value.
|
|
func (d *Datum) GetFloat32() float32 {
|
|
return float32(math.Float64frombits(uint64(d.i)))
|
|
}
|
|
|
|
// SetFloat32 sets float32 value.
|
|
func (d *Datum) SetFloat32(f float32) {
|
|
d.k = KindFloat32
|
|
d.i = int64(math.Float64bits(float64(f)))
|
|
}
|
|
|
|
// GetString gets string value.
|
|
func (d *Datum) GetString() string {
|
|
return hack.String(d.b)
|
|
}
|
|
|
|
// SetString sets string value.
|
|
func (d *Datum) SetString(s string) {
|
|
d.k = KindString
|
|
sink(s)
|
|
d.b = hack.Slice(s)
|
|
}
|
|
|
|
// sink prevents s from being allocated on the stack.
|
|
var sink = func(s string) {
|
|
}
|
|
|
|
// GetBytes gets bytes value.
|
|
func (d *Datum) GetBytes() []byte {
|
|
return d.b
|
|
}
|
|
|
|
// SetBytes sets bytes value to datum.
|
|
func (d *Datum) SetBytes(b []byte) {
|
|
d.k = KindBytes
|
|
d.b = b
|
|
}
|
|
|
|
// SetBytesAsString sets bytes value to datum as string type.
|
|
func (d *Datum) SetBytesAsString(b []byte) {
|
|
d.k = KindString
|
|
d.b = b
|
|
}
|
|
|
|
// GetInterface gets interface value.
|
|
func (d *Datum) GetInterface() interface{} {
|
|
return d.x
|
|
}
|
|
|
|
// SetInterface sets interface to datum.
|
|
func (d *Datum) SetInterface(x interface{}) {
|
|
d.k = KindInterface
|
|
d.x = x
|
|
}
|
|
|
|
// GetRow gets row value.
|
|
func (d *Datum) GetRow() []Datum {
|
|
return d.x.([]Datum)
|
|
}
|
|
|
|
// SetRow sets row value.
|
|
func (d *Datum) SetRow(ds []Datum) {
|
|
d.k = KindRow
|
|
d.x = ds
|
|
}
|
|
|
|
// SetNull sets datum to nil.
|
|
func (d *Datum) SetNull() {
|
|
d.k = KindNull
|
|
d.x = nil
|
|
}
|
|
|
|
// GetMysqlBit gets Bit value
|
|
func (d *Datum) GetMysqlBit() Bit {
|
|
width := int(d.length)
|
|
value := uint64(d.i)
|
|
return Bit{Value: value, Width: width}
|
|
}
|
|
|
|
// SetMysqlBit sets Bit value
|
|
func (d *Datum) SetMysqlBit(b Bit) {
|
|
d.k = KindMysqlBit
|
|
d.length = uint32(b.Width)
|
|
d.i = int64(b.Value)
|
|
}
|
|
|
|
// GetMysqlDecimal gets Decimal value
|
|
func (d *Datum) GetMysqlDecimal() *MyDecimal {
|
|
return d.x.(*MyDecimal)
|
|
}
|
|
|
|
// SetMysqlDecimal sets Decimal value
|
|
func (d *Datum) SetMysqlDecimal(b *MyDecimal) {
|
|
d.k = KindMysqlDecimal
|
|
d.x = b
|
|
}
|
|
|
|
// GetMysqlDuration gets Duration value
|
|
func (d *Datum) GetMysqlDuration() Duration {
|
|
return Duration{Duration: time.Duration(d.i), Fsp: int(d.decimal)}
|
|
}
|
|
|
|
// SetMysqlDuration sets Duration value
|
|
func (d *Datum) SetMysqlDuration(b Duration) {
|
|
d.k = KindMysqlDuration
|
|
d.i = int64(b.Duration)
|
|
d.decimal = uint16(b.Fsp)
|
|
}
|
|
|
|
// GetMysqlEnum gets Enum value
|
|
func (d *Datum) GetMysqlEnum() Enum {
|
|
return Enum{Value: uint64(d.i), Name: hack.String(d.b)}
|
|
}
|
|
|
|
// SetMysqlEnum sets Enum value
|
|
func (d *Datum) SetMysqlEnum(b Enum) {
|
|
d.k = KindMysqlEnum
|
|
d.i = int64(b.Value)
|
|
sink(b.Name)
|
|
d.b = hack.Slice(b.Name)
|
|
}
|
|
|
|
// GetMysqlHex gets Hex value
|
|
func (d *Datum) GetMysqlHex() Hex {
|
|
return Hex{Value: d.i}
|
|
}
|
|
|
|
// SetMysqlHex sets Hex value
|
|
func (d *Datum) SetMysqlHex(b Hex) {
|
|
d.k = KindMysqlHex
|
|
d.i = b.Value
|
|
}
|
|
|
|
// GetMysqlSet gets Set value
|
|
func (d *Datum) GetMysqlSet() Set {
|
|
return Set{Value: uint64(d.i), Name: hack.String(d.b)}
|
|
}
|
|
|
|
// SetMysqlSet sets Set value
|
|
func (d *Datum) SetMysqlSet(b Set) {
|
|
d.k = KindMysqlSet
|
|
d.i = int64(b.Value)
|
|
sink(b.Name)
|
|
d.b = hack.Slice(b.Name)
|
|
}
|
|
|
|
// GetMysqlTime gets types.Time value
|
|
func (d *Datum) GetMysqlTime() Time {
|
|
return d.x.(Time)
|
|
}
|
|
|
|
// SetMysqlTime sets types.Time value
|
|
func (d *Datum) SetMysqlTime(b Time) {
|
|
d.k = KindMysqlTime
|
|
d.x = b
|
|
}
|
|
|
|
// GetValue gets the value of the datum of any kind.
|
|
func (d *Datum) GetValue() interface{} {
|
|
switch d.k {
|
|
case KindInt64:
|
|
return d.GetInt64()
|
|
case KindUint64:
|
|
return d.GetUint64()
|
|
case KindFloat32:
|
|
return d.GetFloat32()
|
|
case KindFloat64:
|
|
return d.GetFloat64()
|
|
case KindString:
|
|
return d.GetString()
|
|
case KindBytes:
|
|
return d.GetBytes()
|
|
case KindMysqlBit:
|
|
return d.GetMysqlBit()
|
|
case KindMysqlDecimal:
|
|
return d.GetMysqlDecimal()
|
|
case KindMysqlDuration:
|
|
return d.GetMysqlDuration()
|
|
case KindMysqlEnum:
|
|
return d.GetMysqlEnum()
|
|
case KindMysqlHex:
|
|
return d.GetMysqlHex()
|
|
case KindMysqlSet:
|
|
return d.GetMysqlSet()
|
|
case KindMysqlTime:
|
|
return d.GetMysqlTime()
|
|
default:
|
|
return d.GetInterface()
|
|
}
|
|
}
|
|
|
|
// SetValue sets any kind of value.
|
|
func (d *Datum) SetValue(val interface{}) {
|
|
switch x := val.(type) {
|
|
case nil:
|
|
d.SetNull()
|
|
case bool:
|
|
if x {
|
|
d.SetInt64(1)
|
|
} else {
|
|
d.SetInt64(0)
|
|
}
|
|
case int:
|
|
d.SetInt64(int64(x))
|
|
case int64:
|
|
d.SetInt64(x)
|
|
case uint64:
|
|
d.SetUint64(x)
|
|
case float32:
|
|
d.SetFloat32(x)
|
|
case float64:
|
|
d.SetFloat64(x)
|
|
case string:
|
|
d.SetString(x)
|
|
case []byte:
|
|
d.SetBytes(x)
|
|
case Bit:
|
|
d.SetMysqlBit(x)
|
|
case *MyDecimal:
|
|
d.SetMysqlDecimal(x)
|
|
case Duration:
|
|
d.SetMysqlDuration(x)
|
|
case Enum:
|
|
d.SetMysqlEnum(x)
|
|
case Hex:
|
|
d.SetMysqlHex(x)
|
|
case Set:
|
|
d.SetMysqlSet(x)
|
|
case Time:
|
|
d.SetMysqlTime(x)
|
|
case []Datum:
|
|
d.SetRow(x)
|
|
case []interface{}:
|
|
ds := MakeDatums(x...)
|
|
d.SetRow(ds)
|
|
default:
|
|
d.SetInterface(x)
|
|
}
|
|
}
|
|
|
|
// CompareDatum compares datum to another datum.
|
|
// TODO: return error properly.
|
|
func (d *Datum) CompareDatum(sc *variable.StatementContext, ad Datum) (int, error) {
|
|
switch ad.k {
|
|
case KindNull:
|
|
if d.k == KindNull {
|
|
return 0, nil
|
|
}
|
|
return 1, nil
|
|
case KindMinNotNull:
|
|
if d.k == KindNull {
|
|
return -1, nil
|
|
} else if d.k == KindMinNotNull {
|
|
return 0, nil
|
|
}
|
|
return 1, nil
|
|
case KindMaxValue:
|
|
if d.k == KindMaxValue {
|
|
return 0, nil
|
|
}
|
|
return -1, nil
|
|
case KindInt64:
|
|
return d.compareInt64(sc, ad.GetInt64())
|
|
case KindUint64:
|
|
return d.compareUint64(sc, ad.GetUint64())
|
|
case KindFloat32, KindFloat64:
|
|
return d.compareFloat64(sc, ad.GetFloat64())
|
|
case KindString:
|
|
return d.compareString(sc, ad.GetString())
|
|
case KindBytes:
|
|
return d.compareBytes(sc, ad.GetBytes())
|
|
case KindMysqlBit:
|
|
return d.compareMysqlBit(sc, ad.GetMysqlBit())
|
|
case KindMysqlDecimal:
|
|
return d.compareMysqlDecimal(sc, ad.GetMysqlDecimal())
|
|
case KindMysqlDuration:
|
|
return d.compareMysqlDuration(sc, ad.GetMysqlDuration())
|
|
case KindMysqlEnum:
|
|
return d.compareMysqlEnum(sc, ad.GetMysqlEnum())
|
|
case KindMysqlHex:
|
|
return d.compareMysqlHex(sc, ad.GetMysqlHex())
|
|
case KindMysqlSet:
|
|
return d.compareMysqlSet(sc, ad.GetMysqlSet())
|
|
case KindMysqlTime:
|
|
return d.compareMysqlTime(sc, ad.GetMysqlTime())
|
|
case KindRow:
|
|
return d.compareRow(sc, ad.GetRow())
|
|
default:
|
|
return 0, nil
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareInt64(sc *variable.StatementContext, i int64) (int, error) {
|
|
switch d.k {
|
|
case KindMaxValue:
|
|
return 1, nil
|
|
case KindInt64:
|
|
return CompareInt64(d.i, i), nil
|
|
case KindUint64:
|
|
if i < 0 || d.GetUint64() > math.MaxInt64 {
|
|
return 1, nil
|
|
}
|
|
return CompareInt64(d.i, i), nil
|
|
default:
|
|
return d.compareFloat64(sc, float64(i))
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareUint64(sc *variable.StatementContext, u uint64) (int, error) {
|
|
switch d.k {
|
|
case KindMaxValue:
|
|
return 1, nil
|
|
case KindInt64:
|
|
if d.i < 0 || u > math.MaxInt64 {
|
|
return -1, nil
|
|
}
|
|
return CompareInt64(d.i, int64(u)), nil
|
|
case KindUint64:
|
|
return CompareUint64(d.GetUint64(), u), nil
|
|
default:
|
|
return d.compareFloat64(sc, float64(u))
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareFloat64(sc *variable.StatementContext, f float64) (int, error) {
|
|
switch d.k {
|
|
case KindNull, KindMinNotNull:
|
|
return -1, nil
|
|
case KindMaxValue:
|
|
return 1, nil
|
|
case KindInt64:
|
|
return CompareFloat64(float64(d.i), f), nil
|
|
case KindUint64:
|
|
return CompareFloat64(float64(d.GetUint64()), f), nil
|
|
case KindFloat32, KindFloat64:
|
|
return CompareFloat64(d.GetFloat64(), f), nil
|
|
case KindString, KindBytes:
|
|
fVal, err := StrToFloat(sc, d.GetString())
|
|
return CompareFloat64(fVal, f), err
|
|
case KindMysqlBit:
|
|
fVal := d.GetMysqlBit().ToNumber()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlDecimal:
|
|
fVal, _ := d.GetMysqlDecimal().ToFloat64()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlDuration:
|
|
fVal := d.GetMysqlDuration().Seconds()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlEnum:
|
|
fVal := d.GetMysqlEnum().ToNumber()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlHex:
|
|
fVal := d.GetMysqlHex().ToNumber()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlSet:
|
|
fVal := d.GetMysqlSet().ToNumber()
|
|
return CompareFloat64(fVal, f), nil
|
|
case KindMysqlTime:
|
|
fVal, _ := d.GetMysqlTime().ToNumber().ToFloat64()
|
|
return CompareFloat64(fVal, f), nil
|
|
default:
|
|
return -1, nil
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareString(sc *variable.StatementContext, s string) (int, error) {
|
|
switch d.k {
|
|
case KindNull, KindMinNotNull:
|
|
return -1, nil
|
|
case KindMaxValue:
|
|
return 1, nil
|
|
case KindString, KindBytes:
|
|
return CompareString(d.GetString(), s), nil
|
|
case KindMysqlDecimal:
|
|
dec := new(MyDecimal)
|
|
err := dec.FromString([]byte(s))
|
|
return d.GetMysqlDecimal().Compare(dec), err
|
|
case KindMysqlTime:
|
|
dt, err := ParseDatetime(s)
|
|
return d.GetMysqlTime().Compare(dt), err
|
|
case KindMysqlDuration:
|
|
dur, err := ParseDuration(s, MaxFsp)
|
|
return d.GetMysqlDuration().Compare(dur), err
|
|
case KindMysqlBit:
|
|
return CompareString(d.GetMysqlBit().ToString(), s), nil
|
|
case KindMysqlHex:
|
|
return CompareString(d.GetMysqlHex().ToString(), s), nil
|
|
case KindMysqlSet:
|
|
return CompareString(d.GetMysqlSet().String(), s), nil
|
|
case KindMysqlEnum:
|
|
return CompareString(d.GetMysqlEnum().String(), s), nil
|
|
default:
|
|
fVal, err := StrToFloat(sc, s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return d.compareFloat64(sc, fVal)
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareBytes(sc *variable.StatementContext, b []byte) (int, error) {
|
|
return d.compareString(sc, hack.String(b))
|
|
}
|
|
|
|
func (d *Datum) compareMysqlBit(sc *variable.StatementContext, bit Bit) (int, error) {
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
return CompareString(d.GetString(), bit.ToString()), nil
|
|
default:
|
|
return d.compareFloat64(sc, bit.ToNumber())
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlDecimal(sc *variable.StatementContext, dec *MyDecimal) (int, error) {
|
|
switch d.k {
|
|
case KindMysqlDecimal:
|
|
return d.GetMysqlDecimal().Compare(dec), nil
|
|
case KindString, KindBytes:
|
|
dDec := new(MyDecimal)
|
|
err := dDec.FromString(d.GetBytes())
|
|
return dDec.Compare(dec), err
|
|
default:
|
|
fVal, _ := dec.ToFloat64()
|
|
return d.compareFloat64(sc, fVal)
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlDuration(sc *variable.StatementContext, dur Duration) (int, error) {
|
|
switch d.k {
|
|
case KindMysqlDuration:
|
|
return d.GetMysqlDuration().Compare(dur), nil
|
|
case KindString, KindBytes:
|
|
dDur, err := ParseDuration(d.GetString(), MaxFsp)
|
|
return dDur.Compare(dur), err
|
|
default:
|
|
return d.compareFloat64(sc, dur.Seconds())
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlEnum(sc *variable.StatementContext, enum Enum) (int, error) {
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
return CompareString(d.GetString(), enum.String()), nil
|
|
default:
|
|
return d.compareFloat64(sc, enum.ToNumber())
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlHex(sc *variable.StatementContext, e Hex) (int, error) {
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
return CompareString(d.GetString(), e.ToString()), nil
|
|
default:
|
|
return d.compareFloat64(sc, e.ToNumber())
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlSet(sc *variable.StatementContext, set Set) (int, error) {
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
return CompareString(d.GetString(), set.String()), nil
|
|
default:
|
|
return d.compareFloat64(sc, set.ToNumber())
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareMysqlTime(sc *variable.StatementContext, time Time) (int, error) {
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
dt, err := ParseDatetime(d.GetString())
|
|
return dt.Compare(time), err
|
|
case KindMysqlTime:
|
|
return d.GetMysqlTime().Compare(time), nil
|
|
default:
|
|
fVal, _ := time.ToNumber().ToFloat64()
|
|
return d.compareFloat64(sc, fVal)
|
|
}
|
|
}
|
|
|
|
func (d *Datum) compareRow(sc *variable.StatementContext, row []Datum) (int, error) {
|
|
var dRow []Datum
|
|
if d.k == KindRow {
|
|
dRow = d.GetRow()
|
|
} else {
|
|
dRow = []Datum{*d}
|
|
}
|
|
for i := 0; i < len(row) && i < len(dRow); i++ {
|
|
cmp, err := dRow[i].CompareDatum(sc, row[i])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if cmp != 0 {
|
|
return cmp, nil
|
|
}
|
|
}
|
|
return CompareInt64(int64(len(dRow)), int64(len(row))), nil
|
|
}
|
|
|
|
// Cast casts datum to certain types.
|
|
func (d *Datum) Cast(sc *variable.StatementContext, target *FieldType) (ad Datum, err error) {
|
|
if !isCastType(target.Tp) {
|
|
return ad, errors.Errorf("unknown cast type - %v", target)
|
|
}
|
|
return d.ConvertTo(sc, target)
|
|
}
|
|
|
|
// ConvertTo converts a datum to the target field type.
|
|
func (d *Datum) ConvertTo(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
if d.k == KindNull {
|
|
return Datum{}, nil
|
|
}
|
|
switch target.Tp { // TODO: implement mysql types convert when "CAST() AS" syntax are supported.
|
|
case mysql.TypeTiny, mysql.TypeShort, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong:
|
|
unsigned := mysql.HasUnsignedFlag(target.Flag)
|
|
if unsigned {
|
|
return d.convertToUint(sc, target)
|
|
}
|
|
return d.convertToInt(sc, target)
|
|
case mysql.TypeFloat, mysql.TypeDouble:
|
|
return d.convertToFloat(sc, target)
|
|
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob,
|
|
mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString:
|
|
return d.convertToString(sc, target)
|
|
case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDate:
|
|
return d.convertToMysqlTime(sc, target)
|
|
case mysql.TypeDuration:
|
|
return d.convertToMysqlDuration(sc, target)
|
|
case mysql.TypeBit:
|
|
return d.convertToMysqlBit(sc, target)
|
|
case mysql.TypeDecimal, mysql.TypeNewDecimal:
|
|
return d.convertToMysqlDecimal(sc, target)
|
|
case mysql.TypeYear:
|
|
return d.convertToMysqlYear(sc, target)
|
|
case mysql.TypeEnum:
|
|
return d.convertToMysqlEnum(sc, target)
|
|
case mysql.TypeSet:
|
|
return d.convertToMysqlSet(sc, target)
|
|
case mysql.TypeNull:
|
|
return Datum{}, nil
|
|
default:
|
|
panic("should never happen")
|
|
}
|
|
}
|
|
|
|
func (d *Datum) convertToFloat(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var (
|
|
f float64
|
|
ret Datum
|
|
err error
|
|
)
|
|
switch d.k {
|
|
case KindNull:
|
|
return ret, nil
|
|
case KindInt64:
|
|
f = float64(d.GetInt64())
|
|
case KindUint64:
|
|
f = float64(d.GetUint64())
|
|
case KindFloat32, KindFloat64:
|
|
f = d.GetFloat64()
|
|
case KindString, KindBytes:
|
|
f, err = StrToFloat(sc, d.GetString())
|
|
case KindMysqlTime:
|
|
f, _ = d.GetMysqlTime().ToNumber().ToFloat64()
|
|
case KindMysqlDuration:
|
|
f, _ = d.GetMysqlDuration().ToNumber().ToFloat64()
|
|
case KindMysqlDecimal:
|
|
f, _ = d.GetMysqlDecimal().ToFloat64()
|
|
case KindMysqlHex:
|
|
f = d.GetMysqlHex().ToNumber()
|
|
case KindMysqlBit:
|
|
f = d.GetMysqlBit().ToNumber()
|
|
case KindMysqlSet:
|
|
f = d.GetMysqlSet().ToNumber()
|
|
case KindMysqlEnum:
|
|
f = d.GetMysqlEnum().ToNumber()
|
|
default:
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
// For float and following double type, we will only truncate it for float(M, D) format.
|
|
// If no D is set, we will handle it like origin float whether M is set or not.
|
|
if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength {
|
|
var err1 error
|
|
f, err1 = TruncateFloat(f, target.Flen, target.Decimal)
|
|
if err == nil {
|
|
err = err1
|
|
}
|
|
}
|
|
if target.Tp == mysql.TypeFloat {
|
|
ret.SetFloat32(float32(f))
|
|
} else {
|
|
ret.SetFloat64(f)
|
|
}
|
|
return ret, errors.Trace(err)
|
|
}
|
|
|
|
func (d *Datum) convertToString(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var ret Datum
|
|
var s string
|
|
switch d.k {
|
|
case KindInt64:
|
|
s = strconv.FormatInt(d.GetInt64(), 10)
|
|
case KindUint64:
|
|
s = strconv.FormatUint(d.GetUint64(), 10)
|
|
case KindFloat32:
|
|
s = strconv.FormatFloat(d.GetFloat64(), 'f', -1, 32)
|
|
case KindFloat64:
|
|
s = strconv.FormatFloat(d.GetFloat64(), 'f', -1, 64)
|
|
case KindString, KindBytes:
|
|
s = d.GetString()
|
|
case KindMysqlTime:
|
|
s = d.GetMysqlTime().String()
|
|
case KindMysqlDuration:
|
|
s = d.GetMysqlDuration().String()
|
|
case KindMysqlDecimal:
|
|
s = d.GetMysqlDecimal().String()
|
|
case KindMysqlHex:
|
|
s = d.GetMysqlHex().ToString()
|
|
case KindMysqlBit:
|
|
s = d.GetMysqlBit().ToString()
|
|
case KindMysqlEnum:
|
|
s = d.GetMysqlEnum().String()
|
|
case KindMysqlSet:
|
|
s = d.GetMysqlSet().String()
|
|
default:
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
|
|
var err error
|
|
if target.Flen >= 0 {
|
|
// Flen is the rune length, not binary length, for UTF8 charset, we need to calculate the
|
|
// rune count and truncate to Flen runes if it is too long.
|
|
if target.Charset == charset.CharsetUTF8 || target.Charset == charset.CharsetUTF8MB4 {
|
|
var runeCount int
|
|
var truncateLen int
|
|
for i := range s {
|
|
runeCount++
|
|
if runeCount == target.Flen+1 {
|
|
// We do break here because we need to iterate to the end to get runeCount.
|
|
truncateLen = i
|
|
}
|
|
}
|
|
if truncateLen > 0 {
|
|
err = ErrDataTooLong.Gen("Data Too Long, field len %d, data len %d", target.Flen, runeCount)
|
|
s = truncateStr(s, truncateLen)
|
|
}
|
|
} else if len(s) > target.Flen {
|
|
err = ErrDataTooLong.Gen("Data Too Long, field len %d, data len %d", target.Flen, len(s))
|
|
s = truncateStr(s, target.Flen)
|
|
}
|
|
}
|
|
ret.SetString(s)
|
|
if target.Charset == charset.CharsetBin {
|
|
ret.k = KindBytes
|
|
}
|
|
return ret, errors.Trace(err)
|
|
}
|
|
|
|
func (d *Datum) convertToInt(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
i64, err := d.toSignedInteger(sc, target.Tp)
|
|
return NewIntDatum(i64), errors.Trace(err)
|
|
}
|
|
|
|
func (d *Datum) convertToUint(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
tp := target.Tp
|
|
upperBound := unsignedUpperBound[tp]
|
|
var (
|
|
val uint64
|
|
err error
|
|
ret Datum
|
|
)
|
|
switch d.k {
|
|
case KindInt64:
|
|
val, err = convertIntToUint(d.GetInt64(), upperBound, tp)
|
|
case KindUint64:
|
|
val, err = convertUintToUint(d.GetUint64(), upperBound, tp)
|
|
case KindFloat32, KindFloat64:
|
|
val, err = convertFloatToUint(d.GetFloat64(), upperBound, tp)
|
|
case KindString, KindBytes:
|
|
fval, err1 := StrToFloat(sc, d.GetString())
|
|
val, err = convertFloatToUint(fval, upperBound, tp)
|
|
if err == nil {
|
|
err = err1
|
|
}
|
|
ret.SetUint64(val)
|
|
case KindMysqlTime:
|
|
dec := d.GetMysqlTime().ToNumber()
|
|
dec.Round(dec, 0)
|
|
ival, err1 := dec.ToInt()
|
|
val, err = convertIntToUint(ival, upperBound, tp)
|
|
if err == nil {
|
|
err = err1
|
|
}
|
|
case KindMysqlDuration:
|
|
dec := d.GetMysqlDuration().ToNumber()
|
|
dec.Round(dec, 0)
|
|
var ival int64
|
|
ival, err = dec.ToInt()
|
|
if err == nil {
|
|
val, err = convertIntToUint(ival, upperBound, tp)
|
|
}
|
|
case KindMysqlDecimal:
|
|
fval, err1 := d.GetMysqlDecimal().ToFloat64()
|
|
val, err = convertFloatToUint(fval, upperBound, tp)
|
|
if err == nil {
|
|
err = err1
|
|
}
|
|
case KindMysqlHex:
|
|
val, err = convertFloatToUint(d.GetMysqlHex().ToNumber(), upperBound, tp)
|
|
case KindMysqlBit:
|
|
val, err = convertFloatToUint(d.GetMysqlBit().ToNumber(), upperBound, tp)
|
|
case KindMysqlEnum:
|
|
val, err = convertFloatToUint(d.GetMysqlEnum().ToNumber(), upperBound, tp)
|
|
case KindMysqlSet:
|
|
val, err = convertFloatToUint(d.GetMysqlSet().ToNumber(), upperBound, tp)
|
|
default:
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
ret.SetUint64(val)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlTime(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
tp := target.Tp
|
|
fsp := DefaultFsp
|
|
if target.Decimal != UnspecifiedLength {
|
|
fsp = target.Decimal
|
|
}
|
|
var ret Datum
|
|
switch d.k {
|
|
case KindMysqlTime:
|
|
t, err := d.GetMysqlTime().Convert(tp)
|
|
if err != nil {
|
|
ret.SetValue(t)
|
|
return ret, errors.Trace(err)
|
|
}
|
|
t, err = t.roundFrac(fsp)
|
|
ret.SetValue(t)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
case KindMysqlDuration:
|
|
t, err := d.GetMysqlDuration().ConvertToTime(tp)
|
|
if err != nil {
|
|
ret.SetValue(t)
|
|
return ret, errors.Trace(err)
|
|
}
|
|
t, err = t.roundFrac(fsp)
|
|
ret.SetValue(t)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
case KindString, KindBytes:
|
|
t, err := ParseTime(d.GetString(), tp, fsp)
|
|
ret.SetValue(t)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
case KindInt64:
|
|
t, err := ParseTimeFromNum(d.GetInt64(), tp, fsp)
|
|
ret.SetValue(t)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
default:
|
|
return invalidConv(d, tp)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlDuration(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
tp := target.Tp
|
|
fsp := DefaultFsp
|
|
if target.Decimal != UnspecifiedLength {
|
|
fsp = target.Decimal
|
|
}
|
|
var ret Datum
|
|
switch d.k {
|
|
case KindMysqlTime:
|
|
dur, err := d.GetMysqlTime().ConvertToDuration()
|
|
if err != nil {
|
|
ret.SetValue(dur)
|
|
return ret, errors.Trace(err)
|
|
}
|
|
dur, err = dur.RoundFrac(fsp)
|
|
ret.SetValue(dur)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
case KindMysqlDuration:
|
|
dur, err := d.GetMysqlDuration().RoundFrac(fsp)
|
|
ret.SetValue(dur)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
case KindString, KindBytes:
|
|
t, err := ParseDuration(d.GetString(), fsp)
|
|
ret.SetValue(t)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
default:
|
|
return invalidConv(d, tp)
|
|
}
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlDecimal(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var ret Datum
|
|
ret.SetLength(target.Flen)
|
|
ret.SetFrac(target.Decimal)
|
|
var dec = &MyDecimal{}
|
|
var err error
|
|
switch d.k {
|
|
case KindInt64:
|
|
dec.FromInt(d.GetInt64())
|
|
case KindUint64:
|
|
dec.FromUint(d.GetUint64())
|
|
case KindFloat32, KindFloat64:
|
|
dec.FromFloat64(d.GetFloat64())
|
|
case KindString, KindBytes:
|
|
err = dec.FromString(d.GetBytes())
|
|
case KindMysqlDecimal:
|
|
*dec = *d.GetMysqlDecimal()
|
|
case KindMysqlTime:
|
|
dec = d.GetMysqlTime().ToNumber()
|
|
case KindMysqlDuration:
|
|
dec = d.GetMysqlDuration().ToNumber()
|
|
case KindMysqlBit:
|
|
dec.FromFloat64(d.GetMysqlBit().ToNumber())
|
|
case KindMysqlEnum:
|
|
dec.FromFloat64(d.GetMysqlEnum().ToNumber())
|
|
case KindMysqlHex:
|
|
dec.FromFloat64(d.GetMysqlHex().ToNumber())
|
|
case KindMysqlSet:
|
|
dec.FromFloat64(d.GetMysqlSet().ToNumber())
|
|
default:
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
if target.Flen != UnspecifiedLength && target.Decimal != UnspecifiedLength {
|
|
prec, frac := dec.PrecisionAndFrac()
|
|
if prec-frac > target.Flen-target.Decimal {
|
|
dec = NewMaxOrMinDec(dec.IsNegative(), target.Flen, target.Decimal)
|
|
err = errors.Trace(ErrOverflow)
|
|
} else if frac != target.Decimal {
|
|
dec.Round(dec, target.Decimal)
|
|
if frac > target.Decimal {
|
|
if sc.TruncateAsError {
|
|
err = errors.Trace(ErrTruncated)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ret.SetValue(dec)
|
|
return ret, err
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlYear(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var (
|
|
ret Datum
|
|
y int64
|
|
err error
|
|
)
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
y, err = StrToInt(sc, d.GetString())
|
|
case KindMysqlTime:
|
|
y = int64(d.GetMysqlTime().Time.Year())
|
|
case KindMysqlDuration:
|
|
y = int64(time.Now().Year())
|
|
default:
|
|
ret, err = d.convertToInt(sc, NewFieldType(mysql.TypeLonglong))
|
|
if err != nil {
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
y = ret.GetInt64()
|
|
}
|
|
y, err = AdjustYear(y)
|
|
if err != nil {
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
ret.SetInt64(y)
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlBit(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
x, err := d.convertToUint(sc, target)
|
|
if err != nil {
|
|
return x, errors.Trace(err)
|
|
}
|
|
// check bit boundary, if bit has n width, the boundary is
|
|
// in [0, (1 << n) - 1]
|
|
width := target.Flen
|
|
if width == 0 || width == UnspecifiedBitWidth {
|
|
width = MinBitWidth
|
|
}
|
|
maxValue := uint64(1)<<uint64(width) - 1
|
|
val := x.GetUint64()
|
|
if val > maxValue {
|
|
x.SetUint64(maxValue)
|
|
return x, overflow(val, target.Tp)
|
|
}
|
|
var ret Datum
|
|
ret.SetValue(Bit{Value: val, Width: width})
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlEnum(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var (
|
|
ret Datum
|
|
e Enum
|
|
err error
|
|
)
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
e, err = ParseEnumName(target.Elems, d.GetString())
|
|
default:
|
|
var uintDatum Datum
|
|
uintDatum, err = d.convertToUint(sc, target)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
e, err = ParseEnumValue(target.Elems, uintDatum.GetUint64())
|
|
}
|
|
if err != nil {
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
ret.SetValue(e)
|
|
return ret, nil
|
|
}
|
|
|
|
func (d *Datum) convertToMysqlSet(sc *variable.StatementContext, target *FieldType) (Datum, error) {
|
|
var (
|
|
ret Datum
|
|
s Set
|
|
err error
|
|
)
|
|
switch d.k {
|
|
case KindString, KindBytes:
|
|
s, err = ParseSetName(target.Elems, d.GetString())
|
|
default:
|
|
var uintDatum Datum
|
|
uintDatum, err = d.convertToUint(sc, target)
|
|
if err != nil {
|
|
return ret, errors.Trace(err)
|
|
}
|
|
s, err = ParseSetValue(target.Elems, uintDatum.GetUint64())
|
|
}
|
|
|
|
if err != nil {
|
|
return invalidConv(d, target.Tp)
|
|
}
|
|
ret.SetValue(s)
|
|
return ret, nil
|
|
}
|
|
|
|
// ToBool converts to a bool.
|
|
// We will use 1 for true, and 0 for false.
|
|
func (d *Datum) ToBool(sc *variable.StatementContext) (int64, error) {
|
|
isZero := false
|
|
switch d.Kind() {
|
|
case KindInt64:
|
|
isZero = (d.GetInt64() == 0)
|
|
case KindUint64:
|
|
isZero = (d.GetUint64() == 0)
|
|
case KindFloat32:
|
|
isZero = (RoundFloat(d.GetFloat64()) == 0)
|
|
case KindFloat64:
|
|
isZero = (RoundFloat(d.GetFloat64()) == 0)
|
|
case KindString:
|
|
s := d.GetString()
|
|
if len(s) == 0 {
|
|
isZero = true
|
|
}
|
|
n, err := StrToInt(sc, s)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
isZero = (n == 0)
|
|
case KindBytes:
|
|
bs := d.GetBytes()
|
|
if len(bs) == 0 {
|
|
isZero = true
|
|
} else {
|
|
n, err := StrToInt(sc, string(bs))
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
isZero = (n == 0)
|
|
}
|
|
case KindMysqlTime:
|
|
isZero = d.GetMysqlTime().IsZero()
|
|
case KindMysqlDuration:
|
|
isZero = (d.GetMysqlDuration().Duration == 0)
|
|
case KindMysqlDecimal:
|
|
v, _ := d.GetMysqlDecimal().ToFloat64()
|
|
isZero = (RoundFloat(v) == 0)
|
|
case KindMysqlHex:
|
|
isZero = (d.GetMysqlHex().ToNumber() == 0)
|
|
case KindMysqlBit:
|
|
isZero = (d.GetMysqlBit().ToNumber() == 0)
|
|
case KindMysqlEnum:
|
|
isZero = (d.GetMysqlEnum().ToNumber() == 0)
|
|
case KindMysqlSet:
|
|
isZero = (d.GetMysqlSet().ToNumber() == 0)
|
|
default:
|
|
return 0, errors.Errorf("cannot convert %v(type %T) to bool", d.GetValue(), d.GetValue())
|
|
}
|
|
if isZero {
|
|
return 0, nil
|
|
}
|
|
return 1, nil
|
|
}
|
|
|
|
// ConvertDatumToDecimal converts datum to decimal.
|
|
func ConvertDatumToDecimal(sc *variable.StatementContext, d Datum) (*MyDecimal, error) {
|
|
dec := new(MyDecimal)
|
|
var err error
|
|
switch d.Kind() {
|
|
case KindInt64:
|
|
dec.FromInt(d.GetInt64())
|
|
case KindUint64:
|
|
dec.FromUint(d.GetUint64())
|
|
case KindFloat32:
|
|
err = dec.FromFloat64(float64(d.GetFloat32()))
|
|
case KindFloat64:
|
|
err = dec.FromFloat64(d.GetFloat64())
|
|
case KindString:
|
|
err = dec.FromString(d.GetBytes())
|
|
case KindMysqlDecimal:
|
|
*dec = *d.GetMysqlDecimal()
|
|
case KindMysqlHex:
|
|
dec.FromInt(d.GetMysqlHex().Value)
|
|
case KindMysqlBit:
|
|
dec.FromUint(d.GetMysqlBit().Value)
|
|
case KindMysqlEnum:
|
|
dec.FromUint(d.GetMysqlEnum().Value)
|
|
case KindMysqlSet:
|
|
dec.FromUint(d.GetMysqlSet().Value)
|
|
default:
|
|
err = fmt.Errorf("can't convert %v to decimal", d.GetValue())
|
|
}
|
|
return dec, err
|
|
}
|
|
|
|
// ToDecimal converts to a decimal.
|
|
func (d *Datum) ToDecimal(sc *variable.StatementContext) (*MyDecimal, error) {
|
|
switch d.Kind() {
|
|
case KindMysqlTime:
|
|
return d.GetMysqlTime().ToNumber(), nil
|
|
case KindMysqlDuration:
|
|
return d.GetMysqlDuration().ToNumber(), nil
|
|
default:
|
|
return ConvertDatumToDecimal(sc, *d)
|
|
}
|
|
}
|
|
|
|
// ToInt64 converts to a int64.
|
|
func (d *Datum) ToInt64(sc *variable.StatementContext) (int64, error) {
|
|
return d.toSignedInteger(sc, mysql.TypeLonglong)
|
|
}
|
|
|
|
func (d *Datum) toSignedInteger(sc *variable.StatementContext, tp byte) (int64, error) {
|
|
lowerBound := signedLowerBound[tp]
|
|
upperBound := signedUpperBound[tp]
|
|
switch d.Kind() {
|
|
case KindInt64:
|
|
return convertIntToInt(d.GetInt64(), lowerBound, upperBound, tp)
|
|
case KindUint64:
|
|
return convertUintToInt(d.GetUint64(), upperBound, tp)
|
|
case KindFloat32:
|
|
return convertFloatToInt(float64(d.GetFloat32()), lowerBound, upperBound, tp)
|
|
case KindFloat64:
|
|
return convertFloatToInt(d.GetFloat64(), lowerBound, upperBound, tp)
|
|
case KindString:
|
|
s := d.GetString()
|
|
fval, err := StrToFloat(sc, s)
|
|
i64, err1 := convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
if err == nil {
|
|
err = err1
|
|
}
|
|
return i64, errors.Trace(err)
|
|
case KindBytes:
|
|
s := string(d.GetBytes())
|
|
fval, err := StrToFloat(sc, s)
|
|
if err != nil {
|
|
return 0, errors.Trace(err)
|
|
}
|
|
return convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
case KindMysqlTime:
|
|
// 2011-11-10 11:11:11.999999 -> 20111110111112
|
|
dec := d.GetMysqlTime().ToNumber()
|
|
dec.Round(dec, 0)
|
|
ival, err := dec.ToInt()
|
|
ival, err2 := convertIntToInt(ival, lowerBound, upperBound, tp)
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
return ival, err
|
|
case KindMysqlDuration:
|
|
// 11:11:11.999999 -> 111112
|
|
dec := d.GetMysqlDuration().ToNumber()
|
|
dec.Round(dec, 0)
|
|
ival, err := dec.ToInt()
|
|
ival, err2 := convertIntToInt(ival, lowerBound, upperBound, tp)
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
return ival, err
|
|
case KindMysqlDecimal:
|
|
var to MyDecimal
|
|
d.GetMysqlDecimal().Round(&to, 0)
|
|
ival, err := to.ToInt()
|
|
ival, err2 := convertIntToInt(ival, lowerBound, upperBound, tp)
|
|
if err == nil {
|
|
err = err2
|
|
}
|
|
return ival, err
|
|
case KindMysqlHex:
|
|
fval := d.GetMysqlHex().ToNumber()
|
|
return convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
case KindMysqlBit:
|
|
fval := d.GetMysqlBit().ToNumber()
|
|
return convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
case KindMysqlEnum:
|
|
fval := d.GetMysqlEnum().ToNumber()
|
|
return convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
case KindMysqlSet:
|
|
fval := d.GetMysqlSet().ToNumber()
|
|
return convertFloatToInt(fval, lowerBound, upperBound, tp)
|
|
default:
|
|
return 0, errors.Errorf("cannot convert %v(type %T) to int64", d.GetValue(), d.GetValue())
|
|
}
|
|
}
|
|
|
|
// ToFloat64 converts to a float64
|
|
func (d *Datum) ToFloat64(sc *variable.StatementContext) (float64, error) {
|
|
switch d.Kind() {
|
|
case KindInt64:
|
|
return float64(d.GetInt64()), nil
|
|
case KindUint64:
|
|
return float64(d.GetUint64()), nil
|
|
case KindFloat32:
|
|
return float64(d.GetFloat32()), nil
|
|
case KindFloat64:
|
|
return d.GetFloat64(), nil
|
|
case KindString:
|
|
return StrToFloat(sc, d.GetString())
|
|
case KindBytes:
|
|
return StrToFloat(sc, string(d.GetBytes()))
|
|
case KindMysqlTime:
|
|
f, err := d.GetMysqlTime().ToNumber().ToFloat64()
|
|
return f, err
|
|
case KindMysqlDuration:
|
|
f, _ := d.GetMysqlDuration().ToNumber().ToFloat64()
|
|
return f, nil
|
|
case KindMysqlDecimal:
|
|
f, err := d.GetMysqlDecimal().ToFloat64()
|
|
return f, err
|
|
case KindMysqlHex:
|
|
return d.GetMysqlHex().ToNumber(), nil
|
|
case KindMysqlBit:
|
|
return d.GetMysqlBit().ToNumber(), nil
|
|
case KindMysqlEnum:
|
|
return d.GetMysqlEnum().ToNumber(), nil
|
|
case KindMysqlSet:
|
|
return d.GetMysqlSet().ToNumber(), nil
|
|
default:
|
|
return 0, errors.Errorf("cannot convert %v(type %T) to float64", d.GetValue(), d.GetValue())
|
|
}
|
|
}
|
|
|
|
// ToString gets the string representation of the datum.
|
|
func (d *Datum) ToString() (string, error) {
|
|
switch d.Kind() {
|
|
case KindInt64:
|
|
return strconv.FormatInt(d.GetInt64(), 10), nil
|
|
case KindUint64:
|
|
return strconv.FormatUint(d.GetUint64(), 10), nil
|
|
case KindFloat32:
|
|
return strconv.FormatFloat(float64(d.GetFloat32()), 'f', -1, 32), nil
|
|
case KindFloat64:
|
|
return strconv.FormatFloat(float64(d.GetFloat64()), 'f', -1, 64), nil
|
|
case KindString:
|
|
return d.GetString(), nil
|
|
case KindBytes:
|
|
return d.GetString(), nil
|
|
case KindMysqlTime:
|
|
return d.GetMysqlTime().String(), nil
|
|
case KindMysqlDuration:
|
|
return d.GetMysqlDuration().String(), nil
|
|
case KindMysqlDecimal:
|
|
return d.GetMysqlDecimal().String(), nil
|
|
case KindMysqlHex:
|
|
return d.GetMysqlHex().ToString(), nil
|
|
case KindMysqlBit:
|
|
return d.GetMysqlBit().ToString(), nil
|
|
case KindMysqlEnum:
|
|
return d.GetMysqlEnum().String(), nil
|
|
case KindMysqlSet:
|
|
return d.GetMysqlSet().String(), nil
|
|
default:
|
|
return "", errors.Errorf("cannot convert %v(type %T) to string", d.GetValue(), d.GetValue())
|
|
}
|
|
}
|
|
|
|
func invalidConv(d *Datum, tp byte) (Datum, error) {
|
|
return Datum{}, errors.Errorf("cannot convert %v to type %s", d, TypeStr(tp))
|
|
}
|
|
|
|
func (d *Datum) convergeType(hasUint, hasDecimal, hasFloat *bool) (x Datum) {
|
|
x = *d
|
|
switch d.Kind() {
|
|
case KindUint64:
|
|
*hasUint = true
|
|
case KindFloat32:
|
|
f := d.GetFloat32()
|
|
x.SetFloat64(float64(f))
|
|
*hasFloat = true
|
|
case KindFloat64:
|
|
*hasFloat = true
|
|
case KindMysqlDecimal:
|
|
*hasDecimal = true
|
|
}
|
|
return x
|
|
}
|
|
|
|
// CoerceDatum changes type.
|
|
// If a or b is Float, changes the both to Float.
|
|
// Else if a or b is Decimal, changes the both to Decimal.
|
|
// Else if a or b is Uint and op is not div, mod, or intDiv changes the both to Uint.
|
|
func CoerceDatum(sc *variable.StatementContext, a, b Datum) (x, y Datum, err error) {
|
|
if a.IsNull() || b.IsNull() {
|
|
return x, y, nil
|
|
}
|
|
var (
|
|
hasUint bool
|
|
hasDecimal bool
|
|
hasFloat bool
|
|
)
|
|
x = a.convergeType(&hasUint, &hasDecimal, &hasFloat)
|
|
y = b.convergeType(&hasUint, &hasDecimal, &hasFloat)
|
|
if hasFloat {
|
|
switch x.Kind() {
|
|
case KindInt64:
|
|
x.SetFloat64(float64(x.GetInt64()))
|
|
case KindUint64:
|
|
x.SetFloat64(float64(x.GetUint64()))
|
|
case KindMysqlHex:
|
|
x.SetFloat64(x.GetMysqlHex().ToNumber())
|
|
case KindMysqlBit:
|
|
x.SetFloat64(x.GetMysqlBit().ToNumber())
|
|
case KindMysqlEnum:
|
|
x.SetFloat64(x.GetMysqlEnum().ToNumber())
|
|
case KindMysqlSet:
|
|
x.SetFloat64(x.GetMysqlSet().ToNumber())
|
|
case KindMysqlDecimal:
|
|
fval, err := x.ToFloat64(sc)
|
|
if err != nil {
|
|
return x, y, errors.Trace(err)
|
|
}
|
|
x.SetFloat64(fval)
|
|
}
|
|
switch y.Kind() {
|
|
case KindInt64:
|
|
y.SetFloat64(float64(y.GetInt64()))
|
|
case KindUint64:
|
|
y.SetFloat64(float64(y.GetUint64()))
|
|
case KindMysqlHex:
|
|
y.SetFloat64(y.GetMysqlHex().ToNumber())
|
|
case KindMysqlBit:
|
|
y.SetFloat64(y.GetMysqlBit().ToNumber())
|
|
case KindMysqlEnum:
|
|
y.SetFloat64(y.GetMysqlEnum().ToNumber())
|
|
case KindMysqlSet:
|
|
y.SetFloat64(y.GetMysqlSet().ToNumber())
|
|
case KindMysqlDecimal:
|
|
fval, err := y.ToFloat64(sc)
|
|
if err != nil {
|
|
return x, y, errors.Trace(err)
|
|
}
|
|
y.SetFloat64(fval)
|
|
}
|
|
} else if hasDecimal {
|
|
var dec *MyDecimal
|
|
dec, err = ConvertDatumToDecimal(sc, x)
|
|
if err != nil {
|
|
return x, y, errors.Trace(err)
|
|
}
|
|
x.SetMysqlDecimal(dec)
|
|
dec, err = ConvertDatumToDecimal(sc, y)
|
|
if err != nil {
|
|
return x, y, errors.Trace(err)
|
|
}
|
|
y.SetMysqlDecimal(dec)
|
|
}
|
|
return
|
|
}
|
|
|
|
// NewDatum creates a new Datum from an interface{}.
|
|
func NewDatum(in interface{}) (d Datum) {
|
|
switch x := in.(type) {
|
|
case []interface{}:
|
|
d.SetValue(MakeDatums(x...))
|
|
default:
|
|
d.SetValue(in)
|
|
}
|
|
return d
|
|
}
|
|
|
|
// NewIntDatum creates a new Datum from an int64 value.
|
|
func NewIntDatum(i int64) (d Datum) {
|
|
d.SetInt64(i)
|
|
return d
|
|
}
|
|
|
|
// NewUintDatum creates a new Datum from an uint64 value.
|
|
func NewUintDatum(i uint64) (d Datum) {
|
|
d.SetUint64(i)
|
|
return d
|
|
}
|
|
|
|
// NewBytesDatum creates a new Datum from a byte slice.
|
|
func NewBytesDatum(b []byte) (d Datum) {
|
|
d.SetBytes(b)
|
|
return d
|
|
}
|
|
|
|
// NewStringDatum creates a new Datum from a string.
|
|
func NewStringDatum(s string) (d Datum) {
|
|
d.SetString(s)
|
|
return d
|
|
}
|
|
|
|
// NewFloat64Datum creates a new Datum from a float64 value.
|
|
func NewFloat64Datum(f float64) (d Datum) {
|
|
d.SetFloat64(f)
|
|
return d
|
|
}
|
|
|
|
// NewFloat32Datum creates a new Datum from a float32 value.
|
|
func NewFloat32Datum(f float32) (d Datum) {
|
|
d.SetFloat32(f)
|
|
return d
|
|
}
|
|
|
|
// NewDurationDatum creates a new Datum from a Duration value.
|
|
func NewDurationDatum(dur Duration) (d Datum) {
|
|
d.SetMysqlDuration(dur)
|
|
return d
|
|
}
|
|
|
|
// NewDecimalDatum creates a new Datum form a MyDecimal value.
|
|
func NewDecimalDatum(dec *MyDecimal) (d Datum) {
|
|
d.SetMysqlDecimal(dec)
|
|
return d
|
|
}
|
|
|
|
// MakeDatums creates datum slice from interfaces.
|
|
func MakeDatums(args ...interface{}) []Datum {
|
|
datums := make([]Datum, len(args))
|
|
for i, v := range args {
|
|
datums[i] = NewDatum(v)
|
|
}
|
|
return datums
|
|
}
|
|
|
|
// DatumsToInterfaces converts a datum slice to interface slice.
|
|
func DatumsToInterfaces(datums []Datum) []interface{} {
|
|
ins := make([]interface{}, len(datums))
|
|
for i, v := range datums {
|
|
ins[i] = v.GetValue()
|
|
}
|
|
return ins
|
|
}
|
|
|
|
// MinNotNullDatum returns a datum represents minimum not null value.
|
|
func MinNotNullDatum() Datum {
|
|
return Datum{k: KindMinNotNull}
|
|
}
|
|
|
|
// MaxValueDatum returns a datum represents max value.
|
|
func MaxValueDatum() Datum {
|
|
return Datum{k: KindMaxValue}
|
|
}
|
|
|
|
// EqualDatums compare if a and b contains the same datum values.
|
|
func EqualDatums(sc *variable.StatementContext, a []Datum, b []Datum) (bool, error) {
|
|
if len(a) != len(b) {
|
|
return false, nil
|
|
}
|
|
if a == nil && b == nil {
|
|
return true, nil
|
|
}
|
|
if a == nil || b == nil {
|
|
return false, nil
|
|
}
|
|
for i, ai := range a {
|
|
v, err := ai.CompareDatum(sc, b[i])
|
|
if err != nil {
|
|
return false, errors.Trace(err)
|
|
}
|
|
if v != 0 {
|
|
return false, nil
|
|
}
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// SortDatums sorts a slice of datum.
|
|
func SortDatums(sc *variable.StatementContext, datums []Datum) error {
|
|
sorter := datumsSorter{datums: datums, sc: sc}
|
|
sort.Sort(&sorter)
|
|
return sorter.err
|
|
}
|
|
|
|
type datumsSorter struct {
|
|
datums []Datum
|
|
sc *variable.StatementContext
|
|
err error
|
|
}
|
|
|
|
func (ds *datumsSorter) Len() int {
|
|
return len(ds.datums)
|
|
}
|
|
|
|
func (ds *datumsSorter) Less(i, j int) bool {
|
|
cmp, err := ds.datums[i].CompareDatum(ds.sc, ds.datums[j])
|
|
if err != nil {
|
|
ds.err = errors.Trace(err)
|
|
return true
|
|
}
|
|
return cmp < 0
|
|
}
|
|
|
|
func (ds *datumsSorter) Swap(i, j int) {
|
|
ds.datums[i], ds.datums[j] = ds.datums[j], ds.datums[i]
|
|
}
|