959 lines · 20.2 KB · Go
RawDownload
// Copyright (c) 2015-2021 MinIO, Inc.//// This file is part of MinIO Object Storage stack//// This program is free software: you can redistribute it and/or modify// it under the terms of the GNU Affero General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the// GNU Affero General Public License for more details.//// You should have received a copy of the GNU Affero General Public License// along with this program.  If not, see <http://www.gnu.org/licenses/>. package sql import (	"encoding/json"	"errors"	"fmt"	"math"	"reflect"	"strconv"	"strings"	"time"	"unicode/utf8") var (	errArithMismatchedTypes = errors.New("cannot perform arithmetic on mismatched types")	errArithInvalidOperator = errors.New("invalid arithmetic operator")	errArithDivideByZero    = errors.New("cannot divide by 0") 	errCmpMismatchedTypes     = errors.New("cannot compare values of different types")	errCmpInvalidBoolOperator = errors.New("invalid comparison operator for boolean arguments")) // Value represents a value of restricted type reduced from an// expression represented by an ASTNode. Only one of the fields is// non-nil.//// In cases where we are fetching data from a data source (like csv),// the type may not be determined yet. In these cases, a byte-slice is// used.type Value struct {	value interface{}} // Missing is used to indicate a non-existing value.type Missing struct{} // MarshalJSON provides json marshaling of values.func (v Value) MarshalJSON() ([]byte, error) {	if b, ok := v.ToBytes(); ok {		return b, nil	}	return json.Marshal(v.value)} // GetTypeString returns a string representation for vTypefunc (v Value) GetTypeString() string {	switch v.value.(type) {	case nil:		return "NULL"	case bool:		return "BOOL"	case string:		return "STRING"	case int64:		return "INT"	case float64:		return "FLOAT"	case time.Time:		return "TIMESTAMP"	case []byte:		return "BYTES"	case []Value:		return "ARRAY"	case Missing:		return "MISSING"	}	return "--"} // Repr returns a string representation of value.func (v Value) Repr() string {	switch x := v.value.(type) {	case nil:		return ":NULL"	case bool, int64, float64:		return fmt.Sprintf("%v:%s", v.value, v.GetTypeString())	case time.Time:		return fmt.Sprintf("%s:TIMESTAMP", x)	case string:		return fmt.Sprintf("\"%s\":%s", x, v.GetTypeString())	case []byte:		return fmt.Sprintf("\"%s\":BYTES", string(x))	case []Value:		var s strings.Builder		s.WriteByte('[')		for i, v := range x {			s.WriteString(v.Repr())			if i < len(x)-1 {				s.WriteByte(',')			}		}		s.WriteString("]:ARRAY")		return s.String()	case Missing:		return ":MISSING"	default:		return fmt.Sprintf("%v:INVALID", v.value)	}} // FromFloat creates a Value from a numberfunc FromFloat(f float64) *Value {	return &Value{value: f}} // FromInt creates a Value from an intfunc FromInt(f int64) *Value {	return &Value{value: f}} // FromString creates a Value from a stringfunc FromString(str string) *Value {	return &Value{value: str}} // FromBool creates a Value from a boolfunc FromBool(b bool) *Value {	return &Value{value: b}} // FromTimestamp creates a Value from a timestampfunc FromTimestamp(t time.Time) *Value {	return &Value{value: t}} // FromNull creates a Value with Null valuefunc FromNull() *Value {	return &Value{value: nil}} // FromMissing creates a Value with Missing valuefunc FromMissing() *Value {	return &Value{value: Missing{}}} // FromBytes creates a Value from a []bytefunc FromBytes(b []byte) *Value {	return &Value{value: b}} // FromArray creates a Value from an array of values.func FromArray(a []Value) *Value {	return &Value{value: a}} // ToFloat works for int and float valuesfunc (v Value) ToFloat() (val float64, ok bool) {	switch x := v.value.(type) {	case float64:		return x, true	case int64:		return float64(x), true	}	return 0, false} // ToInt returns the value if int.func (v Value) ToInt() (val int64, ok bool) {	val, ok = v.value.(int64)	return} // ToString returns the value if string.func (v Value) ToString() (val string, ok bool) {	val, ok = v.value.(string)	return} // Equals returns whether the values strictly match.// Both type and value must match.func (v Value) Equals(b Value) (ok bool) {	if !v.SameTypeAs(b) {		return false	}	return reflect.DeepEqual(v.value, b.value)} // SameTypeAs return whether the two types are strictly the same.func (v Value) SameTypeAs(b Value) (ok bool) {	switch v.value.(type) {	case bool:		_, ok = b.value.(bool)	case string:		_, ok = b.value.(string)	case int64:		_, ok = b.value.(int64)	case float64:		_, ok = b.value.(float64)	case time.Time:		_, ok = b.value.(time.Time)	case []byte:		_, ok = b.value.([]byte)	case []Value:		_, ok = b.value.([]Value)	default:		ok = reflect.TypeOf(v.value) == reflect.TypeOf(b.value)	}	return ok} // ToBool returns the bool value; second return value refers to if the bool// conversion succeeded.func (v Value) ToBool() (val bool, ok bool) {	val, ok = v.value.(bool)	return} // ToTimestamp returns the timestamp value if present.func (v Value) ToTimestamp() (t time.Time, ok bool) {	t, ok = v.value.(time.Time)	return} // ToBytes returns the value if byte-slice.func (v Value) ToBytes() (val []byte, ok bool) {	val, ok = v.value.([]byte)	return} // ToArray returns the value if it is a slice of values.func (v Value) ToArray() (val []Value, ok bool) {	val, ok = v.value.([]Value)	return} // IsNull - checks if value is missing.func (v Value) IsNull() bool {	//nolint:gocritic	switch v.value.(type) {	case nil:		return true	}	return false} // IsMissing - checks if value is missing.func (v Value) IsMissing() bool {	_, ok := v.value.(Missing)	return ok} // IsArray returns whether the value is an array.func (v Value) IsArray() (ok bool) {	_, ok = v.value.([]Value)	return ok} func (v Value) isNumeric() bool {	//nolint:gocritic	switch v.value.(type) {	case int64, float64:		return true	}	return false} // setters used internally to mutate values func (v *Value) setInt(i int64) {	v.value = i} func (v *Value) setFloat(f float64) {	v.value = f} func (v *Value) setString(s string) {	v.value = s} func (v *Value) setBool(b bool) {	v.value = b} func (v *Value) setTimestamp(t time.Time) {	v.value = t} func (v Value) String() string {	return fmt.Sprintf("%#v", v.value)} // CSVString - convert to string for CSV serializationfunc (v Value) CSVString() string {	switch x := v.value.(type) {	case nil, Missing:		return ""	case bool:		if x {			return "true"		}		return "false"	case string:		return x	case int64:		return strconv.FormatInt(x, 10)	case float64:		return strconv.FormatFloat(x, 'g', -1, 64)	case time.Time:		return FormatSQLTimestamp(x)	case []byte:		return string(x)	case []Value:		b, _ := json.Marshal(x)		return string(b) 	default:		return "CSV serialization not implemented for this type"	}} // negate negates a numeric valuefunc (v *Value) negate() {	switch x := v.value.(type) {	case float64:		v.value = -x	case int64:		v.value = -x	}} // Value comparison functions: we do not expose them outside the// module. Logical operators "<", ">", ">=", "<=" work on strings and// numbers. Equality operators "=", "!=" work on strings,// numbers and booleans. // Supported comparison operatorsconst (	opLt    = "<"	opLte   = "<="	opGt    = ">"	opGte   = ">="	opEq    = "="	opIneq  = "!="	opIs    = "IS"	opIsNot = "ISNOT") // InferBytesType will attempt to infer the data type of bytes.// Will fail if value type is not bytes or it would result in invalid utf8.// ORDER: int, float, bool, JSON (object or array), timestamp, string// If the content is valid JSON, the type will still be bytes.func (v *Value) InferBytesType() (err error) {	b, ok := v.ToBytes()	if !ok {		return fmt.Errorf("InferByteType: Input is not bytes, but %v", v.GetTypeString())	} 	// Check for numeric inference	if x, ok := v.bytesToInt(); ok {		v.setInt(x)		return nil	}	if x, ok := v.bytesToFloat(); ok {		v.setFloat(x)		return nil	}	if x, ok := v.bytesToBool(); ok {		v.setBool(x)		return nil	} 	asString := strings.TrimSpace(v.bytesToString())	if len(b) > 0 &&		(strings.HasPrefix(asString, "{") || strings.HasPrefix(asString, "[")) {		return nil	} 	if t, err := parseSQLTimestamp(asString); err == nil {		v.setTimestamp(t)		return nil	}	if !utf8.Valid(b) {		return errors.New("value is not valid utf-8")	}	// Fallback to string	v.setString(asString)	return} // When numeric types are compared, type promotions could happen. If// values do not have types (e.g. when reading from CSV), for// comparison operations, automatic type conversion happens by trying// to check if the value is a number (first an integer, then a float),// and falling back to string.func (v *Value) compareOp(op string, a *Value) (res bool, err error) {	if !isValidComparisonOperator(op) {		return false, errArithInvalidOperator	}	switch op {	case opIs:		if a.IsNull() {			// Missing is null			return v.IsNull() || v.IsMissing(), nil		}		if a.IsMissing() {			return v.IsMissing(), nil		}		// Forward to Equal		op = opEq	case opIsNot:		if a.IsNull() {			// Missing is not null			return !v.IsNull() && !v.IsMissing(), nil		}		if a.IsMissing() {			return !v.IsMissing(), nil		}		// Forward to not equal.		op = opIneq	default:	} 	// Check if type conversion/inference is needed - it is needed	// if the Value is a byte-slice.	err = inferTypesForCmp(v, a)	if err != nil {		return false, err	} 	// Check if either is nil	if v.IsNull() || a.IsNull() {		// If one is, both must be.		return boolCompare(op, v.IsNull(), a.IsNull())	} 	if a.IsMissing() || v.IsMissing() {		// If one is, both must be.		return boolCompare(op, v.IsMissing(), a.IsMissing())	} 	// Check array values	aArr, aOK := a.ToArray()	vArr, vOK := v.ToArray()	if aOK && vOK {		return arrayCompare(op, aArr, vArr)	} 	isNumeric := v.isNumeric() && a.isNumeric()	if isNumeric {		intV, ok1i := v.ToInt()		intA, ok2i := a.ToInt()		if ok1i && ok2i {			return intCompare(op, intV, intA), nil		} 		// If both values are numeric, then at least one is		// float since we got here, so we convert.		flV, _ := v.ToFloat()		flA, _ := a.ToFloat()		return floatCompare(op, flV, flA), nil	} 	strV, ok1s := v.ToString()	strA, ok2s := a.ToString()	if ok1s && ok2s {		return stringCompare(op, strV, strA), nil	} 	boolV, ok1b := v.ToBool()	boolA, ok2b := a.ToBool()	if ok1b && ok2b {		return boolCompare(op, boolV, boolA)	} 	timestampV, ok1t := v.ToTimestamp()	timestampA, ok2t := a.ToTimestamp()	if ok1t && ok2t {		return timestampCompare(op, timestampV, timestampA), nil	} 	// Types cannot be compared, they do not match.	switch op {	case opEq:		return false, nil	case opIneq:		return true, nil	}	return false, errCmpInvalidBoolOperator} func inferTypesForCmp(a *Value, b *Value) error {

Lines 1–500 of 959.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t