// 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. Load more
// 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 vType func (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 number func FromFloat(f float64) *Value { return &Value{value: f} } // FromInt creates a Value from an int func FromInt(f int64) *Value { return &Value{value: f} } // FromString creates a Value from a string func FromString(str string) *Value { return &Value{value: str} } // FromBool creates a Value from a bool func FromBool(b bool) *Value { return &Value{value: b} } // FromTimestamp creates a Value from a timestamp func FromTimestamp(t time.Time) *Value { return &Value{value: t} } // FromNull creates a Value with Null value func FromNull() *Value { return &Value{value: nil} } // FromMissing creates a Value with Missing value func FromMissing() *Value { return &Value{value: Missing{}} } // FromBytes creates a Value from a []byte func 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 values func (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 serialization func (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 value func (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 operators const ( 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 {