You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

632 lines
16 KiB
Go

// Copyright 2012, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
// Package sqltypes implements interfaces and types that represent SQL values.
//
// DROPBOX NOTE: This is a modified version of vitess's sqltypes module.
// The original source can be found at https://code.google.com/p/vitess/
package sqltypes
import (
"bytes"
"encoding/base64"
"encoding/binary"
"reflect"
"strconv"
"strings"
"time"
"github.com/dropbox/godropbox/encoding2"
"github.com/dropbox/godropbox/errors"
)
var (
NULL = Value{}
DONTESCAPE = byte(255)
nullstr = []byte("null")
)
type ValueType byte
const (
NullType = ValueType(0)
NumericType = ValueType(1)
FractionalType = ValueType(2)
StringType = ValueType(3)
UTF8StringType = ValueType(4)
maxMediumintUnsigned int32 = 16777215
)
// Value can store any SQL value. NULL is stored as nil.
type Value struct {
Inner InnerValue
}
// Numeric represents non-fractional SQL number.
type Numeric []byte
// Fractional represents fractional types like float and decimal
// It's functionally equivalent to Numeric other than how it's constructed
type Fractional []byte
// String represents any SQL type that needs to be represented using quotes.
// If isUtf8 is false, it will be hex encoded so it's safe for exception reporting, etc.
type String struct {
data []byte
isUtf8 bool
}
// MakeNumeric makes a Numeric from a []byte without validation.
func MakeNumeric(b []byte) Value {
return Value{Numeric(b)}
}
// MakeFractional makes a Fractional value from a []byte without validation.
func MakeFractional(b []byte) Value {
return Value{Fractional(b)}
}
// MakeString makes a String value from a []byte.
func MakeString(b []byte) Value {
return Value{String{b, false}}
}
// MakeUtf8String makes a String value from a []byte.
func MakeUtf8String(s string) Value {
return Value{String{[]byte(s), true}}
}
// Raw returns the raw bytes. All types are currently implemented as []byte.
func (v Value) Raw() []byte {
if v.Inner == nil {
return nil
}
return v.Inner.raw()
}
// String returns the raw value as a string
func (v Value) String() string {
if v.Inner == nil {
return ""
}
return string(v.Inner.raw())
}
// EncodeSql encodes the value into an SQL statement. Can be binary.
func (v Value) EncodeSql(b encoding2.BinaryWriter) {
if v.Inner == nil {
if _, err := b.Write(nullstr); err != nil {
panic(err)
}
} else {
v.Inner.encodeSql(b)
}
}
// EncodeAscii encodes the value using 7-bit clean ascii bytes.
func (v Value) EncodeAscii(b encoding2.BinaryWriter) {
if v.Inner == nil {
if _, err := b.Write(nullstr); err != nil {
panic(err)
}
} else {
v.Inner.encodeAscii(b)
}
}
// MarshalBinary helps implement BinaryMarshaler interface for Value.
func (v Value) MarshalBinary() ([]byte, error) {
if v.IsNull() {
return []byte{byte(NullType)}, nil
}
return v.Inner.MarshalBinary()
}
// UnmarshalBinary helps implement BinaryUnmarshaler interface for Value.
func (v *Value) UnmarshalBinary(data []byte) error {
reader := bytes.NewReader(data)
b, err := reader.ReadByte()
if err != nil {
return err
}
typ := ValueType(b)
if typ == NullType {
*v = Value{}
return nil
}
length, err := binary.ReadUvarint(reader)
if err != nil {
return err
}
raw := make([]byte, length)
n, err := reader.Read(raw)
if err != nil {
return err
}
if uint64(n) != length {
return errors.Newf("Not enough bytes to read Value")
}
switch typ {
case NumericType:
*v = Value{Numeric(raw)}
case FractionalType:
*v = Value{Fractional(raw)}
case StringType:
*v = Value{String{raw, false}}
case UTF8StringType:
*v = Value{String{raw, true}}
default:
return errors.Newf("Unknown type %d", int(typ))
}
return nil
}
func (v Value) IsNull() bool {
return v.Inner == nil
}
func (v Value) IsNumeric() (ok bool) {
_ = Numeric(nil) // compiler bug work-around
if v.Inner != nil {
_, ok = v.Inner.(Numeric)
}
return ok
}
func (v Value) IsFractional() (ok bool) {
_ = Fractional(nil) // compiler bug work-around
if v.Inner != nil {
_, ok = v.Inner.(Fractional)
}
return ok
}
func (v Value) IsString() (ok bool) {
_ = String{} // compiler bug work-around
if v.Inner != nil {
_, ok = v.Inner.(String)
}
return ok
}
func (v Value) IsUtf8String() (ok bool) {
_ = String{} // compiler bug work-around
if v.Inner != nil {
s, ok := v.Inner.(String)
ok = ok && s.isUtf8
}
return ok
}
// InnerValue defines methods that need to be supported by all non-null value types.
type InnerValue interface {
raw() []byte
encodeSql(encoding2.BinaryWriter)
encodeAscii(encoding2.BinaryWriter)
MarshalBinary() ([]byte, error)
}
func BuildValue(goval interface{}) (v Value, err error) {
switch bindVal := goval.(type) {
case nil:
// no op
case bool:
val := 0
if bindVal {
val = 1
}
v = Value{Numeric(strconv.AppendInt(nil, int64(val), 10))}
//Momo added
case int8:
v = Value{Numeric(strconv.AppendInt(nil, int64(bindVal), 10))}
//Momo added
case int16:
v = Value{Numeric(strconv.AppendInt(nil, int64(bindVal), 10))}
case int:
v = Value{Numeric(strconv.AppendInt(nil, int64(bindVal), 10))}
case int32:
v = Value{Numeric(strconv.AppendInt(nil, int64(bindVal), 10))}
case int64:
v = Value{Numeric(strconv.AppendInt(nil, int64(bindVal), 10))}
case uint:
v = Value{Numeric(strconv.AppendUint(nil, uint64(bindVal), 10))}
case uint8:
v = Value{Numeric(strconv.AppendUint(nil, uint64(bindVal), 10))}
//Momo added
case uint16:
v = Value{Numeric(strconv.AppendUint(nil, uint64(bindVal), 10))}
case uint32:
v = Value{Numeric(strconv.AppendUint(nil, uint64(bindVal), 10))}
case uint64:
v = Value{Numeric(strconv.AppendUint(nil, uint64(bindVal), 10))}
//Momo added
case float32:
v = Value{Fractional(strconv.AppendFloat(nil, float64(bindVal), 'f', -1, 64))}
case float64:
v = Value{Fractional(strconv.AppendFloat(nil, bindVal, 'f', -1, 64))}
case string:
v = Value{String{[]byte(bindVal), true}}
case []byte:
v = Value{String{bindVal, false}}
case time.Time:
v = Value{String{[]byte(bindVal.Format("2006-01-02 15:04:05.000000")), true}}
case Numeric, Fractional, String:
v = Value{bindVal.(InnerValue)}
case Value:
v = bindVal
default:
// Check if v is a pointer.
rv := reflect.ValueOf(goval)
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
return BuildValue(nil)
}
return BuildValue(reflect.Indirect(rv).Interface())
}
return Value{}, errors.Newf("Unsupported bind variable type %T: %v", goval, goval)
}
return v, nil
}
func ConvertIntUnsigned(arg interface{}, columnType string) interface{} {
if i, ok := arg.(int8); ok {
return uint8(i)
}
if i, ok := arg.(int16); ok {
return uint16(i)
}
if i, ok := arg.(int32); ok {
if strings.Contains(columnType,"mediumint") {
// problem with mediumint is that it's a 3-byte type. There is no compatible golang type to match that.
// So to convert from negative to positive we'd need to convert the value manually
if i >= 0 {
return i
}
return uint32(maxMediumintUnsigned + i + 1)
}
return uint32(i)
}
if i, ok := arg.(int64); ok {
return strconv.FormatUint(uint64(i), 10)
}
if i, ok := arg.(int); ok {
return uint(i)
}
return arg
}
// ConverAssignRowNullable is the same as ConvertAssignRow except that it allows
// nil as a value for the row or any of the row values. In thoses cases, the
// corresponding values are ignored.
func ConvertAssignRowNullable(row []Value, dest ...interface{}) error {
if len(row) != len(dest) {
return errors.Newf(
"# of row entries %d does not match # of destinations %d",
len(row),
len(dest))
}
if row == nil {
return nil
}
for i := 0; i < len(row); i++ {
if row[i].IsNull() {
continue
}
err := ConvertAssign(row[i], dest[i])
if err != nil {
return err
}
}
return nil
}
// ConvertAssignRow copies a row of values in the list of destinations. An
// error is returned if any one of the row's element coping is done between
// incompatible value and dest types. The list of destinations must contain
// pointers.
// Note that for anything else than *[]byte the value is copied, however if
// the destination is of type *[]byte it will point the same []byte array as
// the source (no copying).
func ConvertAssignRow(row []Value, dest ...interface{}) error {
if len(row) != len(dest) {
return errors.Newf(
"# of row entries %d does not match # of destinations %d",
len(row),
len(dest))
}
for i := 0; i < len(row); i++ {
err := ConvertAssign(row[i], dest[i])
if err != nil {
return err
}
}
return nil
}
// ConvertAssign copies to the '*dest' the value in 'src'. An error is returned
// if the coping is done between incompatible Value and dest types. 'dest' must be
// a pointer type.
// Note that for anything else than *[]byte the value is copied, however if 'dest'
// is of type *[]byte it will point to same []byte array as 'src.Raw()' (no copying).
func ConvertAssign(src Value, dest interface{}) error {
// TODO(zviad): reflecting might be too slow so common cases
// can probably be handled without reflections
var s String
var n Numeric
var f Fractional
var ok bool
var err error
if src.Inner == nil {
return errors.Newf("source is null")
}
switch d := dest.(type) {
case *string:
if s, ok = src.Inner.(String); !ok {
return errors.Newf("source: '%v' is not String", src)
}
*d = string(s.raw())
return nil
case *[]byte:
if s, ok = src.Inner.(String); !ok {
return errors.Newf("source: '%v' is not String", src)
}
*d = s.raw()
return nil
// TODO(zviad): figure out how to do this without reflections
// because I think reflections are slow?
//case *int, *int8, *int16, *int32, *int64:
// if n, ok := src.Inner.(Numeric); !ok {
// return errors.Newf("source: %v is not Numeric", src)
// }
// if i64, err := strconv.ParseInt(string(n.raw()), 10, 64); err != nil {
// return err
// }
// *d = i64
// return nil
}
dpv := reflect.ValueOf(dest)
if dpv.Kind() != reflect.Ptr {
return errors.Newf("destination not a pointer")
}
if dpv.IsNil() {
return errors.Newf("destination pointer is Nil")
}
dv := reflect.Indirect(dpv)
switch dv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if n, ok = src.Inner.(Numeric); !ok {
return errors.Newf("source: '%v' is not Numeric", src)
}
var i64 int64
if i64, err = strconv.ParseInt(string(n.raw()), 10, dv.Type().Bits()); err != nil {
return err
}
dv.SetInt(i64)
return nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
if n, ok = src.Inner.(Numeric); !ok {
return errors.Newf("source: '%v' is not Numeric", src)
}
var u64 uint64
if u64, err = strconv.ParseUint(string(n.raw()), 10, dv.Type().Bits()); err != nil {
return err
}
dv.SetUint(u64)
return nil
case reflect.Float32, reflect.Float64:
if f, ok = src.Inner.(Fractional); !ok {
return errors.Newf("source: '%v' is not Fractional", src)
}
var f64 float64
if f64, err = strconv.ParseFloat(string(f.raw()), dv.Type().Bits()); err != nil {
return err
}
dv.SetFloat(f64)
return nil
case reflect.Bool:
// treat bool as true if non-zero integer
if n, ok = src.Inner.(Numeric); !ok {
return errors.Newf("source: '%v' is not Numeric", src)
}
var i64 int64
if i64, err = strconv.ParseInt(string(n.raw()), 10, 64); err != nil {
return err
}
dv.SetBool(i64 != 0)
return nil
}
return errors.Newf("unsupported destination type: %v", dest)
}
// ConvertAssign, but with support for default values
func ConvertAssignDefault(src Value, dest interface{}, defaultValue interface{}) error {
if src.IsNull() {
// This is not the most efficient way of doing things, but it's certainly cleaner
v, err := BuildValue(defaultValue)
if err != nil {
return err
}
return ConvertAssign(v, dest)
}
return ConvertAssign(src, dest)
}
// BuildNumeric builds a Numeric type that represents any whole number.
// It normalizes the representation to ensure 1:1 mapping between the
// number and its representation.
func BuildNumeric(val string) (n Value, err error) {
if val[0] == '-' || val[0] == '+' {
signed, err := strconv.ParseInt(val, 0, 64)
if err != nil {
return Value{}, err
}
n = Value{Numeric(strconv.AppendInt(nil, signed, 10))}
} else {
unsigned, err := strconv.ParseUint(val, 0, 64)
if err != nil {
return Value{}, err
}
n = Value{Numeric(strconv.AppendUint(nil, unsigned, 10))}
}
return n, nil
}
func writeBinary(typ ValueType, data []byte) ([]byte, error) {
var scratch [binary.MaxVarintLen64]byte
n := binary.PutUvarint(scratch[:], uint64(len(data)))
var buf bytes.Buffer
buf.WriteByte(byte(typ))
buf.Write(scratch[:n])
buf.Write(data)
return buf.Bytes(), nil
}
func (n Numeric) raw() []byte {
return []byte(n)
}
func (n Numeric) encodeSql(b encoding2.BinaryWriter) {
if _, err := b.Write(n.raw()); err != nil {
panic(err)
}
}
func (n Numeric) encodeAscii(b encoding2.BinaryWriter) {
if _, err := b.Write(n.raw()); err != nil {
panic(err)
}
}
func (n Numeric) MarshalBinary() ([]byte, error) {
return writeBinary(NumericType, n.raw())
}
func (f Fractional) raw() []byte {
return []byte(f)
}
func (f Fractional) encodeSql(b encoding2.BinaryWriter) {
if _, err := b.Write(f.raw()); err != nil {
panic(err)
}
}
func (f Fractional) encodeAscii(b encoding2.BinaryWriter) {
if _, err := b.Write(f.raw()); err != nil {
panic(err)
}
}
func (f Fractional) MarshalBinary() ([]byte, error) {
return writeBinary(FractionalType, f.raw())
}
func (s String) raw() []byte {
return []byte(s.data)
}
func (s String) encodeSql(b encoding2.BinaryWriter) {
if s.isUtf8 {
writebyte(b, '\'')
rawBytes := s.raw()
for i, ch := range rawBytes {
if encodedChar := SqlEncodeMap[ch]; encodedChar == DONTESCAPE {
writebyte(b, ch)
} else if i < len(rawBytes)-1 && '\\' == ch && ('%' == rawBytes[i+1] || '_' == rawBytes[i+1]) {
// Don't escape '\' specifically in the constructions '\%' or
// '\_', because those are special to how the RHS of LIKE
// clauses are escaped. See the notes following table 9.1 in
// http://dev.mysql.com/doc/refman/5.7/en/string-literals.html
writebyte(b, ch)
} else {
writebyte(b, '\\')
writebyte(b, encodedChar)
}
}
writebyte(b, '\'')
} else {
b.Write([]byte("X'"))
encoding2.HexEncodeToWriter(b, s.raw())
writebyte(b, '\'')
}
}
func (s String) encodeAscii(b encoding2.BinaryWriter) {
writebyte(b, '\'')
encoder := base64.NewEncoder(base64.StdEncoding, b)
encoder.Write(s.raw())
encoder.Close()
writebyte(b, '\'')
}
func (s String) MarshalBinary() ([]byte, error) {
if s.isUtf8 {
return writeBinary(UTF8StringType, s.raw())
}
return writeBinary(StringType, s.raw())
}
func writebyte(b encoding2.BinaryWriter, c byte) {
if err := b.WriteByte(c); err != nil {
panic(err)
}
}
// Helper function for converting a uint64 to a string suitable for SQL.
func Uint64EncodeSql(b encoding2.BinaryWriter, num uint64) {
numVal, _ := BuildValue(num)
numVal.EncodeSql(b)
}
// SqlEncodeMap specifies how to escape binary data with '\'.
// Complies to http://dev.mysql.com/doc/refman/5.1/en/string-syntax.html
var SqlEncodeMap [256]byte
// SqlDecodeMap is the reverse of SqlEncodeMap
var SqlDecodeMap [256]byte
var encodeRef = map[byte]byte{
'\x00': '0',
'\'': '\'',
'"': '"',
'\b': 'b',
'\n': 'n',
'\r': 'r',
'\t': 't',
26: 'Z', // ctl-Z
'\\': '\\',
}
func init() {
for i, _ := range SqlEncodeMap {
SqlEncodeMap[i] = DONTESCAPE
SqlDecodeMap[i] = DONTESCAPE
}
for i, _ := range SqlEncodeMap {
if to, ok := encodeRef[byte(i)]; ok {
SqlEncodeMap[byte(i)] = to
SqlDecodeMap[to] = byte(i)
}
}
}