123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- 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)
- }
|