1
0
Fork 0
mirror of https://github.com/chrislusf/seaweedfs synced 2024-06-26 04:18:59 +02:00
seaweedfs/weed/query/sqltypes/value.go
2019-10-09 00:03:18 -07:00

356 lines
9.6 KiB
Go

package sqltypes
import (
"fmt"
"strconv"
"time"
)
var (
// NULL represents the NULL value.
NULL = Value{}
// DontEscape tells you if a character should not be escaped.
DontEscape = byte(255)
nullstr = []byte("null")
)
type Value struct {
typ Type
val []byte
}
// NewValue builds a Value using typ and val. If the value and typ
// don't match, it returns an error.
func NewValue(typ Type, val []byte) (v Value, err error) {
switch {
case IsSigned(typ):
if _, err := strconv.ParseInt(string(val), 0, 64); err != nil {
return NULL, err
}
return MakeTrusted(typ, val), nil
case IsUnsigned(typ):
if _, err := strconv.ParseUint(string(val), 0, 64); err != nil {
return NULL, err
}
return MakeTrusted(typ, val), nil
case IsFloat(typ) || typ == Decimal:
if _, err := strconv.ParseFloat(string(val), 64); err != nil {
return NULL, err
}
return MakeTrusted(typ, val), nil
case IsQuoted(typ) || typ == Bit || typ == Null:
return MakeTrusted(typ, val), nil
}
// All other types are unsafe or invalid.
return NULL, fmt.Errorf("invalid type specified for MakeValue: %v", typ)
}
// MakeTrusted makes a new Value based on the type.
// This function should only be used if you know the value
// and type conform to the rules. Every place this function is
// called, a comment is needed that explains why it's justified.
// Exceptions: The current package and mysql package do not need
// comments. Other packages can also use the function to create
// VarBinary or VarChar values.
func MakeTrusted(typ Type, val []byte) Value {
if typ == Null {
return NULL
}
return Value{typ: typ, val: val}
}
// NewInt64 builds an Int64 Value.
func NewInt64(v int64) Value {
return MakeTrusted(Int64, strconv.AppendInt(nil, v, 10))
}
// NewInt32 builds an Int64 Value.
func NewInt32(v int32) Value {
return MakeTrusted(Int32, strconv.AppendInt(nil, int64(v), 10))
}
// NewUint64 builds an Uint64 Value.
func NewUint64(v uint64) Value {
return MakeTrusted(Uint64, strconv.AppendUint(nil, v, 10))
}
// NewFloat32 builds an Float64 Value.
func NewFloat32(v float32) Value {
return MakeTrusted(Float32, strconv.AppendFloat(nil, float64(v), 'f', -1, 64))
}
// NewFloat64 builds an Float64 Value.
func NewFloat64(v float64) Value {
return MakeTrusted(Float64, strconv.AppendFloat(nil, v, 'g', -1, 64))
}
// NewVarChar builds a VarChar Value.
func NewVarChar(v string) Value {
return MakeTrusted(VarChar, []byte(v))
}
// NewVarBinary builds a VarBinary Value.
// The input is a string because it's the most common use case.
func NewVarBinary(v string) Value {
return MakeTrusted(VarBinary, []byte(v))
}
// NewIntegral builds an integral type from a string representation.
// The type will be Int64 or Uint64. Int64 will be preferred where possible.
func NewIntegral(val string) (n Value, err error) {
signed, err := strconv.ParseInt(val, 0, 64)
if err == nil {
return MakeTrusted(Int64, strconv.AppendInt(nil, signed, 10)), nil
}
unsigned, err := strconv.ParseUint(val, 0, 64)
if err != nil {
return Value{}, err
}
return MakeTrusted(Uint64, strconv.AppendUint(nil, unsigned, 10)), nil
}
// MakeString makes a VarBinary Value.
func MakeString(val []byte) Value {
return MakeTrusted(VarBinary, val)
}
// BuildValue builds a value from any go type. sqltype.Value is
// also allowed.
func BuildValue(goval interface{}) (v Value, err error) {
// Look for the most common types first.
switch goval := goval.(type) {
case nil:
// no op
case []byte:
v = MakeTrusted(VarBinary, goval)
case int64:
v = MakeTrusted(Int64, strconv.AppendInt(nil, int64(goval), 10))
case uint64:
v = MakeTrusted(Uint64, strconv.AppendUint(nil, uint64(goval), 10))
case float64:
v = MakeTrusted(Float64, strconv.AppendFloat(nil, goval, 'f', -1, 64))
case int:
v = MakeTrusted(Int64, strconv.AppendInt(nil, int64(goval), 10))
case int8:
v = MakeTrusted(Int8, strconv.AppendInt(nil, int64(goval), 10))
case int16:
v = MakeTrusted(Int16, strconv.AppendInt(nil, int64(goval), 10))
case int32:
v = MakeTrusted(Int32, strconv.AppendInt(nil, int64(goval), 10))
case uint:
v = MakeTrusted(Uint64, strconv.AppendUint(nil, uint64(goval), 10))
case uint8:
v = MakeTrusted(Uint8, strconv.AppendUint(nil, uint64(goval), 10))
case uint16:
v = MakeTrusted(Uint16, strconv.AppendUint(nil, uint64(goval), 10))
case uint32:
v = MakeTrusted(Uint32, strconv.AppendUint(nil, uint64(goval), 10))
case float32:
v = MakeTrusted(Float32, strconv.AppendFloat(nil, float64(goval), 'f', -1, 64))
case string:
v = MakeTrusted(VarBinary, []byte(goval))
case time.Time:
v = MakeTrusted(Datetime, []byte(goval.Format("2006-01-02 15:04:05")))
case Value:
v = goval
case *BindVariable:
return ValueFromBytes(goval.Type, goval.Value)
default:
return v, fmt.Errorf("unexpected type %T: %v", goval, goval)
}
return v, nil
}
// BuildConverted is like BuildValue except that it tries to
// convert a string or []byte to an integral if the target type
// is an integral. We don't perform other implicit conversions
// because they're unsafe.
func BuildConverted(typ Type, goval interface{}) (v Value, err error) {
if IsIntegral(typ) {
switch goval := goval.(type) {
case []byte:
return ValueFromBytes(typ, goval)
case string:
return ValueFromBytes(typ, []byte(goval))
case Value:
if goval.IsQuoted() {
return ValueFromBytes(typ, goval.Raw())
}
}
}
return BuildValue(goval)
}
// ValueFromBytes builds a Value using typ and val. It ensures that val
// matches the requested type. If type is an integral it's converted to
// a canonical form. Otherwise, the original representation is preserved.
func ValueFromBytes(typ Type, val []byte) (v Value, err error) {
switch {
case IsSigned(typ):
signed, err := strconv.ParseInt(string(val), 0, 64)
if err != nil {
return NULL, err
}
v = MakeTrusted(typ, strconv.AppendInt(nil, signed, 10))
case IsUnsigned(typ):
unsigned, err := strconv.ParseUint(string(val), 0, 64)
if err != nil {
return NULL, err
}
v = MakeTrusted(typ, strconv.AppendUint(nil, unsigned, 10))
case IsFloat(typ) || typ == Decimal:
_, err := strconv.ParseFloat(string(val), 64)
if err != nil {
return NULL, err
}
// After verification, we preserve the original representation.
fallthrough
default:
v = MakeTrusted(typ, val)
}
return v, nil
}
// BuildIntegral builds an integral type from a string representation.
// The type will be Int64 or Uint64. Int64 will be preferred where possible.
func BuildIntegral(val string) (n Value, err error) {
signed, err := strconv.ParseInt(val, 0, 64)
if err == nil {
return MakeTrusted(Int64, strconv.AppendInt(nil, signed, 10)), nil
}
unsigned, err := strconv.ParseUint(val, 0, 64)
if err != nil {
return Value{}, err
}
return MakeTrusted(Uint64, strconv.AppendUint(nil, unsigned, 10)), nil
}
// Type returns the type of Value.
func (v Value) Type() Type {
return v.typ
}
// Raw returns the raw bytes. All types are currently implemented as []byte.
// You should avoid using this function. If you do, you should treat the
// bytes as read-only.
func (v Value) Raw() []byte {
return v.val
}
// Len returns the length.
func (v Value) Len() int {
return len(v.val)
}
// Values represents the array of Value.
type Values []Value
// Len implements the interface.
func (vs Values) Len() int {
len := 0
for _, v := range vs {
len += v.Len()
}
return len
}
// String returns the raw value as a string.
func (v Value) String() string {
return BytesToString(v.val)
}
// ToNative converts Value to a native go type.
// This does not work for sqltypes.Tuple. The function
// panics if there are inconsistencies.
func (v Value) ToNative() interface{} {
var out interface{}
var err error
switch {
case v.typ == Null:
// no-op
case IsSigned(v.typ):
out, err = v.ParseInt64()
case IsUnsigned(v.typ):
out, err = v.ParseUint64()
case IsFloat(v.typ):
out, err = v.ParseFloat64()
default:
out = v.val
}
if err != nil {
panic(err)
}
return out
}
// ParseInt64 will parse a Value into an int64. It does
// not check the type.
func (v Value) ParseInt64() (val int64, err error) {
return strconv.ParseInt(v.String(), 10, 64)
}
// ParseUint64 will parse a Value into a uint64. It does
// not check the type.
func (v Value) ParseUint64() (val uint64, err error) {
return strconv.ParseUint(v.String(), 10, 64)
}
// ParseFloat64 will parse a Value into an float64. It does
// not check the type.
func (v Value) ParseFloat64() (val float64, err error) {
return strconv.ParseFloat(v.String(), 64)
}
// IsNull returns true if Value is null.
func (v Value) IsNull() bool {
return v.typ == Null
}
// IsIntegral returns true if Value is an integral.
func (v Value) IsIntegral() bool {
return IsIntegral(v.typ)
}
// IsSigned returns true if Value is a signed integral.
func (v Value) IsSigned() bool {
return IsSigned(v.typ)
}
// IsUnsigned returns true if Value is an unsigned integral.
func (v Value) IsUnsigned() bool {
return IsUnsigned(v.typ)
}
// IsFloat returns true if Value is a float.
func (v Value) IsFloat() bool {
return IsFloat(v.typ)
}
// IsQuoted returns true if Value must be SQL-quoted.
func (v Value) IsQuoted() bool {
return IsQuoted(v.typ)
}
// IsText returns true if Value is a collatable text.
func (v Value) IsText() bool {
return IsText(v.typ)
}
// IsBinary returns true if Value is binary.
func (v Value) IsBinary() bool {
return IsBinary(v.typ)
}
// IsTemporal returns true if Value is time type.
func (v Value) IsTemporal() bool {
return IsTemporal(v.typ)
}
// ToString returns the value as MySQL would return it as string.
// If the value is not convertible like in the case of Expression, it returns nil.
func (v Value) ToString() string {
return BytesToString(v.val)
}