PR feedback & changes

This commit is contained in:
Daylon Wilkins
2020-02-13 16:15:06 -08:00
committed by Daylon Wilkins
parent beeb5fcb8e
commit c52f790ac1
61 changed files with 1402 additions and 3045 deletions

View File

@@ -1,4 +1,4 @@
pk, int, string, boolean, float, uint, uuid
0, 0, "asdf", TRUE, 0.0, 0, "00000000-0000-0000-0000-000000000000"
1, -1, "qwerty", FALSE, -1.0, 1, "00000000-0000-0000-0000-000000000001"
2, 1, "", TRUE, 0.0, 0, "123e4567-e89b-12d3-a456-426655440000"
0, 0, "asdf", 1, 0.0, 0, "00000000-0000-0000-0000-000000000000"
1, -1, "qwerty", 0, -1.0, 1, "00000000-0000-0000-0000-000000000001"
2, 1, "", 1, 0.0, 0, "123e4567-e89b-12d3-a456-426655440000"
1 pk int string boolean float uint uuid
2 0 0 asdf TRUE 1 0.0 0 00000000-0000-0000-0000-000000000000
3 1 -1 qwerty FALSE 0 -1.0 1 00000000-0000-0000-0000-000000000001
4 2 1 TRUE 1 0.0 0 123e4567-e89b-12d3-a456-426655440000

View File

@@ -54,7 +54,7 @@ teardown() {
[[ "$output" =~ "\`pk\` BIGINT" ]] || false
[[ "$output" =~ "\`int\` BIGINT" ]] || false
[[ "$output" =~ "\`string\` LONGTEXT" ]] || false
[[ "$output" =~ "\`boolean\` BIT(1)" ]] || false
[[ "$output" =~ "\`boolean\` BIGINT" ]] || false
[[ "$output" =~ "\`float\` DOUBLE" ]] || false
[[ "$output" =~ "\`uint\` BIGINT" ]] || false
[[ "$output" =~ "\`uuid\` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin" ]] || false
@@ -77,7 +77,7 @@ teardown() {
[[ "$output" =~ "\`pk\` BIGINT" ]] || false
[[ "$output" =~ "\`int\` BIGINT" ]] || false
[[ "$output" =~ "\`string\` LONGTEXT" ]] || false
[[ "$output" =~ "\`boolean\` BIT(1)" ]] || false
[[ "$output" =~ "\`boolean\` BIGINT" ]] || false
[[ "$output" =~ "\`float\` DOUBLE" ]] || false
[[ "$output" =~ "\`uint\` BIGINT" ]] || false
[[ "$output" =~ "\`uuid\` CHAR(36) CHARACTER SET ascii COLLATE ascii_bin" ]] || false

28
go/Godeps/LICENSES generated
View File

@@ -953,34 +953,6 @@ SOFTWARE.
= LICENSE 78b3d88d4101969f3415353aea94c9f4cce7ca238b012137e57e0273 =
================================================================================
================================================================================
= github.com/araddon/dateparse licensed under: =
The MIT License (MIT)
Copyright (c) 2015-2017 Aaron Raddon
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= LICENSE 99cb99e77c872d66840a2ae14e143620771335a758e6dd7d20f9fb23 =
================================================================================
================================================================================
= github.com/asaskevich/govalidator licensed under: =

View File

@@ -20,7 +20,8 @@ import (
"os"
"strings"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/utils/argparser"
@@ -142,14 +143,15 @@ func ParseKeyValues(nbf *types.NomsBinFormat, sch schema.Schema, args []string)
}
}
convFuncs := make(map[uint64]types.MarshalCallback)
convFuncs := make(map[uint64]func(*string) (types.Value, error))
err := sch.GetPKCols().Iter(func(tag uint64, col schema.Column) (stop bool, err error) {
convFunc, err := doltcore.GetConvFunc(types.StringKind, col.Kind)
if err != nil {
return false, ColumnError{col.Name, "Conversion from string to " + col.KindString() + "is not defined."}
if col.TypeInfo.Equals(typeinfo.StringDefaultType) {
convFuncs[tag] = func(v *string) (types.Value, error) {
return types.String(*v), nil
}
} else {
convFuncs[tag] = col.TypeInfo.ParseValue
}
convFuncs[tag] = convFunc
return false, nil
})
@@ -161,7 +163,7 @@ func ParseKeyValues(nbf *types.NomsBinFormat, sch schema.Schema, args []string)
for _, pkMap := range pkMaps {
taggedVals := make(row.TaggedValues)
for k, v := range pkMap {
val, err := convFuncs[k](types.String(v))
val, err := convFuncs[k](&v)
if err != nil {
return nil, err

View File

@@ -18,11 +18,12 @@ import (
"errors"
"strings"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/store/types"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/table/pipeline"
"github.com/liquidata-inc/dolt/go/store/types"
)
type FilterFn = func(r row.Row) (matchesFilter bool)
@@ -67,14 +68,15 @@ func ParseWhere(sch schema.Schema, whereClause string) (FilterFn, error) {
tags = append(tags, curr.Tag)
}
convFunc, err := doltcore.GetConvFunc(types.StringKind, cols[0].Kind)
if err != nil {
return nil, err
}
val, err := convFunc(types.String(valStr))
if err != nil {
return nil, errors.New("unable to convert '" + valStr + "' to " + col.KindString())
var val types.Value
if cols[0].TypeInfo.Equals(typeinfo.StringDefaultType) {
val = types.String(valStr)
} else {
var err error
val, err = cols[0].TypeInfo.ParseValue(&valStr)
if err != nil {
return nil, errors.New("unable to convert '" + valStr + "' to " + col.TypeInfo.String())
}
}
return func(r row.Row) bool {

View File

@@ -94,7 +94,7 @@ require (
replace github.com/liquidata-inc/dolt/go/gen/proto/dolt/services/eventsapi => ./gen/proto/dolt/services/eventsapi
replace github.com/src-d/go-mysql-server => github.com/liquidata-inc/go-mysql-server v0.5.1-0.20200213191913-709d6546a1d8
replace github.com/src-d/go-mysql-server => github.com/liquidata-inc/go-mysql-server v0.4.1-0.20200214233634-e82bed780f43
replace vitess.io/vitess => github.com/liquidata-inc/vitess v0.0.0-20200102230944-f3410911d61f

View File

@@ -341,6 +341,8 @@ github.com/krishicks/yaml-patch v0.0.10/go.mod h1:Sm5TchwZS6sm7RJoyg87tzxm2ZcKzd
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/liquidata-inc/go-mysql-server v0.4.1-0.20200214233634-e82bed780f43 h1:mzDAPBTZBjt+tSRlhQ0pGjIfjGJ0K94JYncn0tAIzIE=
github.com/liquidata-inc/go-mysql-server v0.4.1-0.20200214233634-e82bed780f43/go.mod h1:Lh0pg7jnO08HxFm6oj6gtcSTUeeOTu4Spt50Aeo2mes=
github.com/liquidata-inc/go-mysql-server v0.5.1-0.20200213191913-709d6546a1d8 h1:ONsKTlbNvi2frCctcqk8mQsQkRgqnf452i1uyhi+HWY=
github.com/liquidata-inc/go-mysql-server v0.5.1-0.20200213191913-709d6546a1d8/go.mod h1:Lh0pg7jnO08HxFm6oj6gtcSTUeeOTu4Spt50Aeo2mes=
github.com/liquidata-inc/ishell v0.0.0-20190514193646-693241f1f2a0 h1:phMgajKClMUiIr+hF2LGt8KRuUa2Vd2GI1sNgHgSXoU=

View File

@@ -54,7 +54,7 @@ var testKeyCols = []schema.Column{
}
var testCols = []schema.Column{
{Name: addrColName, Tag: addrColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
{Name: ageColName, Tag: ageColTag, Kind: types.UintKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
{Name: ageColName, Tag: ageColTag, Kind: types.UintKind, IsPartOfPK: false, TypeInfo: typeinfo.Uint64Type, Constraints: nil},
{Name: titleColName, Tag: titleColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
{Name: reservedColName, Tag: reservedColTag, Kind: types.StringKind, IsPartOfPK: false, TypeInfo: typeinfo.StringDefaultType, Constraints: nil},
}

View File

@@ -17,9 +17,9 @@ package rowconv
import (
"fmt"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/table/pipeline"
"github.com/liquidata-inc/dolt/go/store/types"
)
@@ -56,13 +56,35 @@ func NewRowConverter(mapping *FieldMapping) (*RowConverter, error) {
return nil, fmt.Errorf("Could not find column being mapped. src tag: %d, dest tag: %d", srcTag, destTag)
}
convFunc, err := doltcore.GetConvFunc(srcCol.Kind, destCol.Kind)
if err != nil {
return nil, fmt.Errorf("Unsupported conversion from type %s to %s", srcCol.KindString(), destCol.KindString())
if srcCol.TypeInfo.Equals(destCol.TypeInfo) {
convFuncs[srcTag] = func(v types.Value) (types.Value, error) {
return v, nil
}
}
if destCol.TypeInfo.Equals(typeinfo.StringDefaultType) {
convFuncs[srcTag] = func(v types.Value) (types.Value, error) {
val, err := srcCol.TypeInfo.FormatValue(v)
if err != nil {
return nil, err
}
if val == nil {
return types.NullValue, nil
}
return types.String(*val), nil
}
} else {
convFuncs[srcTag] = func(v types.Value) (types.Value, error) {
str, err := srcCol.TypeInfo.FormatValue(v)
if err != nil {
return nil, err
}
val, err := destCol.TypeInfo.ParseValue(str)
if err != nil {
return nil, err
}
return val, nil
}
}
convFuncs[srcTag] = convFunc
}
return &RowConverter{mapping, false, convFuncs}, nil

View File

@@ -36,7 +36,6 @@ var srcCols, _ = schema.NewColCollection(
schema.NewColumn("inttostr", 4, types.IntKind, false),
schema.NewColumn("stringtostr", 5, types.StringKind, false),
schema.NewColumn("timestamptostr", 6, types.TimestampKind, false),
schema.NewColumn("nulltostr", 7, types.NullKind, false),
)
var srcSch = schema.SchemaFromCols(srcCols)
@@ -62,7 +61,6 @@ func TestRowConverter(t *testing.T) {
4: types.Int(-1234),
5: types.String("string string string"),
6: tt,
7: types.NullValue,
})
assert.NoError(t, err)
@@ -74,11 +72,10 @@ func TestRowConverter(t *testing.T) {
0: types.String(id.String()),
1: types.String("1.25"),
2: types.String("12345678"),
3: types.String("true"),
3: types.String("1"),
4: types.String("-1234"),
5: types.String("string string string"),
6: types.String(tt.String()),
7: types.NullValue,
})
assert.NoError(t, err)

View File

@@ -44,7 +44,7 @@ var (
InvalidTag,
types.NullKind,
false,
typeinfo.NullType,
typeinfo.UnknownType,
nil,
}
)

View File

@@ -79,12 +79,12 @@ func encodeColumn(col schema.Column) encodedColumn {
func (nfd encodedColumn) decodeColumn() (schema.Column, error) {
var typeInfo typeinfo.TypeInfo
var err error
if nfd.Kind == "" && nfd.TypeInfo.Type != "" { // new format
if nfd.TypeInfo.Type != "" {
typeInfo, err = nfd.TypeInfo.decodeTypeInfo()
if err != nil {
return schema.Column{}, err
}
} else if nfd.Kind != "" && nfd.TypeInfo.Type == "" { // old format
} else if nfd.Kind != "" {
typeInfo = typeinfo.FromKind(schema.LwrStrToKind[nfd.Kind])
} else {
return schema.Column{}, errors.New("cannot decode column due to unknown schema format")

View File

@@ -29,11 +29,11 @@ const (
// This is a dolt implementation of the MySQL type Bit, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type bitImpl struct {
type bitType struct {
sqlBitType sql.BitType
}
var _ TypeInfo = (*bitImpl)(nil)
var _ TypeInfo = (*bitType)(nil)
func CreateBitTypeFromParams(params map[string]string) (TypeInfo, error) {
if bitStr, ok := params[bitTypeParam_Bits]; ok {
@@ -45,14 +45,14 @@ func CreateBitTypeFromParams(params map[string]string) (TypeInfo, error) {
if err != nil {
return nil, err
}
return &bitImpl{sqlBitType}, nil
return &bitType{sqlBitType}, nil
} else {
return nil, fmt.Errorf(`create bit type info is missing param "%v"`, bitTypeParam_Bits)
}
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *bitImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *bitType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Uint); ok {
res, err := ti.sqlBitType.Convert(uint64(val))
if err != nil {
@@ -67,83 +67,102 @@ func (ti *bitImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *bitImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.Uint(artifact), nil
func (ti *bitType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
uintVal, err := ti.sqlBitType.Convert(v)
if err != nil {
return nil, err
}
val, ok := uintVal.(uint64)
if ok {
return types.Uint(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *bitImpl) Equals(other TypeInfo) bool {
func (ti *bitType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*bitImpl); ok {
if ti2, ok := other.(*bitType); ok {
return ti.sqlBitType.NumberOfBits() == ti2.sqlBitType.NumberOfBits()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *bitType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
uintVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
val, ok := uintVal.(uint64)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
res := strconv.FormatUint(val, 10)
return &res, nil
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *bitImpl) GetTypeIdentifier() Identifier {
func (ti *bitType) GetTypeIdentifier() Identifier {
return BitTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *bitImpl) GetTypeParams() map[string]string {
func (ti *bitType) GetTypeParams() map[string]string {
return map[string]string{
bitTypeParam_Bits: strconv.FormatInt(int64(ti.sqlBitType.NumberOfBits()), 10),
}
}
// IsValid implements TypeInfo interface.
func (ti *bitImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *bitType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *bitImpl) NomsKind() types.NomsKind {
func (ti *bitType) NomsKind() types.NomsKind {
return types.UintKind
}
// ParseValue implements TypeInfo interface.
func (ti *bitType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
if val, err := strconv.ParseUint(*str, 10, 64); err == nil {
uintVal, err := ti.sqlBitType.Convert(val)
if err != nil {
return nil, err
}
if val, ok := uintVal.(uint64); ok {
return types.Uint(val), nil
}
}
strVal, err := ti.sqlBitType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(uint64); ok {
return types.Uint(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *bitImpl) String() string {
func (ti *bitType) String() string {
return fmt.Sprintf("Bit(%v)", ti.sqlBitType.NumberOfBits())
}
// ToSqlType implements TypeInfo interface.
func (ti *bitImpl) ToSqlType() sql.Type {
func (ti *bitType) ToSqlType() sql.Type {
return ti.sqlBitType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *bitImpl) isValid(v interface{}) (artifact uint64, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return 0, true
case types.Null:
return 0, true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := ti.sqlBitType.Convert(v)
resUint, ok := res.(uint64)
return resUint, err == nil && ok
}

View File

@@ -24,14 +24,16 @@ import (
"github.com/liquidata-inc/dolt/go/store/types"
)
type boolImpl struct{}
type boolType struct {
sqlBitType sql.BitType
}
var _ TypeInfo = (*boolImpl)(nil)
var _ TypeInfo = (*boolType)(nil)
var BoolType TypeInfo = &boolImpl{}
var BoolType TypeInfo = &boolType{sql.MustCreateBitType(1)}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *boolImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *boolType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Bool); ok {
if val {
return uint64(1), nil
@@ -45,162 +47,114 @@ func (ti *boolImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *boolImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
return types.Bool(val), nil
case int:
return types.Bool(val != 0), nil
case int8:
return types.Bool(val != 0), nil
case int16:
return types.Bool(val != 0), nil
case int32:
return types.Bool(val != 0), nil
case int64:
return types.Bool(val != 0), nil
case uint:
return types.Bool(val != 0), nil
case uint8:
return types.Bool(val != 0), nil
case uint16:
return types.Bool(val != 0), nil
case uint32:
return types.Bool(val != 0), nil
case uint64:
return types.Bool(val != 0), nil
case float32:
return types.Bool(int64(math.Round(float64(val))) != 0), nil
case float64:
return types.Bool(int64(math.Round(val)) != 0), nil
case string:
return types.Bool(artifact != 0), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
return val, nil
case types.Int:
return types.Bool(val != 0), nil
case types.Uint:
return types.Bool(val != 0), nil
case types.Float:
return types.Bool(int64(math.Round(float64(val))) != 0), nil
case types.String:
return types.Bool(artifact != 0), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
func (ti *boolType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
return types.Bool(val), nil
case int:
return types.Bool(val != 0), nil
case int8:
return types.Bool(val != 0), nil
case int16:
return types.Bool(val != 0), nil
case int32:
return types.Bool(val != 0), nil
case int64:
return types.Bool(val != 0), nil
case uint:
return types.Bool(val != 0), nil
case uint8:
return types.Bool(val != 0), nil
case uint16:
return types.Bool(val != 0), nil
case uint32:
return types.Bool(val != 0), nil
case uint64:
return types.Bool(val != 0), nil
case float32:
return types.Bool(int64(math.Round(float64(val))) != 0), nil
case float64:
return types.Bool(int64(math.Round(val)) != 0), nil
case string:
b, err := strconv.ParseBool(val)
if err == nil {
return types.Bool(b), nil
}
valInt, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return nil, fmt.Errorf(`"%v" cannot convert value "%v" as it is invalid`, ti.String(), val)
}
return types.Bool(valInt != 0), nil
case []byte:
return ti.ConvertValueToNomsValue(string(val))
default:
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *boolImpl) Equals(other TypeInfo) bool {
func (ti *boolType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*boolImpl)
_, ok := other.(*boolType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *boolType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.Bool); ok {
res := ""
if val {
res = "1"
} else {
res = "0"
}
return &res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *boolImpl) GetTypeIdentifier() Identifier {
func (ti *boolType) GetTypeIdentifier() Identifier {
return BoolTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *boolImpl) GetTypeParams() map[string]string {
func (ti *boolType) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *boolImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *boolType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *boolImpl) NomsKind() types.NomsKind {
func (ti *boolType) NomsKind() types.NomsKind {
return types.BoolKind
}
// ParseValue implements TypeInfo interface.
func (ti *boolType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
return ti.ConvertValueToNomsValue(*str)
}
// String implements TypeInfo interface.
func (ti *boolImpl) String() string {
func (ti *boolType) String() string {
return "Bool"
}
// ToSqlType implements TypeInfo interface.
func (ti *boolImpl) ToSqlType() sql.Type {
return sql.MustCreateBitType(1)
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *boolImpl) isValid(v interface{}) (artifact int64, ok bool) {
switch val := v.(type) {
case nil:
return 0, true
case bool:
return 0, true
case int:
return 0, true
case int8:
return 0, true
case int16:
return 0, true
case int32:
return 0, true
case int64:
return 0, true
case uint:
return 0, true
case uint8:
return 0, true
case uint16:
return 0, true
case uint32:
return 0, true
case uint64:
return 0, true
case float32:
return 0, true
case float64:
return 0, true
case string:
b, err := strconv.ParseBool(val)
if err == nil {
if b {
return 1, true
}
return 0, true
}
valInt, err := strconv.ParseInt(val, 10, 64)
return valInt, err == nil
case types.Null:
return 0, true
case types.Bool:
return 0, true
case types.Int:
return 0, true
case types.Uint:
return 0, true
case types.Float:
return 0, true
case types.String:
b, err := strconv.ParseBool(string(val))
if err == nil {
if b {
return 1, true
}
return 0, true
}
valInt, err := strconv.ParseInt(string(val), 10, 64)
return valInt, err == nil
default:
return 0, false
}
func (ti *boolType) ToSqlType() sql.Type {
return ti.sqlBitType
}

View File

@@ -16,91 +16,54 @@ package typeinfo
import (
"fmt"
"strconv"
"time"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
"github.com/liquidata-inc/dolt/go/store/types"
)
const (
datetimeTypeParam_Min = "min"
datetimeTypeParam_MinNano = "minnano"
datetimeTypeParam_Max = "max"
datetimeTypeParam_MaxNano = "maxnano"
datetimeTypeParam_DateOnly = "date"
datetimeTypeParam_SQL = "sql"
datetimeTypeParam_SQL_Date = "date"
datetimeTypeParam_SQL_Datetime = "datetime"
datetimeTypeParam_SQL_Timestamp = "timestamp"
)
type datetimeImpl struct {
Min time.Time
Max time.Time
DateOnly bool
type datetimeType struct {
sqlDatetimeType sql.DatetimeType
}
var _ TypeInfo = (*datetimeImpl)(nil)
var _ TypeInfo = (*datetimeType)(nil)
var (
DateType = &datetimeType{sql.Date}
DatetimeType = &datetimeType{sql.Datetime}
TimestampType = &datetimeType{sql.Timestamp}
)
func CreateDatetimeTypeFromParams(params map[string]string) (TypeInfo, error) {
var minInt int64
var minNanoInt int64
var err error
if minStr, ok := params[datetimeTypeParam_Min]; ok {
minInt, err = strconv.ParseInt(minStr, 10, 64)
if err != nil {
return nil, err
if sqlType, ok := params[datetimeTypeParam_SQL]; ok {
switch sqlType {
case datetimeTypeParam_SQL_Date:
return DateType, nil
case datetimeTypeParam_SQL_Datetime:
return DatetimeType, nil
case datetimeTypeParam_SQL_Timestamp:
return TimestampType, nil
default:
return nil, fmt.Errorf(`create datetime type info has invalid param "%v"`, sqlType)
}
} else {
return nil, fmt.Errorf(`create datetime type info is missing param "%v"`, datetimeTypeParam_Min)
return nil, fmt.Errorf(`create datetime type info is missing param "%v"`, datetimeTypeParam_SQL)
}
if minNanoStr, ok := params[datetimeTypeParam_MinNano]; ok {
minNanoInt, err = strconv.ParseInt(minNanoStr, 10, 64)
if err != nil {
return nil, err
}
}
var maxInt int64
var maxNanoInt int64
if maxStr, ok := params[datetimeTypeParam_Max]; ok {
maxInt, err = strconv.ParseInt(maxStr, 10, 64)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf(`create datetime type info is missing param "%v"`, datetimeTypeParam_Max)
}
if maxNanoStr, ok := params[datetimeTypeParam_MaxNano]; ok {
maxNanoInt, err = strconv.ParseInt(maxNanoStr, 10, 64)
if err != nil {
return nil, err
}
}
var dateOnly bool
if _, ok := params[datetimeTypeParam_DateOnly]; ok {
dateOnly = true
}
ti := &datetimeImpl{time.Unix(minInt, minNanoInt), time.Unix(maxInt, maxNanoInt), dateOnly}
if dateOnly {
ti.Min = ti.Min.Truncate(24 * time.Hour)
ti.Max = ti.Max.Truncate(24 * time.Hour)
}
if ti.Min.After(ti.Max) || ti.Min.Equal(ti.Max) {
return nil, fmt.Errorf("create datetime type info has min >= max which is disallowed")
}
return ti, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *datetimeImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *datetimeType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
//TODO: handle the zero value as a special case that is valid for all ranges
if val, ok := v.(types.Timestamp); ok {
t := time.Time(val).UTC()
if ti.DateOnly {
t = t.Truncate(24 * time.Hour)
}
if (t.After(ti.Min) && t.Before(ti.Max)) || t.Equal(ti.Min) || t.Equal(ti.Max) {
return t, nil
}
return nil, fmt.Errorf(`"%v" cannot convert time "%v" to value`, ti.String(), t.String())
return ti.sqlDatetimeType.Convert(time.Time(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -109,148 +72,108 @@ func (ti *datetimeImpl) ConvertNomsValueToValue(v types.Value) (interface{}, err
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *datetimeImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
func (ti *datetimeType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
//TODO: handle the zero value as a special case that is valid for all ranges
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case string:
return types.Timestamp(artifact), nil
case types.Null:
return types.NullValue, nil
case time.Time:
return types.Timestamp(artifact), nil
case types.String:
return types.Timestamp(artifact), nil
case types.Timestamp:
return types.Timestamp(artifact), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
timeVal, err := ti.sqlDatetimeType.Convert(v)
if err != nil {
return nil, err
}
val, ok := timeVal.(time.Time)
if ok {
return types.Timestamp(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *datetimeImpl) Equals(other TypeInfo) bool {
func (ti *datetimeType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*datetimeImpl); ok {
return ti.Min.Equal(ti2.Min) && ti.Max.Equal(ti2.Max)
if ti2, ok := other.(*datetimeType); ok {
return ti.sqlDatetimeType.Type() == ti2.sqlDatetimeType.Type()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *datetimeType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
timeVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
val, ok := timeVal.(time.Time)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
if ti.sqlDatetimeType.Type() == sqltypes.Date {
res := val.Format(sql.DateLayout)
return &res, nil
} else {
res := val.Format(sql.TimestampDatetimeLayout)
return &res, nil
}
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *datetimeImpl) GetTypeIdentifier() Identifier {
func (ti *datetimeType) GetTypeIdentifier() Identifier {
return DatetimeTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *datetimeImpl) GetTypeParams() map[string]string {
params := map[string]string{
datetimeTypeParam_Min: strconv.FormatInt(ti.Min.Unix(), 10),
datetimeTypeParam_Max: strconv.FormatInt(ti.Max.Unix(), 10),
func (ti *datetimeType) GetTypeParams() map[string]string {
sqlParam := ""
switch ti.sqlDatetimeType.Type() {
case sqltypes.Date:
sqlParam = datetimeTypeParam_SQL_Date
case sqltypes.Datetime:
sqlParam = datetimeTypeParam_SQL_Datetime
case sqltypes.Timestamp:
sqlParam = datetimeTypeParam_SQL_Timestamp
default:
panic(fmt.Errorf(`unknown datetime type info sql type "%v"`, ti.sqlDatetimeType.Type().String()))
}
if ti.DateOnly {
params[datetimeTypeParam_DateOnly] = ""
}
return params
return map[string]string{datetimeTypeParam_SQL: sqlParam}
}
// IsValid implements TypeInfo interface.
func (ti *datetimeImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *datetimeType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *datetimeImpl) NomsKind() types.NomsKind {
func (ti *datetimeType) NomsKind() types.NomsKind {
return types.TimestampKind
}
// String implements TypeInfo interface.
func (ti *datetimeImpl) String() string {
dateOnly := ""
if ti.DateOnly {
dateOnly = ", DateOnly"
// ParseValue implements TypeInfo interface.
func (ti *datetimeType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
return fmt.Sprintf(`Datetime(Min: "%v", Max: "%v"%v)`, ti.Min.String(), ti.Max.String(), dateOnly)
strVal, err := ti.sqlDatetimeType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(time.Time); ok {
return types.Timestamp(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *datetimeType) String() string {
return fmt.Sprintf(`Datetime(SQL: "%v")`, ti.sqlDatetimeType.String())
}
// ToSqlType implements TypeInfo interface.
func (ti *datetimeImpl) ToSqlType() sql.Type {
if ti.DateOnly {
return sql.Date
}
minTimestamp := sql.Timestamp.MinimumTime()
maxTimestamp := sql.Timestamp.MaximumTime()
if (ti.Min.Equal(minTimestamp) || ti.Min.After(minTimestamp)) && (ti.Max.Equal(maxTimestamp) || ti.Max.Before(maxTimestamp)) {
return sql.Timestamp
}
return sql.Datetime
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *datetimeImpl) isValid(v interface{}) (artifact time.Time, ok bool) {
//TODO: handle the zero value as a special case that is valid for all ranges
switch val := v.(type) {
case nil:
return time.Time{}, true
case string:
for _, format := range sql.TimestampDatetimeLayouts {
if t, err := time.Parse(format, val); err == nil {
t = t.UTC()
if ti.DateOnly {
t = t.Truncate(24 * time.Hour)
}
if (t.After(ti.Min) && t.Before(ti.Max)) || t.Equal(ti.Min) || t.Equal(ti.Max) {
return t, true
}
return time.Time{}, false
}
}
return time.Time{}, false
case types.Null:
return time.Time{}, true
case time.Time:
val = val.UTC()
if ti.DateOnly {
val = val.Truncate(24 * time.Hour)
}
if (val.After(ti.Min) && val.Before(ti.Max)) || val.Equal(ti.Min) || val.Equal(ti.Max) {
return val, true
}
return time.Time{}, false
case types.String:
valStr := string(val)
for _, format := range sql.TimestampDatetimeLayouts {
if t, err := time.Parse(format, valStr); err == nil {
t = t.UTC()
if ti.DateOnly {
t = t.Truncate(24 * time.Hour)
}
if (t.After(ti.Min) && t.Before(ti.Max)) || t.Equal(ti.Min) || t.Equal(ti.Max) {
return t, true
}
return time.Time{}, false
}
}
return time.Time{}, false
case types.Timestamp:
t := time.Time(val).UTC()
if ti.DateOnly {
t = t.Truncate(24 * time.Hour)
}
if (t.After(ti.Min) && t.Before(ti.Max)) || t.Equal(ti.Min) || t.Equal(ti.Max) {
return t, true
}
return time.Time{}, false
default:
return time.Time{}, false
}
func (ti *datetimeType) ToSqlType() sql.Type {
return ti.sqlDatetimeType
}

View File

@@ -30,11 +30,11 @@ const (
// This is a dolt implementation of the MySQL type Decimal, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type decimalImpl struct {
type decimalType struct {
sqlDecimalType sql.DecimalType
}
var _ TypeInfo = (*decimalImpl)(nil)
var _ TypeInfo = (*decimalType)(nil)
func CreateDecimalTypeFromParams(params map[string]string) (TypeInfo, error) {
var precision uint8
@@ -61,11 +61,11 @@ func CreateDecimalTypeFromParams(params map[string]string) (TypeInfo, error) {
if err != nil {
return nil, err
}
return &decimalImpl{sqlDecimalType}, nil
return &decimalType{sqlDecimalType}, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *decimalImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *decimalType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.String); ok {
res, err := ti.sqlDecimalType.Convert(string(val))
if err != nil {
@@ -80,36 +80,56 @@ func (ti *decimalImpl) ConvertNomsValueToValue(v types.Value) (interface{}, erro
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *decimalImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.String(artifact), nil
func (ti *decimalType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
strVal, err := ti.sqlDecimalType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *decimalImpl) Equals(other TypeInfo) bool {
func (ti *decimalType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*decimalImpl); ok {
if ti2, ok := other.(*decimalType); ok {
return ti.sqlDecimalType.Precision() == ti2.sqlDecimalType.Precision() &&
ti.sqlDecimalType.Scale() == ti2.sqlDecimalType.Scale()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *decimalType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
strVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return &val, nil
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *decimalImpl) GetTypeIdentifier() Identifier {
func (ti *decimalType) GetTypeIdentifier() Identifier {
return DecimalTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *decimalImpl) GetTypeParams() map[string]string {
func (ti *decimalType) GetTypeParams() map[string]string {
return map[string]string{
decimalTypeParam_Precision: strconv.FormatUint(uint64(ti.sqlDecimalType.Precision()), 10),
decimalTypeParam_Scale: strconv.FormatUint(uint64(ti.sqlDecimalType.Scale()), 10),
@@ -117,48 +137,37 @@ func (ti *decimalImpl) GetTypeParams() map[string]string {
}
// IsValid implements TypeInfo interface.
func (ti *decimalImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *decimalType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *decimalImpl) NomsKind() types.NomsKind {
func (ti *decimalType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *decimalType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
strVal, err := ti.sqlDecimalType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *decimalImpl) String() string {
func (ti *decimalType) String() string {
return fmt.Sprintf("Decimal(%v, %v)", ti.sqlDecimalType.Precision(), ti.sqlDecimalType.Scale())
}
// ToSqlType implements TypeInfo interface.
func (ti *decimalImpl) ToSqlType() sql.Type {
func (ti *decimalType) ToSqlType() sql.Type {
return ti.sqlDecimalType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *decimalImpl) isValid(v interface{}) (artifact string, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return "", true
case types.Null:
return "", true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := ti.sqlDecimalType.Convert(v)
resStr, ok := res.(string)
return resStr, err == nil && ok
}

View File

@@ -31,11 +31,11 @@ const (
// This is a dolt implementation of the MySQL type Enum, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type enumImpl struct {
type enumType struct {
sqlEnumType sql.EnumType
}
var _ TypeInfo = (*enumImpl)(nil)
var _ TypeInfo = (*enumType)(nil)
func CreateEnumTypeFromParams(params map[string]string) (TypeInfo, error) {
var collation sql.Collation
@@ -62,11 +62,11 @@ func CreateEnumTypeFromParams(params map[string]string) (TypeInfo, error) {
if err != nil {
return nil, err
}
return &enumImpl{sqlEnumType}, nil
return &enumType{sqlEnumType}, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *enumImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *enumType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.String); ok {
res, err := ti.sqlEnumType.Convert(string(val))
if err != nil {
@@ -81,23 +81,27 @@ func (ti *enumImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *enumImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.String(artifact), nil
func (ti *enumType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
strVal, err := ti.sqlEnumType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *enumImpl) Equals(other TypeInfo) bool {
func (ti *enumType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*enumImpl); ok && ti.sqlEnumType.NumberOfElements() == ti2.sqlEnumType.NumberOfElements() {
if ti2, ok := other.(*enumType); ok && ti.sqlEnumType.NumberOfElements() == ti2.sqlEnumType.NumberOfElements() {
tiVals := ti.sqlEnumType.Values()
ti2Vals := ti2.sqlEnumType.Values()
for i := range tiVals {
@@ -110,13 +114,29 @@ func (ti *enumImpl) Equals(other TypeInfo) bool {
return false
}
// FormatValue implements TypeInfo interface.
func (ti *enumType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
strVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return &val, nil
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *enumImpl) GetTypeIdentifier() Identifier {
func (ti *enumType) GetTypeIdentifier() Identifier {
return EnumTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *enumImpl) GetTypeParams() map[string]string {
func (ti *enumType) GetTypeParams() map[string]string {
var sb strings.Builder
enc := gob.NewEncoder(&sb)
err := enc.Encode(ti.sqlEnumType.Values())
@@ -131,48 +151,37 @@ func (ti *enumImpl) GetTypeParams() map[string]string {
}
// IsValid implements TypeInfo interface.
func (ti *enumImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *enumType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *enumImpl) NomsKind() types.NomsKind {
func (ti *enumType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *enumType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
strVal, err := ti.sqlEnumType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *enumImpl) String() string {
func (ti *enumType) String() string {
return fmt.Sprintf(`Enum(Collation: %v, Values: %v)`, ti.sqlEnumType.Collation().String(), strings.Join(ti.sqlEnumType.Values(), ", "))
}
// ToSqlType implements TypeInfo interface.
func (ti *enumImpl) ToSqlType() sql.Type {
func (ti *enumType) ToSqlType() sql.Type {
return ti.sqlEnumType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *enumImpl) isValid(v interface{}) (artifact string, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return "", true
case types.Null:
return "", true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := ti.sqlEnumType.Convert(v)
resStr, ok := res.(string)
return resStr, err == nil && ok
}

View File

@@ -16,10 +16,10 @@ package typeinfo
import (
"fmt"
"math"
"strconv"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
"github.com/liquidata-inc/dolt/go/store/types"
)
@@ -27,27 +27,27 @@ import (
type FloatWidth int8
const (
FloatWidth32 FloatWidth = 32
FloatWidth64 FloatWidth = 64
floatTypeParam_Width = "width"
floatTypeParam_Width = "width"
floatTypeParam_Width_32 = "32"
floatTypeParam_Width_64 = "64"
)
type floatImpl struct {
Width FloatWidth
type floatType struct {
sqlFloatType sql.NumberType
}
var _ TypeInfo = (*floatImpl)(nil)
var _ TypeInfo = (*floatType)(nil)
var (
Float32Type TypeInfo = &floatImpl{FloatWidth32}
Float64Type TypeInfo = &floatImpl{FloatWidth64}
Float32Type = &floatType{sql.Float32}
Float64Type = &floatType{sql.Float64}
)
func CreateFloatTypeFromParams(params map[string]string) (TypeInfo, error) {
if width, ok := params[floatTypeParam_Width]; ok {
switch width {
case "32":
case floatTypeParam_Width_32:
return Float32Type, nil
case "64":
case floatTypeParam_Width_64:
return Float64Type, nil
default:
return nil, fmt.Errorf(`create float type info has "%v" param with value "%v"`, floatTypeParam_Width, width)
@@ -57,16 +57,9 @@ func CreateFloatTypeFromParams(params map[string]string) (TypeInfo, error) {
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *floatImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *floatType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Float); ok {
switch ti.Width {
case FloatWidth32:
return float32(val), nil
case FloatWidth64:
return float64(val), nil
default:
panic(fmt.Errorf(`float width "%v" is not valid`, ti.Width))
}
return ti.sqlFloatType.Convert(float64(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -75,176 +68,107 @@ func (ti *floatImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *floatImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
if val {
return types.Float(1), nil
}
return types.Float(0), nil
case int:
return types.Float(val), nil
case int8:
return types.Float(val), nil
case int16:
return types.Float(val), nil
case int32:
return types.Float(val), nil
case int64:
return types.Float(val), nil
case uint:
return types.Float(val), nil
case uint8:
return types.Float(val), nil
case uint16:
return types.Float(val), nil
case uint32:
return types.Float(val), nil
case uint64:
return types.Float(val), nil
case float32:
return types.Float(val), nil
case float64:
return types.Float(val), nil
case string:
return types.Float(artifact), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
if val {
return types.Float(1), nil
}
return types.Float(0), nil
case types.Int:
return types.Float(val), nil
case types.Uint:
return types.Float(val), nil
case types.Float:
return val, nil
case types.String:
return types.Float(artifact), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *floatType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
fltVal, err := ti.sqlFloatType.Convert(v)
if err != nil {
return nil, err
}
switch val := fltVal.(type) {
case float32:
return types.Float(val), nil
case float64:
return types.Float(val), nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *floatImpl) Equals(other TypeInfo) bool {
func (ti *floatType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*floatImpl); ok {
return ti.Width == ti2.Width
if ti2, ok := other.(*floatType); ok {
return ti.sqlFloatType.Type() == ti2.sqlFloatType.Type()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *floatType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
fltVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
switch val := fltVal.(type) {
case float32:
res := strconv.FormatFloat(float64(val), 'f', -1, 64)
return &res, nil
case float64:
res := strconv.FormatFloat(val, 'f', -1, 64)
return &res, nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *floatImpl) GetTypeIdentifier() Identifier {
func (ti *floatType) GetTypeIdentifier() Identifier {
return FloatTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *floatImpl) GetTypeParams() map[string]string {
return map[string]string{floatTypeParam_Width: strconv.Itoa(int(ti.Width))}
func (ti *floatType) GetTypeParams() map[string]string {
sqlParam := ""
switch ti.sqlFloatType.Type() {
case sqltypes.Float32:
sqlParam = floatTypeParam_Width_32
case sqltypes.Float64:
sqlParam = floatTypeParam_Width_64
default:
panic(fmt.Errorf(`unknown float type info sql type "%v"`, ti.sqlFloatType.Type().String()))
}
return map[string]string{floatTypeParam_Width: sqlParam}
}
// IsValid implements TypeInfo interface.
func (ti *floatImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *floatType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *floatImpl) NomsKind() types.NomsKind {
func (ti *floatType) NomsKind() types.NomsKind {
return types.FloatKind
}
// ParseValue implements TypeInfo interface.
func (ti *floatType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
return ti.ConvertValueToNomsValue(*str)
}
// String implements TypeInfo interface.
func (ti *floatImpl) String() string {
switch ti.Width {
case FloatWidth32:
func (ti *floatType) String() string {
switch ti.sqlFloatType.Type() {
case sqltypes.Float32:
return "Float32"
case FloatWidth64:
case sqltypes.Float64:
return "Float64"
default:
panic(fmt.Errorf(`float width "%v" is not valid`, ti.Width))
panic(fmt.Errorf(`unknown float type info sql type "%v"`, ti.sqlFloatType.Type().String()))
}
}
// ToSqlType implements TypeInfo interface.
func (ti *floatImpl) ToSqlType() sql.Type {
switch ti.Width {
case FloatWidth32:
return sql.Float32
case FloatWidth64:
return sql.Float64
default:
panic(fmt.Errorf(`float width "%v" is not valid`, ti.Width))
}
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *floatImpl) isValid(v interface{}) (artifact float64, ok bool) {
switch val := v.(type) {
case nil:
return 0, true
case bool:
return 0, true
case int:
return 0, true
case int8:
return 0, true
case int16:
return 0, true
case int32:
return 0, true
case int64:
return 0, true
case uint:
return 0, true
case uint8:
return 0, true
case uint16:
return 0, true
case uint32:
return 0, true
case uint64:
return 0, true
case float32:
return 0, true
case float64:
if ti.Width == FloatWidth32 {
return 0, val >= -math.MaxFloat32 && val <= math.MaxFloat32
}
return 0, true
case string:
fltVal, err := strconv.ParseFloat(val, 64)
return fltVal, err == nil
case types.Null:
return 0, true
case types.Bool:
return 0, true
case types.Int:
return 0, true
case types.Uint:
return 0, true
case types.Float:
if ti.Width == FloatWidth32 {
return 0, val >= -math.MaxFloat32 && val <= math.MaxFloat32
}
return 0, true
case types.String:
fltVal, err := strconv.ParseFloat(string(val), 64)
return fltVal, err == nil
default:
return 0, false
}
func (ti *floatType) ToSqlType() sql.Type {
return ti.sqlFloatType
}

View File

@@ -15,10 +15,8 @@
package typeinfo
import (
"encoding/hex"
"fmt"
"math"
"strings"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
@@ -26,16 +24,18 @@ import (
"github.com/liquidata-inc/dolt/go/store/types"
)
type inlineBlobImpl struct{}
type inlineBlobType struct {
sqlBinaryType sql.StringType
}
var _ TypeInfo = (*inlineBlobImpl)(nil)
var _ TypeInfo = (*inlineBlobType)(nil)
var InlineBlobType TypeInfo = &inlineBlobImpl{}
var InlineBlobType = &inlineBlobType{sql.MustCreateBinary(sqltypes.VarBinary, math.MaxUint16)}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *inlineBlobImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *inlineBlobType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.InlineBlob); ok {
return strings.ToUpper(hex.EncodeToString(val)), nil
return string(val), nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -44,86 +44,91 @@ func (ti *inlineBlobImpl) ConvertNomsValueToValue(v types.Value) (interface{}, e
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *inlineBlobImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if _, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case []byte:
return types.InlineBlob(val), nil
case string:
return types.InlineBlob(val), nil
case types.Null:
return types.NullValue, nil
case types.InlineBlob:
return val, nil
case types.String:
return types.InlineBlob(val), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *inlineBlobType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
strVal, err := ti.sqlBinaryType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.InlineBlob(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *inlineBlobImpl) Equals(other TypeInfo) bool {
func (ti *inlineBlobType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*inlineBlobImpl)
_, ok := other.(*inlineBlobType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *inlineBlobType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.InlineBlob); ok {
convVal, err := ti.ConvertNomsValueToValue(val)
if err != nil {
return nil, err
}
res, ok := convVal.(string)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return &res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *inlineBlobImpl) GetTypeIdentifier() Identifier {
func (ti *inlineBlobType) GetTypeIdentifier() Identifier {
return InlineBlobTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *inlineBlobImpl) GetTypeParams() map[string]string {
func (ti *inlineBlobType) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *inlineBlobImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *inlineBlobType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *inlineBlobImpl) NomsKind() types.NomsKind {
func (ti *inlineBlobType) NomsKind() types.NomsKind {
return types.InlineBlobKind
}
// ParseValue implements TypeInfo interface.
func (ti *inlineBlobType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
strVal, err := ti.sqlBinaryType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.InlineBlob(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *inlineBlobImpl) String() string {
func (ti *inlineBlobType) String() string {
return "InlineBlob"
}
// ToSqlType implements TypeInfo interface.
func (ti *inlineBlobImpl) ToSqlType() sql.Type {
return sql.MustCreateBinary(sqltypes.VarBinary, math.MaxUint16)
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *inlineBlobImpl) isValid(v interface{}) (artifact []byte, ok bool) {
switch val := v.(type) {
case nil:
return nil, true
case []byte:
return nil, len(val) <= math.MaxUint16
case string:
return nil, len(val) <= math.MaxUint16
case types.Null:
return nil, true
case types.InlineBlob:
return nil, len(val) <= math.MaxUint16
case types.String:
return nil, len(val) <= math.MaxUint16
default:
return nil, false
}
func (ti *inlineBlobType) ToSqlType() sql.Type {
return ti.sqlBinaryType
}

View File

@@ -16,55 +16,48 @@ package typeinfo
import (
"fmt"
"math"
"strconv"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
"github.com/liquidata-inc/dolt/go/store/types"
)
type IntWidth int8
const (
IntWidth8 IntWidth = 8
IntWidth16 IntWidth = 16
IntWidth24 IntWidth = 24
IntWidth32 IntWidth = 32
IntWidth64 IntWidth = 64
intTypeParams_Width = "width"
intTypeParams_Width_8 = "8"
intTypeParams_Width_16 = "16"
intTypeParams_Width_24 = "24"
intTypeParams_Width_32 = "32"
intTypeParams_Width_64 = "64"
)
const (
MaxInt24 = 1<<23 - 1
MinInt24 = -1 << 23
intTypeParams_Width = "width"
)
type intImpl struct {
Width IntWidth
type intType struct {
sqlIntType sql.NumberType
}
var _ TypeInfo = (*intImpl)(nil)
var _ TypeInfo = (*intType)(nil)
var (
Int8Type TypeInfo = &intImpl{IntWidth8}
Int16Type TypeInfo = &intImpl{IntWidth16}
Int24Type TypeInfo = &intImpl{IntWidth24}
Int32Type TypeInfo = &intImpl{IntWidth32}
Int64Type TypeInfo = &intImpl{IntWidth64}
Int8Type = &intType{sql.Int8}
Int16Type = &intType{sql.Int16}
Int24Type = &intType{sql.Int24}
Int32Type = &intType{sql.Int32}
Int64Type = &intType{sql.Int64}
)
func CreateIntTypeFromParams(params map[string]string) (TypeInfo, error) {
if width, ok := params[intTypeParams_Width]; ok {
switch width {
case "8":
case intTypeParams_Width_8:
return Int8Type, nil
case "16":
case intTypeParams_Width_16:
return Int16Type, nil
case "24":
case intTypeParams_Width_24:
return Int24Type, nil
case "32":
case intTypeParams_Width_32:
return Int32Type, nil
case "64":
case intTypeParams_Width_64:
return Int64Type, nil
default:
return nil, fmt.Errorf(`create int type info has "%v" param with value "%v"`, intTypeParams_Width, width)
@@ -74,22 +67,9 @@ func CreateIntTypeFromParams(params map[string]string) (TypeInfo, error) {
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *intImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *intType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Int); ok {
switch ti.Width {
case IntWidth8:
return int8(val), nil
case IntWidth16:
return int16(val), nil
case IntWidth24:
return int32(val), nil
case IntWidth32:
return int32(val), nil
case IntWidth64:
return int64(val), nil
default:
panic(fmt.Errorf(`int width "%v" is not valid`, ti.Width))
}
return ti.sqlIntType.Convert(int64(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -98,204 +78,129 @@ func (ti *intImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *intImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
if val {
return types.Int(1), nil
}
return types.Int(0), nil
case int:
return types.Int(val), nil
case int8:
return types.Int(val), nil
case int16:
return types.Int(val), nil
case int32:
return types.Int(val), nil
case int64:
return types.Int(val), nil
case uint:
return types.Int(val), nil
case uint8:
return types.Int(val), nil
case uint16:
return types.Int(val), nil
case uint32:
return types.Int(val), nil
case uint64:
return types.Int(val), nil
case float32:
return types.Int(val), nil
case float64:
return types.Int(val), nil
case string:
return types.Int(artifact), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
if val {
return types.Int(1), nil
}
return types.Int(0), nil
case types.Int:
return val, nil
case types.Uint:
return types.Int(val), nil
case types.Float:
return types.Int(val), nil
case types.String:
return types.Int(artifact), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *intType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
intVal, err := ti.sqlIntType.Convert(v)
if err != nil {
return nil, err
}
switch val := intVal.(type) {
case int8:
return types.Int(val), nil
case int16:
return types.Int(val), nil
case int32:
return types.Int(val), nil
case int64:
return types.Int(val), nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *intImpl) Equals(other TypeInfo) bool {
func (ti *intType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*intImpl); ok {
return ti.Width == ti2.Width
if ti2, ok := other.(*intType); ok {
return ti.sqlIntType.Type() == ti2.sqlIntType.Type()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *intType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
intVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
switch val := intVal.(type) {
case int8:
res := strconv.FormatInt(int64(val), 10)
return &res, nil
case int16:
res := strconv.FormatInt(int64(val), 10)
return &res, nil
case int32:
res := strconv.FormatInt(int64(val), 10)
return &res, nil
case int64:
res := strconv.FormatInt(val, 10)
return &res, nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *intImpl) GetTypeIdentifier() Identifier {
func (ti *intType) GetTypeIdentifier() Identifier {
return IntTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *intImpl) GetTypeParams() map[string]string {
return map[string]string{intTypeParams_Width: strconv.Itoa(int(ti.Width))}
func (ti *intType) GetTypeParams() map[string]string {
sqlParam := ""
switch ti.sqlIntType.Type() {
case sqltypes.Int8:
sqlParam = intTypeParams_Width_8
case sqltypes.Int16:
sqlParam = intTypeParams_Width_16
case sqltypes.Int24:
sqlParam = intTypeParams_Width_24
case sqltypes.Int32:
sqlParam = intTypeParams_Width_32
case sqltypes.Int64:
sqlParam = intTypeParams_Width_64
default:
panic(fmt.Errorf(`unknown int type info sql type "%v"`, ti.sqlIntType.Type().String()))
}
return map[string]string{intTypeParams_Width: sqlParam}
}
// IsValid implements TypeInfo interface.
func (ti *intImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *intType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *intImpl) NomsKind() types.NomsKind {
func (ti *intType) NomsKind() types.NomsKind {
return types.IntKind
}
// ParseValue implements TypeInfo interface.
func (ti *intType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
return ti.ConvertValueToNomsValue(*str)
}
// String implements TypeInfo interface.
func (ti *intImpl) String() string {
switch ti.Width {
case IntWidth8:
func (ti *intType) String() string {
switch ti.sqlIntType.Type() {
case sqltypes.Int8:
return "Int8"
case IntWidth16:
case sqltypes.Int16:
return "Int16"
case IntWidth24:
case sqltypes.Int24:
return "Int24"
case IntWidth32:
case sqltypes.Int32:
return "Int32"
case IntWidth64:
case sqltypes.Int64:
return "Int64"
default:
panic(fmt.Errorf(`int width "%v" is not valid`, ti.Width))
panic(fmt.Errorf(`unknown int type info sql type "%v"`, ti.sqlIntType.Type().String()))
}
}
// ToSqlType implements TypeInfo interface.
func (ti *intImpl) ToSqlType() sql.Type {
switch ti.Width {
case IntWidth8:
return sql.Int8
case IntWidth16:
return sql.Int16
case IntWidth24:
return sql.Int24
case IntWidth32:
return sql.Int32
case IntWidth64:
return sql.Int64
default:
panic(fmt.Errorf(`int width "%v" is not valid`, ti.Width))
}
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *intImpl) isValid(v interface{}) (artifact int64, ok bool) {
var minValue int64
var maxValue int64
switch ti.Width {
case IntWidth8:
minValue = math.MinInt8
maxValue = math.MaxInt8
case IntWidth16:
minValue = math.MinInt16
maxValue = math.MaxInt16
case IntWidth24:
minValue = MinInt24
maxValue = MaxInt24
case IntWidth32:
minValue = math.MinInt32
maxValue = math.MaxInt32
case IntWidth64:
minValue = math.MinInt64
maxValue = math.MaxInt64
default:
panic(fmt.Errorf(`int width "%v" is not valid`, ti.Width))
}
switch val := v.(type) {
case nil:
return 0, true
case bool:
return 0, true
case int:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case int8:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case int16:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case int32:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case int64:
return 0, val >= minValue && val <= maxValue
case uint:
return 0, int64(val) <= maxValue
case uint8:
return 0, int64(val) <= maxValue
case uint16:
return 0, int64(val) <= maxValue
case uint32:
return 0, int64(val) <= maxValue
case uint64:
return 0, val <= uint64(maxValue)
case float32:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case float64:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case string:
intVal, err := strconv.ParseInt(val, 10, 64)
return intVal, err == nil
case types.Null:
return 0, true
case types.Bool:
return 0, true
case types.Int:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case types.Uint:
return 0, uint64(val) <= uint64(maxValue)
case types.Float:
return 0, int64(val) >= minValue && int64(val) <= maxValue
case types.String:
intVal, err := strconv.ParseInt(string(val), 10, 64)
return intVal, err == nil
default:
return 0, false
}
func (ti *intType) ToSqlType() sql.Type {
return ti.sqlIntType
}

View File

@@ -1,76 +0,0 @@
// Copyright 2020 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package typeinfo
import (
"github.com/src-d/go-mysql-server/sql"
"github.com/liquidata-inc/dolt/go/store/types"
)
type nullImpl struct{}
var _ TypeInfo = (*nullImpl)(nil)
var NullType TypeInfo = &nullImpl{}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *nullImpl) ConvertNomsValueToValue(types.Value) (interface{}, error) {
return nil, nil
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *nullImpl) ConvertValueToNomsValue(interface{}) (types.Value, error) {
return types.NullValue, nil
}
// Equals implements TypeInfo interface.
func (ti *nullImpl) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*nullImpl)
return ok
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *nullImpl) GetTypeIdentifier() Identifier {
return NullTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *nullImpl) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *nullImpl) IsValid(interface{}) bool {
return true
}
// NomsKind implements TypeInfo interface.
func (ti *nullImpl) NomsKind() types.NomsKind {
return types.NullKind
}
// String implements TypeInfo interface.
func (ti *nullImpl) String() string {
return "Null"
}
// ToSqlType implements TypeInfo interface.
func (ti *nullImpl) ToSqlType() sql.Type {
return sql.Null
}

View File

@@ -31,11 +31,11 @@ const (
// This is a dolt implementation of the MySQL type Set, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type setImpl struct {
type setType struct {
sqlSetType sql.SetType
}
var _ TypeInfo = (*setImpl)(nil)
var _ TypeInfo = (*setType)(nil)
func CreateSetTypeFromParams(params map[string]string) (TypeInfo, error) {
var collation sql.Collation
@@ -62,11 +62,11 @@ func CreateSetTypeFromParams(params map[string]string) (TypeInfo, error) {
if err != nil {
return nil, err
}
return &setImpl{sqlSetType}, nil
return &setType{sqlSetType}, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *setImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *setType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.String); ok {
res, err := ti.sqlSetType.Convert(string(val))
if err != nil {
@@ -81,23 +81,27 @@ func (ti *setImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *setImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.String(artifact), nil
func (ti *setType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
strVal, err := ti.sqlSetType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *setImpl) Equals(other TypeInfo) bool {
func (ti *setType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*setImpl); ok && ti.sqlSetType.NumberOfElements() == ti2.sqlSetType.NumberOfElements() {
if ti2, ok := other.(*setType); ok && ti.sqlSetType.NumberOfElements() == ti2.sqlSetType.NumberOfElements() {
tiVals := ti.sqlSetType.Values()
ti2Vals := ti2.sqlSetType.Values()
for i := range tiVals {
@@ -110,13 +114,29 @@ func (ti *setImpl) Equals(other TypeInfo) bool {
return false
}
// FormatValue implements TypeInfo interface.
func (ti *setType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
strVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return &val, nil
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *setImpl) GetTypeIdentifier() Identifier {
func (ti *setType) GetTypeIdentifier() Identifier {
return SetTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *setImpl) GetTypeParams() map[string]string {
func (ti *setType) GetTypeParams() map[string]string {
var sb strings.Builder
enc := gob.NewEncoder(&sb)
err := enc.Encode(ti.sqlSetType.Values())
@@ -131,48 +151,37 @@ func (ti *setImpl) GetTypeParams() map[string]string {
}
// IsValid implements TypeInfo interface.
func (ti *setImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *setType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *setImpl) NomsKind() types.NomsKind {
func (ti *setType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *setType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
strVal, err := ti.sqlSetType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *setImpl) String() string {
func (ti *setType) String() string {
return fmt.Sprintf(`Set(Collation: %v, Values: %v)`, ti.sqlSetType.Collation().String(), strings.Join(ti.sqlSetType.Values(), ","))
}
// ToSqlType implements TypeInfo interface.
func (ti *setImpl) ToSqlType() sql.Type {
func (ti *setType) ToSqlType() sql.Type {
return ti.sqlSetType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *setImpl) isValid(v interface{}) (artifact string, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return "", true
case types.Null:
return "", true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := ti.sqlSetType.Convert(v)
resStr, ok := res.(string)
return resStr, err == nil && ok
}

View File

@@ -24,20 +24,19 @@ import (
// This is a dolt implementation of the MySQL type Time, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type timeImpl struct{}
type timeType struct {
sqlTimeType sql.TimeType
}
var _ TypeInfo = (*timeImpl)(nil)
var _ TypeInfo = (*timeType)(nil)
var TimeType TypeInfo = &timeImpl{}
var TimeType = &timeType{sql.Time}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *timeImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *timeType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
//TODO: expose the MySQL type's microsecond implementation and persist that to disk? Enables sorting
if val, ok := v.(types.String); ok {
res, err := sql.Time.Convert(string(val))
if err != nil {
return nil, fmt.Errorf(`"%v" cannot convert "%v" to value`, ti.String(), val)
}
return res, nil
return string(val), nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -46,79 +45,84 @@ func (ti *timeImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *timeImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.String(artifact), nil
func (ti *timeType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
strVal, err := ti.sqlTimeType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *timeImpl) Equals(other TypeInfo) bool {
func (ti *timeType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*timeImpl)
_, ok := other.(*timeType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *timeType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.String); ok {
res := string(val)
return &res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *timeImpl) GetTypeIdentifier() Identifier {
func (ti *timeType) GetTypeIdentifier() Identifier {
return TimeTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *timeImpl) GetTypeParams() map[string]string {
func (ti *timeType) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *timeImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *timeType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *timeImpl) NomsKind() types.NomsKind {
func (ti *timeType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *timeType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
strVal, err := ti.sqlTimeType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *timeImpl) String() string {
func (ti *timeType) String() string {
return "Time"
}
// ToSqlType implements TypeInfo interface.
func (ti *timeImpl) ToSqlType() sql.Type {
return sql.Time
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *timeImpl) isValid(v interface{}) (artifact string, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return "", true
case types.Null:
return "", true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := sql.Time.Convert(v)
resStr, ok := res.(string)
return resStr, err == nil && ok
func (ti *timeType) ToSqlType() sql.Type {
return ti.sqlTimeType
}

View File

@@ -22,19 +22,23 @@ import (
"github.com/liquidata-inc/dolt/go/store/types"
)
type tupleImpl struct{}
type tupleType struct{}
var _ TypeInfo = (*tupleImpl)(nil)
var _ TypeInfo = (*tupleType)(nil)
var TupleType TypeInfo = &tupleImpl{}
// This is for internal use only. Used in merge conflicts.
var TupleType = &tupleType{}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *tupleImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *tupleType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if _, ok := v.(types.Null); ok {
return nil, nil
}
return v, nil
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *tupleImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
func (ti *tupleType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if tVal, ok := v.(types.Value); ok {
return tVal, nil
}
@@ -45,26 +49,31 @@ func (ti *tupleImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error)
}
// Equals implements TypeInfo interface.
func (ti *tupleImpl) Equals(other TypeInfo) bool {
func (ti *tupleType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*tupleImpl)
_, ok := other.(*tupleType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *tupleType) FormatValue(v types.Value) (*string, error) {
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *tupleImpl) GetTypeIdentifier() Identifier {
return TupleIdentifier
func (ti *tupleType) GetTypeIdentifier() Identifier {
return TupleTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *tupleImpl) GetTypeParams() map[string]string {
return nil
func (ti *tupleType) GetTypeParams() map[string]string {
panic("cannot persist tuple type")
}
// IsValid implements TypeInfo interface.
func (ti *tupleImpl) IsValid(v interface{}) bool {
func (ti *tupleType) IsValid(v types.Value) bool {
if v == nil {
return true
}
@@ -73,16 +82,21 @@ func (ti *tupleImpl) IsValid(v interface{}) bool {
}
// NomsKind implements TypeInfo interface.
func (ti *tupleImpl) NomsKind() types.NomsKind {
func (ti *tupleType) NomsKind() types.NomsKind {
return types.TupleKind
}
// ParseValue implements TypeInfo interface.
func (ti *tupleType) ParseValue(str *string) (types.Value, error) {
return nil, fmt.Errorf(`"%v" cannot parse strings`, ti.String())
}
// String implements TypeInfo interface.
func (ti *tupleImpl) String() string {
func (ti *tupleType) String() string {
return "Tuple"
}
// ToSqlType implements TypeInfo interface.
func (ti *tupleImpl) ToSqlType() sql.Type {
return sql.LongText
func (ti *tupleType) ToSqlType() sql.Type {
panic("we should never be calling the SQL type on a Tuple column")
}

View File

@@ -35,10 +35,9 @@ const (
FloatTypeIdentifier Identifier = "float"
InlineBlobTypeIdentifier Identifier = "inlineblob"
IntTypeIdentifier Identifier = "int"
NullTypeIdentifier Identifier = "null"
SetTypeIdentifier Identifier = "set"
TimeTypeIdentifier Identifier = "time"
TupleIdentifier Identifier = "tuple"
TupleTypeIdentifier Identifier = "tuple"
UintTypeIdentifier Identifier = "uint"
UuidTypeIdentifier Identifier = "uuid"
VarBinaryTypeIdentifier Identifier = "varbinary"
@@ -56,10 +55,9 @@ var Identifiers = map[Identifier]struct{}{
FloatTypeIdentifier: {},
InlineBlobTypeIdentifier: {},
IntTypeIdentifier: {},
NullTypeIdentifier: {},
SetTypeIdentifier: {},
TimeTypeIdentifier: {},
TupleIdentifier: {},
TupleTypeIdentifier: {},
UintTypeIdentifier: {},
UuidTypeIdentifier: {},
VarBinaryTypeIdentifier: {},
@@ -80,6 +78,9 @@ type TypeInfo interface {
// Equals returns whether the given TypeInfo is equivalent to this TypeInfo.
Equals(other TypeInfo) bool
// FormatValue returns the stringified version of the value.
FormatValue(v types.Value) (*string, error)
// GetTypeIdentifier returns an identifier for this type used for serialization.
GetTypeIdentifier() Identifier
@@ -88,11 +89,14 @@ type TypeInfo interface {
GetTypeParams() map[string]string
// IsValid takes in a value (go or Noms) and returns whether the value is valid for this type.
IsValid(v interface{}) bool
IsValid(v types.Value) bool
// NomsKind returns the NomsKind that best matches this TypeInfo.
NomsKind() types.NomsKind
// ParseValue parses a string and returns a go value that represents it according to this type.
ParseValue(str *string) (types.Value, error)
// ToSqlType returns the TypeInfo as a sql.Type. If an exact match is able to be made then that is
// the one returned, otherwise the sql.Type is the closest match possible.
ToSqlType() sql.Type
@@ -105,7 +109,7 @@ type TypeInfo interface {
func FromSqlType(sqlType sql.Type) (TypeInfo, error) {
switch sqlType.Type() {
case sqltypes.Null:
return NullType, nil
return UnknownType, nil
case sqltypes.Int8:
return Int8Type, nil
case sqltypes.Int16:
@@ -131,114 +135,103 @@ func FromSqlType(sqlType sql.Type) (TypeInfo, error) {
case sqltypes.Float64:
return Float64Type, nil
case sqltypes.Timestamp:
datetimeType, ok := sqlType.(sql.DatetimeType)
if !ok {
return nil, fmt.Errorf(`expected "DatetimeTypeIdentifier" from SQL basetype "Timestamp"`)
}
return &datetimeImpl{
Min: datetimeType.MinimumTime(),
Max: datetimeType.MaximumTime(),
DateOnly: false,
}, nil
return TimestampType, nil
case sqltypes.Date:
datetimeType, ok := sqlType.(sql.DatetimeType)
if !ok {
return nil, fmt.Errorf(`expected "DatetimeTypeIdentifier" from SQL basetype "Date"`)
}
return &datetimeImpl{
Min: datetimeType.MinimumTime(),
Max: datetimeType.MaximumTime(),
DateOnly: true,
}, nil
return DateType, nil
case sqltypes.Time:
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
return TimeType, nil
case sqltypes.Datetime:
datetimeType, ok := sqlType.(sql.DatetimeType)
if !ok {
return nil, fmt.Errorf(`expected "DatetimeTypeIdentifier" from SQL basetype "Datetime"`)
}
return &datetimeImpl{
Min: datetimeType.MinimumTime(),
Max: datetimeType.MaximumTime(),
DateOnly: false,
}, nil
return DatetimeType, nil
case sqltypes.Year:
return YearType, nil
case sqltypes.Decimal:
decimalType, ok := sqlType.(sql.DecimalType)
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
decimalSQLType, ok := sqlType.(sql.DecimalType)
if !ok {
return nil, fmt.Errorf(`expected "DecimalTypeIdentifier" from SQL basetype "Decimal"`)
}
return &decimalImpl{decimalType}, nil
return &decimalType{decimalSQLType}, nil
case sqltypes.Text:
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "Text"`)
}
return &varStringImpl{
stringType.Collation(),
stringType.MaxCharacterLength(),
false,
true,
}, nil
return &varStringType{stringType}, nil
case sqltypes.Blob:
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "Blob"`)
}
return &varBinaryImpl{stringType.MaxByteLength(), false, true}, nil
return &varBinaryType{stringType}, nil
case sqltypes.VarChar:
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "VarChar"`)
}
return &varStringImpl{
stringType.Collation(),
stringType.MaxCharacterLength(),
false,
false,
}, nil
return &varStringType{stringType}, nil
case sqltypes.VarBinary:
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "VarBinary"`)
}
return &varBinaryImpl{stringType.MaxByteLength(), false, false}, nil
return &varBinaryType{stringType}, nil
case sqltypes.Char:
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "Char"`)
}
return &varStringImpl{
stringType.Collation(),
stringType.MaxCharacterLength(),
true,
false,
}, nil
return &varStringType{stringType}, nil
case sqltypes.Binary:
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
stringType, ok := sqlType.(sql.StringType)
if !ok {
return nil, fmt.Errorf(`expected "StringType" from SQL basetype "Binary"`)
}
return &varBinaryImpl{stringType.MaxByteLength(), true, false}, nil
return &varBinaryType{stringType}, nil
case sqltypes.Bit:
bitType, ok := sqlType.(sql.BitType)
bitSQLType, ok := sqlType.(sql.BitType)
if !ok {
return nil, fmt.Errorf(`expected "BitTypeIdentifier" from SQL basetype "Bit"`)
}
return &bitImpl{bitType}, nil
return &bitType{bitSQLType}, nil
case sqltypes.Enum:
enumType, ok := sqlType.(sql.EnumType)
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
enumSQLType, ok := sqlType.(sql.EnumType)
if !ok {
return nil, fmt.Errorf(`expected "EnumTypeIdentifier" from SQL basetype "Enum"`)
}
return &enumImpl{enumType}, nil
return &enumType{enumSQLType}, nil
case sqltypes.Set:
setType, ok := sqlType.(sql.SetType)
//TODO: determine the storage format
if fmt.Sprintf("a") != "" { // always evaluates to true, compiler won't complain about unreachable code
return nil, fmt.Errorf(`"%v" has not yet been implemented`, sqlType.String())
}
setSQLType, ok := sqlType.(sql.SetType)
if !ok {
return nil, fmt.Errorf(`expected "SetTypeIdentifier" from SQL basetype "Set"`)
}
return &setImpl{setType}, nil
return &setType{setSQLType}, nil
default:
return nil, fmt.Errorf(`no type info can be created from SQL base type "%v"`, sqlType.String())
}
@@ -263,13 +256,11 @@ func FromTypeParams(id Identifier, params map[string]string) (TypeInfo, error) {
return InlineBlobType, nil
case IntTypeIdentifier:
return CreateIntTypeFromParams(params)
case NullTypeIdentifier:
return NullType, nil
case SetTypeIdentifier:
return CreateSetTypeFromParams(params)
case TimeTypeIdentifier:
return TimeType, nil
case TupleIdentifier:
case TupleTypeIdentifier:
return TupleType, nil
case UintTypeIdentifier:
return CreateUintTypeFromParams(params)
@@ -298,18 +289,11 @@ func FromKind(kind types.NomsKind) TypeInfo {
case types.IntKind:
return Int64Type
case types.NullKind:
return NullType
return UnknownType
case types.StringKind:
return StringDefaultType
case types.TimestampKind:
// Here we set it to the limits of the SQL Datetime type just so conversions
// between the two types are straightforward. This is an arbitrary decision and
// this can definitely be widened later if we decide to.
return &datetimeImpl{
sql.Datetime.MinimumTime(),
sql.Datetime.MaximumTime(),
false,
}
return DatetimeType
case types.TupleKind:
return TupleType
case types.UintKind:

View File

@@ -16,54 +16,48 @@ package typeinfo
import (
"fmt"
"math"
"strconv"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
"github.com/liquidata-inc/dolt/go/store/types"
)
type UintWidth int8
const (
UintWidth8 UintWidth = 8
UintWidth16 UintWidth = 16
UintWidth24 UintWidth = 24
UintWidth32 UintWidth = 32
UintWidth64 UintWidth = 64
uintTypeParam_Width = "width"
uintTypeParam_Width = "width"
uintTypeParam_Width_8 = "8"
uintTypeParam_Width_16 = "16"
uintTypeParam_Width_24 = "24"
uintTypeParam_Width_32 = "32"
uintTypeParam_Width_64 = "64"
)
const (
MaxUint24 = 1 << 24
)
type uintImpl struct {
Width UintWidth
type uintType struct {
sqlUintType sql.NumberType
}
var _ TypeInfo = (*uintImpl)(nil)
var _ TypeInfo = (*uintType)(nil)
var (
Uint8Type TypeInfo = &uintImpl{UintWidth8}
Uint16Type TypeInfo = &uintImpl{UintWidth16}
Uint24Type TypeInfo = &uintImpl{UintWidth24}
Uint32Type TypeInfo = &uintImpl{UintWidth32}
Uint64Type TypeInfo = &uintImpl{UintWidth64}
Uint8Type = &uintType{sql.Uint8}
Uint16Type = &uintType{sql.Uint16}
Uint24Type = &uintType{sql.Uint24}
Uint32Type = &uintType{sql.Uint32}
Uint64Type = &uintType{sql.Uint64}
)
func CreateUintTypeFromParams(params map[string]string) (TypeInfo, error) {
if width, ok := params[uintTypeParam_Width]; ok {
switch width {
case "8":
case uintTypeParam_Width_8:
return Uint8Type, nil
case "16":
case uintTypeParam_Width_16:
return Uint16Type, nil
case "24":
case uintTypeParam_Width_24:
return Uint24Type, nil
case "32":
case uintTypeParam_Width_32:
return Uint32Type, nil
case "64":
case uintTypeParam_Width_64:
return Uint64Type, nil
default:
return nil, fmt.Errorf(`create uint type info has "%v" param with value "%v"`, uintTypeParam_Width, width)
@@ -73,22 +67,9 @@ func CreateUintTypeFromParams(params map[string]string) (TypeInfo, error) {
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *uintImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *uintType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Uint); ok {
switch ti.Width {
case UintWidth8:
return uint8(val), nil
case UintWidth16:
return uint16(val), nil
case UintWidth24:
return uint32(val), nil
case UintWidth32:
return uint32(val), nil
case UintWidth64:
return uint64(val), nil
default:
panic(fmt.Errorf(`uint width "%v" is not valid`, ti.Width))
}
return ti.sqlUintType.Convert(uint64(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -97,198 +78,129 @@ func (ti *uintImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *uintImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
if val {
return types.Uint(1), nil
}
return types.Uint(0), nil
case int:
return types.Uint(val), nil
case int8:
return types.Uint(val), nil
case int16:
return types.Uint(val), nil
case int32:
return types.Uint(val), nil
case int64:
return types.Uint(val), nil
case uint:
return types.Uint(val), nil
case uint8:
return types.Uint(val), nil
case uint16:
return types.Uint(val), nil
case uint32:
return types.Uint(val), nil
case uint64:
return types.Uint(val), nil
case float32:
return types.Uint(val), nil
case float64:
return types.Uint(val), nil
case string:
return types.Uint(artifact), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
if val {
return types.Uint(1), nil
}
return types.Uint(0), nil
case types.Int:
return types.Uint(val), nil
case types.Uint:
return val, nil
case types.Float:
return types.Uint(val), nil
case types.String:
return types.Uint(artifact), nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *uintType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
uintVal, err := ti.sqlUintType.Convert(v)
if err != nil {
return nil, err
}
switch val := uintVal.(type) {
case uint8:
return types.Uint(val), nil
case uint16:
return types.Uint(val), nil
case uint32:
return types.Uint(val), nil
case uint64:
return types.Uint(val), nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *uintImpl) Equals(other TypeInfo) bool {
func (ti *uintType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*uintImpl); ok {
return ti.Width == ti2.Width
if ti2, ok := other.(*uintType); ok {
return ti.sqlUintType.Type() == ti2.sqlUintType.Type()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *uintType) FormatValue(v types.Value) (*string, error) {
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
uintVal, err := ti.ConvertNomsValueToValue(v)
if err != nil {
return nil, err
}
switch val := uintVal.(type) {
case uint8:
res := strconv.FormatUint(uint64(val), 10)
return &res, nil
case uint16:
res := strconv.FormatUint(uint64(val), 10)
return &res, nil
case uint32:
res := strconv.FormatUint(uint64(val), 10)
return &res, nil
case uint64:
res := strconv.FormatUint(val, 10)
return &res, nil
default:
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *uintImpl) GetTypeIdentifier() Identifier {
func (ti *uintType) GetTypeIdentifier() Identifier {
return UintTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *uintImpl) GetTypeParams() map[string]string {
return map[string]string{uintTypeParam_Width: strconv.Itoa(int(ti.Width))}
func (ti *uintType) GetTypeParams() map[string]string {
sqlParam := ""
switch ti.sqlUintType.Type() {
case sqltypes.Uint8:
sqlParam = uintTypeParam_Width_8
case sqltypes.Uint16:
sqlParam = uintTypeParam_Width_16
case sqltypes.Uint24:
sqlParam = uintTypeParam_Width_24
case sqltypes.Uint32:
sqlParam = uintTypeParam_Width_32
case sqltypes.Uint64:
sqlParam = uintTypeParam_Width_64
default:
panic(fmt.Errorf(`unknown uint type info sql type "%v"`, ti.sqlUintType.Type().String()))
}
return map[string]string{uintTypeParam_Width: sqlParam}
}
// IsValid implements TypeInfo interface.
func (ti *uintImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *uintType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *uintImpl) NomsKind() types.NomsKind {
func (ti *uintType) NomsKind() types.NomsKind {
return types.UintKind
}
// ParseValue implements TypeInfo interface.
func (ti *uintType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
return ti.ConvertValueToNomsValue(*str)
}
// String implements TypeInfo interface.
func (ti *uintImpl) String() string {
switch ti.Width {
case UintWidth8:
func (ti *uintType) String() string {
switch ti.sqlUintType.Type() {
case sqltypes.Uint8:
return "Uint8"
case UintWidth16:
case sqltypes.Uint16:
return "Uint16"
case UintWidth24:
case sqltypes.Uint24:
return "Uint24"
case UintWidth32:
case sqltypes.Uint32:
return "Uint32"
case UintWidth64:
case sqltypes.Uint64:
return "Uint64"
default:
panic(fmt.Errorf(`uint width "%v" is not valid`, ti.Width))
panic(fmt.Errorf(`unknown uint type info sql type "%v"`, ti.sqlUintType.Type().String()))
}
}
// ToSqlType implements TypeInfo interface.
func (ti *uintImpl) ToSqlType() sql.Type {
switch ti.Width {
case UintWidth8:
return sql.Uint8
case UintWidth16:
return sql.Uint16
case UintWidth24:
return sql.Uint24
case UintWidth32:
return sql.Uint32
case UintWidth64:
return sql.Uint64
default:
panic(fmt.Errorf(`uint width "%v" is not valid`, ti.Width))
}
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *uintImpl) isValid(v interface{}) (artifact uint64, ok bool) {
var maxValue uint64
switch ti.Width {
case UintWidth8:
maxValue = math.MaxUint8
case UintWidth16:
maxValue = math.MaxUint16
case UintWidth24:
maxValue = MaxUint24
case UintWidth32:
maxValue = math.MaxUint32
case UintWidth64:
maxValue = math.MaxUint64
default:
panic(fmt.Errorf(`uint width "%v" is not valid`, ti.Width))
}
switch val := v.(type) {
case nil:
return 0, true
case bool:
return 0, true
case int:
return 0, val >= 0 && uint64(val) <= maxValue
case int8:
return 0, val >= 0
case int16:
return 0, val >= 0 && uint64(val) <= maxValue
case int32:
return 0, val >= 0 && uint64(val) <= maxValue
case int64:
return 0, val >= 0 && uint64(val) <= maxValue
case uint:
return 0, uint64(val) <= maxValue
case uint8:
return 0, uint64(val) <= maxValue
case uint16:
return 0, uint64(val) <= maxValue
case uint32:
return 0, uint64(val) <= maxValue
case uint64:
return 0, val <= maxValue
case float32:
return 0, val >= 0 && uint64(val) <= maxValue
case float64:
return 0, val >= 0 && uint64(val) <= maxValue
case string:
uintVal, err := strconv.ParseUint(val, 10, 64)
return uintVal, err == nil
case types.Null:
return 0, true
case types.Bool:
return 0, true
case types.Int:
return 0, int64(val) >= 0 && uint64(val) <= maxValue
case types.Uint:
return 0, uint64(val) <= maxValue
case types.Float:
return 0, val >= 0 && uint64(val) <= maxValue
case types.String:
uintVal, err := strconv.ParseUint(string(val), 10, 64)
return uintVal, err == nil
default:
return 0, false
}
func (ti *uintType) ToSqlType() sql.Type {
return ti.sqlUintType
}

View File

@@ -43,6 +43,11 @@ func (ti *unknownImpl) Equals(TypeInfo) bool {
return false
}
// FormatValue implements TypeInfo interface.
func (ti *unknownImpl) FormatValue(types.Value) (*string, error) {
return nil, fmt.Errorf(`"Unknown" cannot convert any Noms value to a string`)
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *unknownImpl) GetTypeIdentifier() Identifier {
return UnknownTypeIdentifier
@@ -50,11 +55,11 @@ func (ti *unknownImpl) GetTypeIdentifier() Identifier {
// GetTypeParams implements TypeInfo interface.
func (ti *unknownImpl) GetTypeParams() map[string]string {
return nil
panic("cannot persist unknown type")
}
// IsValid implements TypeInfo interface.
func (ti *unknownImpl) IsValid(v interface{}) bool {
func (ti *unknownImpl) IsValid(types.Value) bool {
return false
}
@@ -63,6 +68,11 @@ func (ti *unknownImpl) NomsKind() types.NomsKind {
return types.UnknownKind
}
// ParseValue implements TypeInfo interface.
func (ti *unknownImpl) ParseValue(*string) (types.Value, error) {
return nil, fmt.Errorf(`"Unknown" cannot convert any strings to a Noms value`)
}
// String implements TypeInfo interface.
func (ti *unknownImpl) String() string {
return "Unknown"

View File

@@ -24,14 +24,16 @@ import (
"github.com/liquidata-inc/dolt/go/store/types"
)
type uuidImpl struct{}
type uuidType struct {
sqlCharType sql.StringType
}
var _ TypeInfo = (*uuidImpl)(nil)
var _ TypeInfo = (*uuidType)(nil)
var UuidType TypeInfo = &uuidImpl{}
var UuidType = &uuidType{sql.MustCreateString(sqltypes.Char, 36, sql.Collation_ascii_bin)}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *uuidImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *uuidType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.UUID); ok {
return val.String(), nil
}
@@ -42,88 +44,83 @@ func (ti *uuidImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *uuidImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case string:
return types.UUID(artifact), nil
case types.Null:
return types.NullValue, nil
case uuid.UUID:
return types.UUID(val), nil
case types.String:
return types.UUID(artifact), nil
case types.UUID:
return val, nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
func (ti *uuidType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case string:
valUuid, err := uuid.Parse(val)
if err != nil {
return nil, err
}
return types.UUID(valUuid), err
case uuid.UUID:
return types.UUID(val), nil
default:
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *uuidImpl) Equals(other TypeInfo) bool {
func (ti *uuidType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*uuidImpl)
_, ok := other.(*uuidType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *uuidType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.UUID); ok {
res := val.String()
return &res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *uuidImpl) GetTypeIdentifier() Identifier {
func (ti *uuidType) GetTypeIdentifier() Identifier {
return UuidTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *uuidImpl) GetTypeParams() map[string]string {
func (ti *uuidType) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *uuidImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *uuidType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *uuidImpl) NomsKind() types.NomsKind {
func (ti *uuidType) NomsKind() types.NomsKind {
return types.UUIDKind
}
// ParseValue implements TypeInfo interface.
func (ti *uuidType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
uuidVal, err := uuid.Parse(*str)
if err != nil {
return nil, err
}
return types.UUID(uuidVal), nil
}
// String implements TypeInfo interface.
func (ti *uuidImpl) String() string {
func (ti *uuidType) String() string {
return "Uuid"
}
// ToSqlType implements TypeInfo interface.
func (ti *uuidImpl) ToSqlType() sql.Type {
return sql.MustCreateString(sqltypes.Char, 36, sql.Collation_ascii_bin)
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *uuidImpl) isValid(v interface{}) (artifact uuid.UUID, ok bool) {
switch val := v.(type) {
case nil:
return uuid.UUID{}, true
case string:
valUuid, err := uuid.Parse(val)
return valUuid, err == nil
case types.Null:
return uuid.UUID{}, true
case uuid.UUID:
return val, true
case types.String:
valUuid, err := uuid.Parse(string(val))
return valUuid, err == nil
case types.UUID:
return uuid.UUID(val), true
default:
return uuid.UUID{}, false
}
func (ti *uuidType) ToSqlType() sql.Type {
return ti.sqlCharType
}

View File

@@ -25,50 +25,59 @@ import (
)
const (
varBinaryTypeParam_Length = "length"
varBinaryTypeParam_PadBytes = "pad"
varBinaryTypeParam_IsSqlBlob = "blob"
varBinaryTypeParam_Length = "length"
varBinaryTypeParam_SQL = "sql"
varBinaryTypeParam_SQL_Binary = "bin"
varBinaryTypeParam_SQL_VarBinary = "varbin"
varBinaryTypeParam_SQL_Blob = "blob"
)
// As a type, this is modeled more after MySQL's story for binary data. There, it's treated
// as a string that is interpreted as raw bytes, rather than as a bespoke data structure,
// and thus this is mirrored here in its implementation. This will minimize any differences
// that could arise.
type varBinaryImpl struct {
MaxLength int64
PadBytes bool
IsSqlBlob bool // When converting to a SQL type, this ensures a distinction between BLOB and VARBINARY
type varBinaryType struct {
sqlBinaryType sql.StringType
}
var _ TypeInfo = (*varBinaryImpl)(nil)
var _ TypeInfo = (*varBinaryType)(nil)
func CreateVarBinaryTypeFromParams(params map[string]string) (TypeInfo, error) {
ti := &varBinaryImpl{}
var length int64
var err error
if lengthStr, ok := params[varBinaryTypeParam_Length]; ok {
ti.MaxLength, err = strconv.ParseInt(lengthStr, 10, 64)
length, err = strconv.ParseInt(lengthStr, 10, 64)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf(`create varbinary type info is missing param "%v"`, varBinaryTypeParam_Length)
}
if _, ok := params[varBinaryTypeParam_PadBytes]; ok {
ti.PadBytes = true
if sqlStr, ok := params[varBinaryTypeParam_SQL]; ok {
var sqlType sql.StringType
switch sqlStr {
case varBinaryTypeParam_SQL_Binary:
sqlType, err = sql.CreateBinary(sqltypes.Binary, length)
case varBinaryTypeParam_SQL_VarBinary:
sqlType, err = sql.CreateBinary(sqltypes.VarBinary, length)
case varBinaryTypeParam_SQL_Blob:
sqlType, err = sql.CreateBinary(sqltypes.Blob, length)
default:
return nil, fmt.Errorf(`create varbinary type info has "%v" param with value "%v"`, varBinaryTypeParam_SQL, sqlStr)
}
if err != nil {
return nil, err
}
return &varBinaryType{sqlType}, nil
} else {
return nil, fmt.Errorf(`create varbinary type info is missing param "%v"`, varBinaryTypeParam_SQL)
}
if _, ok := params[varBinaryTypeParam_IsSqlBlob]; ok {
ti.IsSqlBlob = true
}
return ti, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *varBinaryImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *varBinaryType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.String); ok {
if ti.PadBytes {
return ti.padBytes(string(val)), nil
}
return string(val), nil
return ti.sqlBinaryType.Convert(string(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -77,221 +86,117 @@ func (ti *varBinaryImpl) ConvertNomsValueToValue(v types.Value) (interface{}, er
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *varBinaryImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
if val {
return types.String("1"), nil
}
return types.String("0"), nil
case int:
return types.String(artifact), nil
case int8:
return types.String(artifact), nil
case int16:
return types.String(artifact), nil
case int32:
return types.String(artifact), nil
case int64:
return types.String(artifact), nil
case uint:
return types.String(artifact), nil
case uint8:
return types.String(artifact), nil
case uint16:
return types.String(artifact), nil
case uint32:
return types.String(artifact), nil
case uint64:
return types.String(artifact), nil
case float32:
return types.String(artifact), nil
case float64:
return types.String(artifact), nil
case string:
if ti.PadBytes {
return types.String(ti.padBytes(val)), nil
}
return types.String(val), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
if val {
return types.String("1"), nil
}
return types.String("0"), nil
case types.Int:
return types.String(artifact), nil
case types.Uint:
return types.String(artifact), nil
case types.Float:
return types.String(artifact), nil
case types.String:
if ti.PadBytes {
return types.String(ti.padBytes(string(val))), nil
}
return val, nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *varBinaryType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
strVal, err := ti.sqlBinaryType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *varBinaryImpl) Equals(other TypeInfo) bool {
func (ti *varBinaryType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*varBinaryImpl); ok {
return ti.MaxLength == ti2.MaxLength && ti.PadBytes == ti2.PadBytes
if ti2, ok := other.(*varBinaryType); ok {
return ti.sqlBinaryType.MaxCharacterLength() == ti2.sqlBinaryType.MaxCharacterLength() &&
ti.sqlBinaryType.Type() == ti2.sqlBinaryType.Type()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *varBinaryType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.String); ok {
res, err := ti.sqlBinaryType.Convert(string(val))
if err != nil {
return nil, err
}
if resStr, ok := res.(string); ok {
return &resStr, nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *varBinaryImpl) GetTypeIdentifier() Identifier {
func (ti *varBinaryType) GetTypeIdentifier() Identifier {
return VarBinaryTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *varBinaryImpl) GetTypeParams() map[string]string {
func (ti *varBinaryType) GetTypeParams() map[string]string {
typeParams := map[string]string{
varBinaryTypeParam_Length: strconv.FormatInt(ti.MaxLength, 10),
varBinaryTypeParam_Length: strconv.FormatInt(ti.sqlBinaryType.MaxCharacterLength(), 10),
}
if ti.PadBytes {
typeParams[varBinaryTypeParam_PadBytes] = ""
}
if ti.IsSqlBlob {
typeParams[varBinaryTypeParam_IsSqlBlob] = ""
switch ti.sqlBinaryType.Type() {
case sqltypes.Binary:
typeParams[varBinaryTypeParam_SQL] = varBinaryTypeParam_SQL_Binary
case sqltypes.VarBinary:
typeParams[varBinaryTypeParam_SQL] = varBinaryTypeParam_SQL_VarBinary
case sqltypes.Blob:
typeParams[varBinaryTypeParam_SQL] = varBinaryTypeParam_SQL_Blob
default:
panic(fmt.Errorf(`unknown varbinary type info sql type "%v"`, ti.sqlBinaryType.Type().String()))
}
return typeParams
}
// IsValid implements TypeInfo interface.
func (ti *varBinaryImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *varBinaryType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *varBinaryImpl) NomsKind() types.NomsKind {
func (ti *varBinaryType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *varBinaryType) ParseValue(str *string) (types.Value, error) {
if str == nil {
return types.NullValue, nil
}
strVal, err := ti.sqlBinaryType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := strVal.(string); ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *varBinaryImpl) String() string {
additionalText := ""
if ti.PadBytes {
additionalText = ", PadBytes"
func (ti *varBinaryType) String() string {
sqlType := ""
switch ti.sqlBinaryType.Type() {
case sqltypes.Binary:
sqlType = "Binary"
case sqltypes.VarBinary:
sqlType = "VarBinary"
case sqltypes.Blob:
sqlType = "Blob"
default:
panic(fmt.Errorf(`unknown varbinary type info sql type "%v"`, ti.sqlBinaryType.Type().String()))
}
if ti.IsSqlBlob {
additionalText = ", SqlBlob"
}
return fmt.Sprintf(`VarBinary(%v%v)`, ti.MaxLength, additionalText)
return fmt.Sprintf(`VarBinary(%v, SQL: %v)`, ti.sqlBinaryType.MaxCharacterLength(), sqlType)
}
// ToSqlType implements TypeInfo interface.
func (ti *varBinaryImpl) ToSqlType() sql.Type {
// Binary is the only type that pads bytes.
if ti.PadBytes {
sqlType, err := sql.CreateBinary(sqltypes.Binary, ti.MaxLength)
if err == nil {
return sqlType
}
}
if !ti.IsSqlBlob {
sqlType, err := sql.CreateBinary(sqltypes.VarBinary, ti.MaxLength)
if err == nil {
return sqlType
}
}
// The SQL type has a max character limit
maxLength := ti.MaxLength
if maxLength > sql.LongBlob.MaxCharacterLength() {
maxLength = sql.LongBlob.MaxCharacterLength()
}
sqlType, err := sql.CreateBinary(sqltypes.Blob, maxLength)
if err != nil {
panic(err)
}
return sqlType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *varBinaryImpl) isValid(v interface{}) (artifact string, ok bool) {
switch val := v.(type) {
case nil:
return "", true
case bool:
return "", ti.MaxLength >= 1
case int:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int8:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int16:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int32:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int64:
strVal := strconv.FormatInt(val, 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint8:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint16:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint32:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint64:
strVal := strconv.FormatUint(val, 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case float32:
strVal := strconv.FormatFloat(float64(val), 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case float64:
strVal := strconv.FormatFloat(val, 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case string:
return "", int64(len(val)) <= ti.MaxLength
case types.Null:
return "", true
case types.Bool:
return "", ti.MaxLength >= 1
case types.Int:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.Uint:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.Float:
strVal := strconv.FormatFloat(float64(val), 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.String:
return "", int64(len(val)) <= ti.MaxLength
default:
return "", false
}
}
// padBytes pads a string with zero bytes if the string length is less than the max length.
func (ti *varBinaryImpl) padBytes(v string) string {
if int64(len(v)) < ti.MaxLength {
return string(append([]byte(v), make([]byte, ti.MaxLength-int64(len(v)))...))
}
return v
func (ti *varBinaryType) ToSqlType() sql.Type {
return ti.sqlBinaryType
}

View File

@@ -16,9 +16,9 @@ package typeinfo
import (
"fmt"
"math"
"strconv"
"strings"
"unicode"
"github.com/src-d/go-mysql-server/sql"
"vitess.io/vitess/go/sqltypes"
@@ -27,32 +27,27 @@ import (
)
const (
varStringTypeParam_Collate = "collate"
varStringTypeParam_Length = "length"
varStringTypeParam_RemoveTrailingSpaces = "rts"
varStringTypeParam_IsSqlText = "text"
varStringTypeParam_Collate = "collate"
varStringTypeParam_Length = "length"
varStringTypeParam_SQL = "sql"
varStringTypeParam_SQL_Char = "char"
varStringTypeParam_SQL_VarChar = "varchar"
varStringTypeParam_SQL_Text = "text"
)
type varStringImpl struct {
Collation sql.Collation
MaxLength int64
RemoveTrailingSpaces bool
IsSqlText bool // When converting to a SQL type, this ensures a distinction between TEXT and VARCHAR
type varStringType struct {
sqlStringType sql.StringType
}
var _ TypeInfo = (*varStringImpl)(nil)
var StringDefaultType TypeInfo = &varStringImpl{
sql.Collation_Default,
math.MaxUint32,
false,
true,
}
var _ TypeInfo = (*varStringType)(nil)
var StringDefaultType = &varStringType{sql.CreateLongText(sql.Collation_Default)}
func CreateVarStringTypeFromParams(params map[string]string) (TypeInfo, error) {
ti := &varStringImpl{}
var length int64
var collation sql.Collation
var err error
if collationStr, ok := params[varStringTypeParam_Collate]; ok {
ti.Collation, err = sql.ParseCollation(nil, &collationStr, false)
collation, err = sql.ParseCollation(nil, &collationStr, false)
if err != nil {
return nil, err
}
@@ -60,26 +55,46 @@ func CreateVarStringTypeFromParams(params map[string]string) (TypeInfo, error) {
return nil, fmt.Errorf(`create varstring type info is missing param "%v"`, varStringTypeParam_Collate)
}
if maxLengthStr, ok := params[varStringTypeParam_Length]; ok {
ti.MaxLength, err = strconv.ParseInt(maxLengthStr, 10, 64)
length, err = strconv.ParseInt(maxLengthStr, 10, 64)
} else {
return nil, fmt.Errorf(`create varstring type info is missing param "%v"`, varStringTypeParam_Length)
}
if _, ok := params[varStringTypeParam_RemoveTrailingSpaces]; ok {
ti.RemoveTrailingSpaces = true
if sqlStr, ok := params[varStringTypeParam_SQL]; ok {
var sqlType sql.StringType
switch sqlStr {
case varStringTypeParam_SQL_Char:
sqlType, err = sql.CreateString(sqltypes.Char, length, collation)
case varStringTypeParam_SQL_VarChar:
sqlType, err = sql.CreateString(sqltypes.VarChar, length, collation)
case varStringTypeParam_SQL_Text:
sqlType, err = sql.CreateString(sqltypes.Text, length, collation)
default:
return nil, fmt.Errorf(`create varstring type info has "%v" param with value "%v"`, varStringTypeParam_SQL, sqlStr)
}
if err != nil {
return nil, err
}
return &varStringType{sqlType}, nil
} else {
return nil, fmt.Errorf(`create varstring type info is missing param "%v"`, varStringTypeParam_Length)
}
if _, ok := params[varStringTypeParam_IsSqlText]; ok {
ti.IsSqlText = true
}
return ti, nil
}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *varStringImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *varStringType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.String); ok {
if ti.RemoveTrailingSpaces {
return strings.TrimRight(string(val), " "), nil
strVal, err := ti.sqlStringType.Convert(string(val))
if err != nil {
return nil, err
}
return string(val), nil
res, ok := strVal.(string)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
if ti.sqlStringType.Type() == sqltypes.Char {
res = strings.TrimRightFunc(res, unicode.IsSpace)
}
return res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -88,215 +103,112 @@ func (ti *varStringImpl) ConvertNomsValueToValue(v types.Value) (interface{}, er
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *varStringImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch val := v.(type) {
case nil:
return types.NullValue, nil
case bool:
if val {
return types.String("1"), nil
}
return types.String("0"), nil
case int:
return types.String(artifact), nil
case int8:
return types.String(artifact), nil
case int16:
return types.String(artifact), nil
case int32:
return types.String(artifact), nil
case int64:
return types.String(artifact), nil
case uint:
return types.String(artifact), nil
case uint8:
return types.String(artifact), nil
case uint16:
return types.String(artifact), nil
case uint32:
return types.String(artifact), nil
case uint64:
return types.String(artifact), nil
case float32:
return types.String(artifact), nil
case float64:
return types.String(artifact), nil
case string:
if ti.RemoveTrailingSpaces {
return types.String(strings.TrimRight(val, " ")), nil
}
return types.String(val), nil
case types.Null:
return types.NullValue, nil
case types.Bool:
if val {
return types.String("1"), nil
}
return types.String("0"), nil
case types.Int:
return types.String(artifact), nil
case types.Uint:
return types.String(artifact), nil
case types.Float:
return types.String(artifact), nil
case types.String:
if ti.RemoveTrailingSpaces {
return types.String(strings.TrimRight(string(val), " ")), nil
}
return val, nil
default:
return nil, fmt.Errorf(`"%v" has falsely evaluated value "%v" of type "%T" as valid`, ti.String(), val, val)
}
func (ti *varStringType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
strVal, err := ti.sqlStringType.Convert(v)
if err != nil {
return nil, err
}
val, ok := strVal.(string)
if ok {
return types.String(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
}
// Equals implements TypeInfo interface.
func (ti *varStringImpl) Equals(other TypeInfo) bool {
func (ti *varStringType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
if ti2, ok := other.(*varStringImpl); ok {
return *ti == *ti2
if ti2, ok := other.(*varStringType); ok {
return ti.sqlStringType.MaxCharacterLength() == ti2.sqlStringType.MaxCharacterLength() &&
ti.sqlStringType.Type() == ti2.sqlStringType.Type() &&
ti.sqlStringType.Collation() == ti2.sqlStringType.Collation()
}
return false
}
// FormatValue implements TypeInfo interface.
func (ti *varStringType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.String); ok {
res, err := ti.ConvertNomsValueToValue(val)
if err != nil {
return nil, err
}
if resStr, ok := res.(string); ok {
return &resStr, nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *varStringImpl) GetTypeIdentifier() Identifier {
func (ti *varStringType) GetTypeIdentifier() Identifier {
return VarStringTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *varStringImpl) GetTypeParams() map[string]string {
func (ti *varStringType) GetTypeParams() map[string]string {
typeParams := map[string]string{
varStringTypeParam_Collate: ti.Collation.String(),
varStringTypeParam_Length: strconv.FormatInt(ti.MaxLength, 10),
varStringTypeParam_Collate: ti.sqlStringType.Collation().String(),
varStringTypeParam_Length: strconv.FormatInt(ti.sqlStringType.MaxCharacterLength(), 10),
}
if ti.RemoveTrailingSpaces {
typeParams[varStringTypeParam_RemoveTrailingSpaces] = ""
}
if ti.IsSqlText {
typeParams[varStringTypeParam_IsSqlText] = ""
switch ti.sqlStringType.Type() {
case sqltypes.Char:
typeParams[varStringTypeParam_SQL] = varStringTypeParam_SQL_Char
case sqltypes.VarChar:
typeParams[varStringTypeParam_SQL] = varStringTypeParam_SQL_VarChar
case sqltypes.Text:
typeParams[varStringTypeParam_SQL] = varStringTypeParam_SQL_Text
default:
panic(fmt.Errorf(`unknown varstring type info sql type "%v"`, ti.sqlStringType.Type().String()))
}
return typeParams
}
// IsValid implements TypeInfo interface.
func (ti *varStringImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *varStringType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *varStringImpl) NomsKind() types.NomsKind {
func (ti *varStringType) NomsKind() types.NomsKind {
return types.StringKind
}
// ParseValue implements TypeInfo interface.
func (ti *varStringType) ParseValue(str *string) (types.Value, error) {
if str == nil {
return types.NullValue, nil
}
return ti.ConvertValueToNomsValue(*str)
}
// String implements TypeInfo interface.
func (ti *varStringImpl) String() string {
additionalText := ""
if ti.RemoveTrailingSpaces {
additionalText = ", RemoveTrailingSpaces"
func (ti *varStringType) String() string {
sqlType := ""
switch ti.sqlStringType.Type() {
case sqltypes.Char:
sqlType = "Char"
case sqltypes.VarChar:
sqlType = "VarChar"
case sqltypes.Text:
sqlType = "Text"
default:
panic(fmt.Errorf(`unknown varstring type info sql type "%v"`, ti.sqlStringType.Type().String()))
}
if ti.IsSqlText {
additionalText = ", SqlText"
}
return fmt.Sprintf(`VarString(%v, %v%v)`, ti.Collation.String(), ti.MaxLength, additionalText)
return fmt.Sprintf(`VarString(%v, %v, SQL: %v)`, ti.sqlStringType.Collation().String(), ti.sqlStringType.MaxCharacterLength(), sqlType)
}
// ToSqlType implements TypeInfo interface.
func (ti *varStringImpl) ToSqlType() sql.Type {
// Char is the only type that removes trailing spaces
if ti.RemoveTrailingSpaces {
sqlType, err := sql.CreateString(sqltypes.Char, ti.MaxLength, ti.Collation)
if err == nil {
return sqlType
}
}
if !ti.IsSqlText {
sqlType, err := sql.CreateString(sqltypes.VarChar, ti.MaxLength, ti.Collation)
if err == nil {
return sqlType
}
}
// The SQL type has a max character limit
maxLength := ti.MaxLength
if maxLength > sql.LongText.MaxCharacterLength() {
maxLength = sql.LongText.MaxCharacterLength()
}
sqlType, err := sql.CreateString(sqltypes.Text, maxLength, ti.Collation)
if err != nil {
panic(err)
}
return sqlType
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *varStringImpl) isValid(v interface{}) (artifact string, ok bool) {
//TODO: handle collations
switch val := v.(type) {
case nil:
return "", true
case bool:
return "", ti.MaxLength >= 1
case int:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int8:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int16:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int32:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case int64:
strVal := strconv.FormatInt(val, 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint8:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint16:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint32:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case uint64:
strVal := strconv.FormatUint(val, 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case float32:
strVal := strconv.FormatFloat(float64(val), 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case float64:
strVal := strconv.FormatFloat(val, 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case string:
return "", int64(len(val)) <= ti.MaxLength
case types.Null:
return "", true
case types.Bool:
return "", ti.MaxLength >= 1
case types.Int:
strVal := strconv.FormatInt(int64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.Uint:
strVal := strconv.FormatUint(uint64(val), 10)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.Float:
strVal := strconv.FormatFloat(float64(val), 'g', -1, 64)
return strVal, int64(len(strVal)) <= ti.MaxLength
case types.String:
return "", int64(len(val)) <= ti.MaxLength
default:
return "", false
}
func (ti *varStringType) ToSqlType() sql.Type {
return ti.sqlStringType
}

View File

@@ -16,6 +16,7 @@ package typeinfo
import (
"fmt"
"strconv"
"github.com/src-d/go-mysql-server/sql"
@@ -24,20 +25,18 @@ import (
// This is a dolt implementation of the MySQL type Year, thus most of the functionality
// within is directly reliant on the go-mysql-server implementation.
type yearImpl struct{}
type yearType struct {
sqlYearType sql.YearType
}
var _ TypeInfo = (*yearImpl)(nil)
var _ TypeInfo = (*yearType)(nil)
var YearType TypeInfo = &yearImpl{}
var YearType = &yearType{sql.Year}
// ConvertNomsValueToValue implements TypeInfo interface.
func (ti *yearImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
func (ti *yearType) ConvertNomsValueToValue(v types.Value) (interface{}, error) {
if val, ok := v.(types.Int); ok {
res, err := sql.Year.Convert(int16(val))
if err != nil {
return nil, fmt.Errorf(`"%v" cannot convert year "%v" to value`, ti.String(), val)
}
return res, nil
return ti.sqlYearType.Convert(int64(val))
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
@@ -46,79 +45,92 @@ func (ti *yearImpl) ConvertNomsValueToValue(v types.Value) (interface{}, error)
}
// ConvertValueToNomsValue implements TypeInfo interface.
func (ti *yearImpl) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if artifact, ok := ti.isValid(v); ok {
switch v.(type) {
case nil, types.Null:
return types.NullValue, nil
}
return types.Int(artifact), nil
func (ti *yearType) ConvertValueToNomsValue(v interface{}) (types.Value, error) {
if v == nil {
return types.NullValue, nil
}
return nil, fmt.Errorf(`"%v" cannot convert value "%v" of type "%T" as it is invalid`, ti.String(), v, v)
intVal, err := ti.sqlYearType.Convert(v)
if err != nil {
return nil, err
}
val, ok := intVal.(int16)
if ok {
return types.Int(val), nil
}
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
// Equals implements TypeInfo interface.
func (ti *yearImpl) Equals(other TypeInfo) bool {
func (ti *yearType) Equals(other TypeInfo) bool {
if other == nil {
return false
}
_, ok := other.(*yearImpl)
_, ok := other.(*yearType)
return ok
}
// FormatValue implements TypeInfo interface.
func (ti *yearType) FormatValue(v types.Value) (*string, error) {
if val, ok := v.(types.Int); ok {
convVal, err := ti.ConvertNomsValueToValue(val)
if err != nil {
return nil, err
}
val, ok := convVal.(int16)
if !ok {
return nil, fmt.Errorf(`"%v" has unexpectedly encountered a value of type "%T" from embedded type`, ti.String(), v)
}
res := strconv.FormatInt(int64(val), 10)
return &res, nil
}
if _, ok := v.(types.Null); ok || v == nil {
return nil, nil
}
return nil, fmt.Errorf(`"%v" cannot convert NomsKind "%v" to a string`, ti.String(), v.Kind())
}
// GetTypeIdentifier implements TypeInfo interface.
func (ti *yearImpl) GetTypeIdentifier() Identifier {
func (ti *yearType) GetTypeIdentifier() Identifier {
return YearTypeIdentifier
}
// GetTypeParams implements TypeInfo interface.
func (ti *yearImpl) GetTypeParams() map[string]string {
func (ti *yearType) GetTypeParams() map[string]string {
return nil
}
// IsValid implements TypeInfo interface.
func (ti *yearImpl) IsValid(v interface{}) bool {
_, ok := ti.isValid(v)
return ok
func (ti *yearType) IsValid(v types.Value) bool {
_, err := ti.ConvertNomsValueToValue(v)
return err == nil
}
// NomsKind implements TypeInfo interface.
func (ti *yearImpl) NomsKind() types.NomsKind {
func (ti *yearType) NomsKind() types.NomsKind {
return types.IntKind
}
// ParseValue implements TypeInfo interface.
func (ti *yearType) ParseValue(str *string) (types.Value, error) {
if str == nil || *str == "" {
return types.NullValue, nil
}
intVal, err := ti.sqlYearType.Convert(*str)
if err != nil {
return nil, err
}
if val, ok := intVal.(int16); ok {
return types.Int(val), nil
}
return nil, fmt.Errorf(`"%v" cannot convert the string "%v" to a value`, ti.String(), str)
}
// String implements TypeInfo interface.
func (ti *yearImpl) String() string {
func (ti *yearType) String() string {
return "Year"
}
// ToSqlType implements TypeInfo interface.
func (ti *yearImpl) ToSqlType() sql.Type {
return sql.Year
}
// isValid is an internal implementation for the TypeInfo interface function IsValid.
// Some validity checks process the value into its final form, which may be returned
// as an artifact so that a value doesn't need to be processed twice in some scenarios.
func (ti *yearImpl) isValid(v interface{}) (artifact int16, ok bool) {
// convert some Noms values to their standard golang equivalents, except Null
switch val := v.(type) {
case nil:
return 0, true
case types.Null:
return 0, true
case types.Bool:
v = bool(val)
case types.Int:
v = int64(val)
case types.Uint:
v = uint64(val)
case types.Float:
v = float64(val)
case types.String:
v = string(val)
}
res, err := sql.Year.Convert(v)
resInt, ok := res.(int16)
return resInt, err == nil && ok
func (ti *yearType) ToSqlType() sql.Type {
return ti.sqlYearType
}

View File

@@ -18,11 +18,12 @@ import (
"fmt"
"strings"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/store/types"
"github.com/google/uuid"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/store/types"
)
const doubleQuot = `"`
@@ -283,25 +284,16 @@ func valueAsSqlString(value types.Value) (string, error) {
return "FALSE", nil
}
case types.UUIDKind:
convFn, err := doltcore.GetConvFunc(value.Kind(), types.StringKind)
if err != nil {
return "", err
}
str, _ := convFn(value)
return doubleQuot + string(str.(types.String)) + doubleQuot, nil
return doubleQuot + uuid.UUID(value.(types.UUID)).String() + doubleQuot, nil
case types.StringKind:
s := string(value.(types.String))
s = strings.ReplaceAll(s, doubleQuot, "\\\"")
return doubleQuot + s + doubleQuot, nil
default:
convFn, err := doltcore.GetConvFunc(value.Kind(), types.StringKind)
str, err := typeinfo.FromKind(value.Kind()).FormatValue(value)
if err != nil {
return "", err
}
str, err := convFn(value)
if err != nil {
return "", err
}
return string(str.(types.String)), nil
return *str, nil
}
}

View File

@@ -1,130 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"github.com/liquidata-inc/dolt/go/store/types"
)
var DoltToSQLType = map[types.NomsKind]string{
types.StringKind: VARCHAR + "(1024)", // we don't actually enforce string lengths, but mysql requires them
types.BoolKind: BOOL,
types.FloatKind: FLOAT_TYPE,
types.IntKind: INT,
types.UintKind: INT + " " + UNSIGNED,
types.UUIDKind: UUID,
}
// TypeConversionFn is a function that converts one noms value to another of a different type in a guaranteed fashion,
// i.e. any conversion that could possibly fail (e.g. string -> int) are not allowed. Only SQL-safe conversions are
// allowed, even if they are guaranteed to be safe, so that e.g. there is no way to convert a numeric type to a string.
// These kinds of conversions must be explicit in SQL.
type TypeConversionFn func(types.Value) types.Value
var convFuncMap = map[types.NomsKind]map[types.NomsKind]TypeConversionFn{
types.StringKind: {
types.StringKind: identityConvFunc,
types.NullKind: convToNullFunc,
},
types.UUIDKind: {
types.UUIDKind: identityConvFunc,
types.NullKind: convToNullFunc,
},
types.UintKind: {
types.UintKind: identityConvFunc,
types.IntKind: convUintToInt,
types.FloatKind: convUintToFloat,
types.NullKind: convToNullFunc,
},
types.IntKind: {
types.UintKind: convIntToUint,
types.IntKind: identityConvFunc,
types.FloatKind: convIntToFloat,
types.NullKind: convToNullFunc,
},
types.FloatKind: {
types.FloatKind: identityConvFunc,
types.NullKind: convToNullFunc,
},
types.BoolKind: {
types.BoolKind: identityConvFunc,
types.NullKind: convToNullFunc,
},
types.NullKind: {
types.StringKind: convToNullFunc,
types.UUIDKind: convToNullFunc,
types.UintKind: convToNullFunc,
types.IntKind: convToNullFunc,
types.FloatKind: convToNullFunc,
types.BoolKind: convToNullFunc,
types.NullKind: convToNullFunc,
},
}
// GetTypeConversionFn takes in a source kind and a destination kind and returns a TypeConversionFn which can convert
// values of the source kind to values of the destination kind in a type-safe manner, or nil if no such conversion is
// possible.
func GetTypeConversionFn(srcKind, destKind types.NomsKind) TypeConversionFn {
var convFunc TypeConversionFn
if destKindMap, ok := convFuncMap[srcKind]; ok {
convFunc = destKindMap[destKind]
}
return convFunc
}
func identityConvFunc(value types.Value) types.Value {
return value
}
var convToNullFunc = func(types.Value) types.Value {
return nil
}
func convUintToInt(val types.Value) types.Value {
if val == nil {
return nil
}
n := uint64(val.(types.Uint))
return types.Int(int64(n))
}
func convUintToFloat(val types.Value) types.Value {
if val == nil {
return nil
}
n := uint64(val.(types.Uint))
return types.Float(float64(n))
}
func convIntToUint(val types.Value) types.Value {
if val == nil {
return nil
}
n := int64(val.(types.Int))
return types.Uint(uint64(n))
}
func convIntToFloat(val types.Value) types.Value {
if val == nil {
return nil
}
n := int64(val.(types.Int))
return types.Float(float64(n))
}

View File

@@ -121,15 +121,11 @@ func TestCreateTable(t *testing.T) {
c10 tinytext comment 'tag:10',
c11 mediumtext comment 'tag:11',
c12 longtext comment 'tag:12',
c13 tinyblob comment 'tag:13',
c14 blob comment 'tag:14',
c15 longblob comment 'tag:15',
c16 char(5) comment 'tag:16',
c17 varchar(255) comment 'tag:17',
c18 varchar(80) comment 'tag:18',
c19 float comment 'tag:19',
c20 double comment 'tag:20',
c21 decimal(10,5) comment 'tag:21',
c22 int unsigned comment 'tag:22',
c23 tinyint unsigned comment 'tag:23',
c24 smallint unsigned comment 'tag:24',
@@ -149,15 +145,15 @@ func TestCreateTable(t *testing.T) {
schemaNewColumn(t, "c10", 10, sql.TinyText, false),
schemaNewColumn(t, "c11", 11, sql.MediumText, false),
schemaNewColumn(t, "c12", 12, sql.LongText, false),
schemaNewColumn(t, "c13", 13, sql.TinyBlob, false),
schemaNewColumn(t, "c14", 14, sql.Blob, false),
schemaNewColumn(t, "c15", 15, sql.LongBlob, false),
//schemaNewColumn(t, "c13", 13, sql.TinyBlob, false),
//schemaNewColumn(t, "c14", 14, sql.Blob, false),
//schemaNewColumn(t, "c15", 15, sql.LongBlob, false),
schemaNewColumn(t, "c16", 16, sql.MustCreateStringWithDefaults(sqltypes.Char, 5), false),
schemaNewColumn(t, "c17", 17, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 255), false),
schemaNewColumn(t, "c18", 18, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schemaNewColumn(t, "c19", 19, sql.Float32, false),
schemaNewColumn(t, "c20", 20, sql.Float64, false),
schemaNewColumn(t, "c21", 21, sql.MustCreateDecimalType(10, 5), false),
//schemaNewColumn(t, "c21", 21, sql.MustCreateDecimalType(10, 5), false),
schemaNewColumn(t, "c22", 22, sql.Uint32, false),
schemaNewColumn(t, "c23", 23, sql.Uint8, false),
schemaNewColumn(t, "c24", 24, sql.Uint16, false),
@@ -501,8 +497,6 @@ func TestAddColumn(t *testing.T) {
}
func TestModifyAndChangeColumn(t *testing.T) {
// This worked previously as LONGTEXT & VARCHAR(80) are both LONGTEXT to dolt, so it thought there was no change
t.Skip("We don't yet support column type changes")
tests := []struct {
name string
query string
@@ -512,11 +506,11 @@ func TestModifyAndChangeColumn(t *testing.T) {
}{
{
name: "alter modify column reorder middle",
query: "alter table people modify column first_name varchar(80) not null after last_name",
query: "alter table people modify column first_name longtext not null after last_name",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schemaNewColumn(t, "first_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("rating", RatingTag, types.FloatKind, false),
@@ -527,9 +521,9 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter modify column reorder first",
query: "alter table people modify column first_name varchar(80) not null first",
query: "alter table people modify column first_name longtext not null first",
expectedSchema: dtestutils.CreateSchema(
schemaNewColumn(t, "first_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
@@ -542,10 +536,10 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter modify column drop null constraint",
query: "alter table people modify column first_name varchar(80) null",
query: "alter table people modify column first_name longtext null",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schemaNewColumn(t, "first_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
@@ -557,11 +551,11 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter change column rename and reorder",
query: "alter table people change first_name christian_name varchar(80) not null after last_name",
query: "alter table people change first_name christian_name longtext not null after last_name",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schemaNewColumn(t, "christian_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}),
schema.NewColumn("christian_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
schema.NewColumn("rating", RatingTag, types.FloatKind, false),
@@ -572,9 +566,9 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter change column rename and reorder first",
query: "alter table people change column first_name christian_name varchar(80) not null first",
query: "alter table people change column first_name christian_name longtext not null first",
expectedSchema: dtestutils.CreateSchema(
schemaNewColumn(t, "christian_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false, schema.NotNullConstraint{}),
schema.NewColumn("christian_name", FirstNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
@@ -587,10 +581,10 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter change column drop null constraint",
query: "alter table people change column first_name first_name varchar(80) null",
query: "alter table people change column first_name first_name longtext null",
expectedSchema: dtestutils.CreateSchema(
schema.NewColumn("id", IdTag, types.IntKind, true, schema.NotNullConstraint{}),
schemaNewColumn(t, "first_name", FirstNameTag, sql.MustCreateStringWithDefaults(sqltypes.VarChar, 80), false),
schema.NewColumn("first_name", FirstNameTag, types.StringKind, false),
schema.NewColumn("last_name", LastNameTag, types.StringKind, false, schema.NotNullConstraint{}),
schema.NewColumn("is_married", IsMarriedTag, types.BoolKind, false),
schema.NewColumn("age", AgeTag, types.IntKind, false),
@@ -602,17 +596,17 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter modify column change tag",
query: "alter table people modify column first_name varchar(80) not null comment 'tag:100'",
query: "alter table people modify column first_name longtext not null comment 'tag:100'",
expectedErr: "A column with the name first_name already exists",
},
{
name: "alter modify column not null with type mismatch in default",
query: "alter table people modify rating float default 'not a number'",
query: "alter table people modify rating double default 'not a number'",
expectedErr: "incompatible type for default value",
},
{
name: "alter modify column with tag conflict",
query: "alter table people modify rating float default 1.0 comment 'tag:1'",
query: "alter table people modify rating double default 1.0 comment 'tag:1'",
expectedErr: "A column with the name rating already exists",
},
{
@@ -622,7 +616,7 @@ func TestModifyAndChangeColumn(t *testing.T) {
},
{
name: "alter modify column not null, existing null values",
query: "alter table people modify num_episodes int unsigned not null",
query: "alter table people modify num_episodes bigint unsigned not null",
expectedErr: "cannot change column to NOT NULL",
},
}

View File

@@ -1,37 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doltcore
import (
"errors"
"github.com/liquidata-inc/dolt/go/store/types"
)
// StringToValue takes a string and a NomsKind and tries to convert the string to a noms Value.
func StringToValue(s string, kind types.NomsKind) (types.Value, error) {
if !types.IsPrimitiveKind(kind) || kind == types.BlobKind {
return nil, errors.New("Only primitive type support")
}
emptyStringType := types.KindToType[types.StringKind]
marshalFunc, err := emptyStringType.GetMarshalFunc(kind)
if err != nil {
panic("Unsupported type " + kind.String())
}
return marshalFunc(types.String(s))
}

View File

@@ -1,84 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doltcore
import (
"testing"
"github.com/liquidata-inc/dolt/go/store/types"
)
type strToNomsTypeTests struct {
s string
k types.NomsKind
expVal types.Value
expErr bool
}
var tests = []strToNomsTypeTests{
{"test string", types.StringKind, types.String("test string"), false},
{"1.3294398", types.FloatKind, types.Float(1.3294398), false},
{"-3294398", types.FloatKind, types.Float(-3294398), false},
{"TRuE", types.BoolKind, types.Bool(true), false},
{"False", types.BoolKind, types.Bool(false), false},
{"-123456", types.IntKind, types.Int(-123456), false},
{"123456", types.IntKind, types.Int(123456), false},
{"0.123", types.IntKind, types.Int(0), false},
{".123", types.IntKind, types.Int(0), false},
{"-0.123", types.IntKind, types.Int(0), false},
{"-.123", types.IntKind, types.Int(0), false},
{"100000000000", types.UintKind, types.Uint(100000000000), false},
{"0", types.UintKind, types.Uint(0), false},
{
"01234567-89ab-cdef-FEDC-BA9876543210",
types.UUIDKind,
types.UUID([16]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}),
false},
{"0", types.UintKind, types.Uint(0), false},
{"", types.NullKind, types.NullValue, false},
{
"61626364656667",
types.InlineBlobKind,
types.InlineBlob([]byte{0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67}),
false},
{
"61D184E19083F09D95AB",
types.InlineBlobKind,
types.InlineBlob([]byte{0x61, 0xd1, 0x84, 0xe1, 0x90, 0x83, 0xf0, 0x9d, 0x95, 0xab}),
false},
{"test failure", types.FloatKind, nil, true},
{"test failure", types.BoolKind, nil, true},
{"test failure", types.IntKind, nil, true},
{"0xdeadbeef", types.IntKind, nil, true},
{"test failure", types.UintKind, nil, true},
{"-1", types.UintKind, nil, true},
{"0123456789abcdeffedcba9876543210abc", types.UUIDKind, nil, true},
{"0", types.UUIDKind, nil, true},
}
func TestStrConversion(t *testing.T) {
for _, test := range tests {
val, err := StringToValue(test.s, test.k)
if (err != nil) != test.expErr {
t.Errorf("Conversion of \"%s\" returned unexpected error: %v", test.s, err)
}
if err == nil && !val.Equals(test.expVal) {
t.Errorf("Conversion of \"%s\" returned unexpected error: %v", test.s, err)
}
}
}

View File

@@ -22,7 +22,6 @@ import (
"github.com/bcicen/jstream"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/encoding"
@@ -146,31 +145,9 @@ func (r *JSONReader) convToRow(rowMap map[string]interface{}) (row.Row, error) {
return nil, fmt.Errorf("column %s not found in schema", k)
}
switch val := v.(type) {
case int:
f, err := doltcore.GetConvFunc(types.IntKind, col.Kind)
if err != nil {
return nil, err
}
taggedVals[col.Tag], _ = f(types.Int(val))
case string:
f, err := doltcore.GetConvFunc(types.StringKind, col.Kind)
if err != nil {
return nil, err
}
taggedVals[col.Tag], _ = f(types.String(val))
case bool:
f, err := doltcore.GetConvFunc(types.BoolKind, col.Kind)
if err != nil {
return nil, err
}
taggedVals[col.Tag], _ = f(types.Bool(val))
case float64:
f, err := doltcore.GetConvFunc(types.FloatKind, col.Kind)
if err != nil {
return nil, err
}
taggedVals[col.Tag], _ = f(types.Float(val))
switch v.(type) {
case int, string, bool, float64:
taggedVals[col.Tag], _ = col.TypeInfo.ConvertValueToNomsValue(v)
}
}

View File

@@ -20,7 +20,6 @@ import (
"github.com/tealeg/xlsx"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema"
"github.com/liquidata-inc/dolt/go/store/types"
@@ -62,7 +61,7 @@ func decodeXLSXRows(nbf *types.NomsBinFormat, xlData [][][]string, sch schema.Sc
return nil, errors.New(v + "is not a valid column")
}
valString := dataVals[i+1][k]
taggedVals[col.Tag], err = doltcore.StringToValue(valString, col.Kind)
taggedVals[col.Tag], err = col.TypeInfo.ParseValue(&valString)
if err != nil {
return nil, err
}

View File

@@ -21,8 +21,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/liquidata-inc/dolt/go/libraries/doltcore"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/row"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/table/untyped"
"github.com/liquidata-inc/dolt/go/store/types"
)
@@ -43,10 +43,14 @@ func TestDecodeXLSXRows(t *testing.T) {
}
taggedVals := make(row.TaggedValues, sch.GetAllCols().Size())
taggedVals[uint64(0)], _ = doltcore.StringToValue("1", types.StringKind)
taggedVals[uint64(1)], _ = doltcore.StringToValue("osheiza", types.StringKind)
taggedVals[uint64(2)], _ = doltcore.StringToValue("otori", types.StringKind)
taggedVals[uint64(3)], _ = doltcore.StringToValue("24", types.StringKind)
str := "1"
taggedVals[uint64(0)], _ = typeinfo.StringDefaultType.ParseValue(&str)
str = "osheiza"
taggedVals[uint64(1)], _ = typeinfo.StringDefaultType.ParseValue(&str)
str = "otori"
taggedVals[uint64(2)], _ = typeinfo.StringDefaultType.ParseValue(&str)
str = "24"
taggedVals[uint64(3)], _ = typeinfo.StringDefaultType.ParseValue(&str)
newRow, err := row.New(types.Format_7_18, sch, taggedVals)

View File

@@ -1,30 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doltcore
import (
"fmt"
"github.com/liquidata-inc/dolt/go/store/types"
)
// GetConvFunc takes in a source kind and a destination kind and returns a MarshalCallback which can convert values of the
// source kind to values of the destination kind.
func GetConvFunc(srcKind, destKind types.NomsKind) (types.MarshalCallback, error) {
if emptyVal, ok := types.KindToType[srcKind]; ok && emptyVal != nil {
return emptyVal.GetMarshalFunc(destKind)
}
return nil, fmt.Errorf("%v does not have an associated Value implementation", srcKind)
}

View File

@@ -1,172 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package doltcore
import (
"testing"
"time"
"github.com/google/uuid"
"github.com/liquidata-inc/dolt/go/store/types"
)
const (
zeroUUIDStr = "00000000-0000-0000-0000-000000000000"
)
var zeroUUID = uuid.Must(uuid.Parse(zeroUUIDStr))
func TestConv(t *testing.T) {
tests := []struct {
input types.Value
expectedOut types.Value
expectFunc bool
expectErr bool
}{
{types.String("test"), types.String("test"), true, false},
{types.String(""), types.String(""), true, false},
{types.String(`""`), types.String(`""`), true, false},
{types.String(zeroUUIDStr), types.UUID(zeroUUID), true, false},
{types.String("10"), types.Uint(10), true, false},
{types.String("-101"), types.Int(-101), true, false},
{types.String("3.25"), types.Float(3.25), true, false},
{types.String("true"), types.Bool(true), true, false},
{types.String("61D184E19083F09D95AB"), types.InlineBlob([]byte{0x61, 0xd1, 0x84, 0xe1, 0x90, 0x83, 0xf0, 0x9d, 0x95, 0xab}),
true, false},
{types.String("1970/12/31"),
types.Timestamp(time.Date(1970, 12, 31, 0, 0, 0, 0, time.UTC)),
true, false},
{types.String("anything"), types.NullValue, true, false},
{types.UUID(zeroUUID), types.String(zeroUUIDStr), true, false},
{types.UUID(zeroUUID), types.UUID(zeroUUID), true, false},
{types.UUID(zeroUUID), types.Uint(0), false, false},
{types.UUID(zeroUUID), types.Int(0), false, false},
{types.UUID(zeroUUID), types.Float(0), false, false},
{types.UUID(zeroUUID), types.Bool(false), false, false},
{types.UUID(zeroUUID), types.InlineBlob{}, false, false},
{types.UUID(zeroUUID), types.Timestamp{}, false, false},
{types.UUID(zeroUUID), types.NullValue, true, false},
{types.Uint(10), types.String("10"), true, false},
{types.Uint(100), types.UUID(zeroUUID), false, false},
{types.Uint(1000), types.Uint(1000), true, false},
{types.Uint(10000), types.Int(10000), true, false},
{types.Uint(100000), types.Float(100000), true, false},
{types.Uint(1000000), types.Bool(true), true, false},
{types.Uint(10000000), types.InlineBlob{}, false, false},
{types.Uint(100000000), types.Timestamp(time.Unix(100000000, 0).UTC()), true, false},
{types.Uint(1000000000), types.NullValue, true, false},
{types.Int(-10), types.String("-10"), true, false},
{types.Int(-100), types.UUID(zeroUUID), false, false},
{types.Int(1000), types.Uint(1000), true, false},
{types.Int(-10000), types.Int(-10000), true, false},
{types.Int(-100000), types.Float(-100000), true, false},
{types.Int(-1000000), types.Bool(true), true, false},
{types.Int(-10000000), types.InlineBlob{}, false, false},
{types.Int(-100000000), types.Timestamp(time.Unix(-100000000, 0).UTC()), true, false},
{types.Int(-1000000000), types.NullValue, true, false},
{types.Float(1.5), types.String("1.5"), true, false},
{types.Float(10.5), types.UUID(zeroUUID), false, false},
{types.Float(100.5), types.Uint(100), true, false},
{types.Float(1000.5), types.Int(1000), true, false},
{types.Float(10000.5), types.Float(10000.5), true, false},
{types.Float(100000.5), types.Bool(true), true, false},
{types.Float(1000000.5), types.InlineBlob{}, false, false},
{types.Float(10000000.5), types.Timestamp(time.Unix(10000000, 500000000).UTC()), true, false},
{types.Float(100000000.5), types.NullValue, true, false},
{types.Bool(true), types.String("true"), true, false},
{types.Bool(false), types.UUID(zeroUUID), false, false},
{types.Bool(true), types.Uint(1), true, false},
{types.Bool(false), types.Int(0), true, false},
{types.Bool(true), types.Float(1), true, false},
{types.Bool(false), types.Bool(false), true, false},
{types.Bool(false), types.InlineBlob{}, false, true},
{types.Bool(false), types.Timestamp{}, false, true},
{types.Bool(true), types.NullValue, true, false},
{types.InlineBlob([]byte{0x61, 0xd1, 0x84, 0xe1, 0x90, 0x83, 0xf0, 0x9d, 0x95, 0xab}),
types.String("61D184E19083F09D95AB"), true, false},
{types.InlineBlob([]byte{}), types.UUID(zeroUUID), false, false},
{types.InlineBlob([]byte{}), types.Uint(1583200922), false, false},
{types.InlineBlob([]byte{}), types.Int(1901502183), false, false},
{types.InlineBlob([]byte{}), types.Float(2219803444.4), false, false},
{types.InlineBlob([]byte{}), types.Bool(false), false, true},
{types.InlineBlob([]byte{1, 10, 100}), types.InlineBlob([]byte{1, 10, 100}), true, false},
{types.InlineBlob([]byte{}), types.NullValue, true, false},
{types.Timestamp(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)),
types.String("2000-01-01 00:00:00 +0000"), true, false},
{types.Timestamp(time.Date(2010, 2, 2, 1, 1, 1, 0, time.UTC)),
types.UUID(zeroUUID), false, false},
{types.Timestamp(time.Date(2020, 3, 3, 2, 2, 2, 0, time.UTC)),
types.Uint(1583200922), true, false},
{types.Timestamp(time.Date(2030, 4, 4, 3, 3, 3, 0, time.UTC)),
types.Int(1901502183), true, false},
{types.Timestamp(time.Date(2040, 5, 5, 4, 4, 4, 400000000, time.UTC)),
types.Float(2219803444.4), true, false},
{types.Timestamp(time.Date(2050, 6, 6, 5, 5, 5, 0, time.UTC)),
types.Bool(false), false, true},
{types.Timestamp(time.Date(2060, 7, 7, 6, 6, 6, 678912345, time.UTC)),
types.Timestamp(time.Unix(2856405966, 678912345).UTC()), true, false},
{types.Timestamp(time.Date(2070, 8, 8, 7, 7, 7, 0, time.UTC)),
types.NullValue, true, false},
}
for _, test := range tests {
convFunc, err := GetConvFunc(test.input.Kind(), test.expectedOut.Kind())
if convFunc == nil && err != nil && test.expectFunc == true {
t.Error("Did not receive conversion function for conversion from", test.input.Kind(), "to", test.expectedOut.Kind())
} else if convFunc != nil {
if test.expectFunc == false {
t.Error("Incorrectly received conversion function for conversion from", test.input.Kind(), "to", test.expectedOut.Kind())
continue
}
result, err := convFunc(test.input)
if (err != nil) != test.expectErr {
t.Error("input:", test.input, "expected err:", test.expectErr, "actual err:", err != nil)
}
if !test.expectedOut.Equals(result) {
t.Error("input:", test.input, "expected result:", test.expectedOut, "actual result:", result)
}
}
}
}
var convertibleTypes = []types.NomsKind{types.StringKind, types.UUIDKind, types.UintKind, types.IntKind, types.FloatKind, types.BoolKind, types.InlineBlobKind}
func TestNullConversion(t *testing.T) {
for _, srcKind := range convertibleTypes {
for _, destKind := range convertibleTypes {
convFunc, err := GetConvFunc(srcKind, destKind)
if convFunc != nil && err == nil {
res, err := convFunc(nil)
if res != nil || err != nil {
t.Error("null conversion failed")
}
}
}
}
}

View File

@@ -21,7 +21,8 @@ import (
"strconv"
"strings"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/sql"
"github.com/liquidata-inc/dolt/go/libraries/doltcore/schema/typeinfo"
"github.com/liquidata-inc/dolt/go/store/types"
)
@@ -171,10 +172,7 @@ func getSQLHeader(cols []*SeedColumn, tableName, format string) string {
colStr = fmt.Sprintf("%s COMMENT 'tag:%d'", colStr, i)
// translate noms type
sqlType, ok := sql.DoltToSQLType[col.Type]
if !ok {
log.Fatalf("unable to format sql string, unknown noms to sql conversion for type %v \n", col.Type)
}
sqlType := typeinfo.FromKind(col.Type).ToSqlType().String()
schema = append(schema, fmt.Sprintf(colStr, col.Name, strings.ToUpper(sqlType)))
}

View File

@@ -476,10 +476,6 @@ func readBlob(ctx context.Context, r io.Reader, vrw ValueReadWriter) (Blob, erro
return newBlob(seq), nil
}
func (Blob) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(BlobKind, targetKind)
}
func (b Blob) readFrom(nbf *NomsBinFormat, bnr *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -95,65 +95,6 @@ func (b Bool) skip(nbf *NomsBinFormat, bnr *binaryNomsReader) {
bnr.skipUint8()
}
func (Bool) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case BoolKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case FloatKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
b := val.(Bool)
if b {
return Float(1), nil
}
return Float(0), nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
b := val.(Bool)
if b {
return Int(1), nil
}
return Int(0), nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
b := val.(Bool)
if b {
return String("true"), nil
}
return String("false"), nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
b := val.(Bool)
if b {
return Uint(1), nil
}
return Uint(0), nil
}, nil
}
return nil, CreateNoConversionError(BoolKind, targetKind)
}
func (b Bool) HumanReadableString() string {
return strconv.FormatBool(bool(b))
}

View File

@@ -24,7 +24,6 @@ package types
import (
"context"
"strconv"
"time"
"github.com/liquidata-inc/dolt/go/store/hash"
)
@@ -95,73 +94,6 @@ func (v Float) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipFloat(nbf)
}
func (Float) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case BoolKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
fl := float64(val.(Float))
return Bool(fl != 0), nil
}, nil
case FloatKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
fl := float64(val.(Float))
return Int(int(fl)), nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
fl := float64(val.(Float))
str := strconv.FormatFloat(fl, 'f', -1, 64)
return String(str), nil
}, nil
case TimestampKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
fl := float64(val.(Float))
// If Float is too large, we'll clamp it to the max time representable
// There are comparison issues for times too large, so "200000000-12-31 23:59:59 UTC" seems like a reasonable maximum.
if fl > 6311328264403199 {
fl = 6311328264403199
// I could not find anything pointing to a minimum allowed time, so "-200000000-01-01 00:00:00 UTC" seems reasonable
} else if fl < -6311452567219200 {
fl = -6311452567219200
}
// We treat a Float as seconds and nanoseconds, unlike integers which are just seconds
seconds := int64(fl)
nanoseconds := int64((fl - float64(seconds)) * float64(time.Second/time.Nanosecond))
return Timestamp(time.Unix(seconds, nanoseconds).UTC()), nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
fl := float64(val.(Float))
return Uint(uint64(fl)), nil
}, nil
}
return nil, CreateNoConversionError(FloatKind, targetKind)
}
func (v Float) HumanReadableString() string {
return strconv.FormatFloat(float64(v), 'g', -1, 64)
}

View File

@@ -102,28 +102,6 @@ func (v InlineBlob) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipBytes(size)
}
func (InlineBlob) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case InlineBlobKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
return String(strings.ToUpper(hex.EncodeToString(val.(InlineBlob)))), nil
}, nil
}
return nil, CreateNoConversionError(InlineBlobKind, targetKind)
}
func (v InlineBlob) HumanReadableString() string {
return strings.ToUpper(hex.EncodeToString(v))
}

View File

@@ -24,7 +24,6 @@ package types
import (
"context"
"strconv"
"time"
"github.com/liquidata-inc/dolt/go/store/hash"
)
@@ -96,69 +95,6 @@ func (v Int) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipInt()
}
func (Int) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case BoolKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := int64(val.(Int))
return Bool(n != 0), nil
}, nil
case FloatKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := int64(val.(Int))
return Float(float64(n)), nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := int64(val.(Int))
str := strconv.FormatInt(n, 10)
return String(str), nil
}, nil
case TimestampKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := int64(val.(Int))
// There are comparison issues for times too large, so "200000000-12-31 23:59:59 UTC" seems like a reasonable maximum.
if n > 6311328264403199 {
n = 6311328264403199
// I could not find anything pointing to a minimum allowed time, so "-200000000-01-01 00:00:00" seems reasonable
} else if n < -6311452567219200 {
n = -6311452567219200
}
return Timestamp(time.Unix(n, 0).UTC()), nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := int64(val.(Int))
return Uint(uint64(n)), nil
}, nil
}
return nil, CreateNoConversionError(IntKind, targetKind)
}
func (v Int) HumanReadableString() string {
return strconv.FormatInt(int64(v), 10)
}

View File

@@ -429,10 +429,6 @@ func newEmptyListSequenceChunker(ctx context.Context, vrw ValueReadWriter) (*seq
return newEmptySequenceChunker(ctx, vrw, makeListLeafChunkFn(vrw), newIndexedMetaSequenceChunkFn(ListKind, vrw), hashValueBytes)
}
func (List) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(ListKind, targetKind)
}
func (l List) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -562,10 +562,6 @@ func newEmptyMapSequenceChunker(ctx context.Context, vrw ValueReadWriter) (*sequ
return newEmptySequenceChunker(ctx, vrw, makeMapLeafChunkFn(vrw), newOrderedMetaSequenceChunkFn(MapKind, vrw), mapHashValueBytes)
}
func (Map) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(MapKind, targetKind)
}
func (m Map) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -1,50 +0,0 @@
// Copyright 2019 Liquidata, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types
import (
"errors"
"fmt"
)
type ConversionError struct {
fromKind NomsKind
toKind NomsKind
err error
}
// CreateConversionError creates a special kind of error to return during issues with marshalling values.
func CreateConversionError(sourceKind NomsKind, targetKind NomsKind, err error) ConversionError {
return ConversionError{
fromKind: sourceKind,
toKind: targetKind,
err: err,
}
}
// CreateNoConversionError creates a special kind of error to return when no marshal function is provided.
func CreateNoConversionError(sourceKind NomsKind, targetKind NomsKind) ConversionError {
return ConversionError{
fromKind: sourceKind,
toKind: targetKind,
err: errors.New("no marshalling function found"),
}
}
func (ce ConversionError) Error() string {
toKindStr := KindToString[ce.toKind]
fromKindStr := KindToString[ce.fromKind]
return fmt.Sprint("error converting", fromKindStr, "to", toKindStr, ": ", ce.err.Error())
}

View File

@@ -88,12 +88,6 @@ func (v Null) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
func (v Null) skip(nbf *NomsBinFormat, b *binaryNomsReader) {}
func (Null) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return func(Value) (Value, error) {
return NullValue, nil
}, nil
}
func (v Null) HumanReadableString() string {
return "null_value"
}

View File

@@ -205,10 +205,6 @@ func (r Ref) isSameTargetType(other Ref) bool {
return bytes.Equal(targetTypeBytes, otherTargetTypeBytes)
}
func (Ref) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(RefKind, targetKind)
}
func (r Ref) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -354,10 +354,6 @@ func newEmptySetSequenceChunker(ctx context.Context, vrw ValueReadWriter) (*sequ
return newEmptySequenceChunker(ctx, vrw, makeSetLeafChunkFn(vrw), newOrderedMetaSequenceChunkFn(SetKind, vrw), hashValueBytes)
}
func (Set) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(SetKind, targetKind)
}
func (s Set) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -23,15 +23,9 @@ package types
import (
"context"
"encoding/hex"
"errors"
"fmt"
"math"
"strconv"
"strings"
"github.com/araddon/dateparse"
"github.com/google/uuid"
"github.com/liquidata-inc/dolt/go/store/hash"
)
@@ -122,143 +116,6 @@ func parseNumber(s String) (isNegative bool, decPos int, err error) {
return isNegative, decPos, nil
}
func (String) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case BoolKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
b, err := strconv.ParseBool(strings.ToLower(string(s)))
if err != nil {
return Bool(false), CreateConversionError(s.Kind(), BoolKind, err)
}
return Bool(b), nil
}, nil
case FloatKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
f, err := strconv.ParseFloat(string(s), 64)
if err != nil {
return Float(math.NaN()), CreateConversionError(s.Kind(), FloatKind, err)
}
return Float(f), nil
}, nil
case InlineBlobKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
data, err := hex.DecodeString(string(s))
if err != nil {
return InlineBlob{}, CreateConversionError(s.Kind(), InlineBlobKind, err)
}
return InlineBlob(data), nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
isNegative, decPos, err := parseNumber(s)
if err != nil {
b, boolErr := strconv.ParseBool(string(s))
if boolErr == nil {
if b {
return Int(1), nil
}
return Int(0), nil
}
return Int(0), CreateConversionError(s.Kind(), IntKind, err)
}
if decPos == 0 || (decPos == 1 && isNegative) {
return Int(0), nil
}
if decPos != -1 {
s = s[:decPos]
}
n, err := strconv.ParseInt(string(s), 10, 64)
if err != nil {
return Int(0), CreateConversionError(s.Kind(), IntKind, err)
}
return Int(n), nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case TimestampKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
t, err := dateparse.ParseStrict(string(s))
if err != nil {
return nil, err
}
return Timestamp(t), nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
n, err := strconv.ParseUint(string(s), 10, 64)
if err != nil {
return Uint(0), CreateConversionError(s.Kind(), UintKind, err)
}
return Uint(n), nil
}, nil
case UUIDKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
s := val.(String)
if len(s) == 0 {
return NullValue, nil
}
u, err := uuid.Parse(string(s))
if err != nil {
return UUID(u), CreateConversionError(s.Kind(), UUIDKind, err)
}
return UUID(u), nil
}, nil
}
return nil, CreateNoConversionError(StringKind, targetKind)
}
func (s String) HumanReadableString() string {
return strconv.Quote(string(s))
}

View File

@@ -656,10 +656,6 @@ func verifyStructName(name string) {
}
}
func (Struct) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(StructKind, targetKind)
}
func (s Struct) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -23,7 +23,7 @@ import (
const (
timestampNumBytes = 15
timestampFormat = "2006-01-02 15:04:05.999999999 -0700"
timestampFormat = "2006-01-02 15:04:05.999999"
)
type Timestamp time.Time
@@ -105,59 +105,8 @@ func (v Timestamp) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipBytes(timestampNumBytes)
}
func (Timestamp) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case FloatKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
t := time.Time(val.(Timestamp))
seconds := t.Unix()
// Since Float allows decimals, we represent the nanoseconds as a decimal
nanoseconds := t.Nanosecond()
combination := float64(seconds) + (float64(nanoseconds) / float64(time.Second/time.Nanosecond))
return Float(combination), nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
t := time.Time(val.(Timestamp))
return Int(t.Unix()), nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
t := val.(Timestamp)
return String(t.String()), nil
}, nil
case TimestampKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
t := time.Time(val.(Timestamp))
return Uint(t.Unix()), nil
}, nil
}
return nil, CreateNoConversionError(TimestampKind, targetKind)
}
func (v Timestamp) String() string {
return time.Time(v).Format(timestampFormat)
return time.Time(v).UTC().Format(timestampFormat)
}
func (v Timestamp) HumanReadableString() string {

View File

@@ -496,10 +496,6 @@ func (t Tuple) fieldsToMap() (map[Value]Value, error) {
return valMap, nil
}
func (Tuple) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(TupleKind, targetKind)
}
func (t Tuple) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -189,10 +189,6 @@ func indexOfType(t *Type, tl []*Type) (uint32, bool) {
return 0, false
}
func (*Type) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
return nil, CreateNoConversionError(TypeKind, targetKind)
}
func (t *Type) readFrom(nbf *NomsBinFormat, b *binaryNomsReader) (Value, error) {
panic("unreachable")
}

View File

@@ -24,7 +24,6 @@ package types
import (
"context"
"strconv"
"time"
"github.com/liquidata-inc/dolt/go/store/hash"
)
@@ -97,66 +96,6 @@ func (v Uint) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipUint()
}
func (Uint) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case BoolKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := uint64(val.(Uint))
return Bool(n != 0), nil
}, nil
case FloatKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := uint64(val.(Uint))
return Float(float64(n)), nil
}, nil
case IntKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := uint64(val.(Uint))
return Int(int64(n)), nil
}, nil
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := uint64(val.(Uint))
str := strconv.FormatUint(n, 10)
return String(str), nil
}, nil
case TimestampKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
n := uint64(val.(Uint))
// There are comparison issues for times too large, so "200000000-12-31 23:59:59 UTC" seems like a reasonable maximum.
if n > 6311328264403199 {
n = 6311328264403199
}
return Timestamp(time.Unix(int64(n), 0).UTC()), nil
}, nil
case UintKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
}
return nil, CreateNoConversionError(UintKind, targetKind)
}
func (v Uint) HumanReadableString() string {
return strconv.FormatUint(uint64(v), 10)
}

View File

@@ -103,28 +103,6 @@ func (v UUID) skip(nbf *NomsBinFormat, b *binaryNomsReader) {
b.skipBytes(uuidNumBytes)
}
func (UUID) GetMarshalFunc(targetKind NomsKind) (MarshalCallback, error) {
switch targetKind {
case NullKind:
return func(Value) (Value, error) {
return NullValue, nil
}, nil
case StringKind:
return func(val Value) (Value, error) {
if val == nil {
return nil, nil
}
return String(val.(UUID).String()), nil
}, nil
case UUIDKind:
return func(val Value) (Value, error) {
return val, nil
}, nil
}
return nil, CreateNoConversionError(UUIDKind, targetKind)
}
func (v UUID) String() string {
return uuid.UUID(v).String()
}

View File

@@ -110,10 +110,6 @@ type Value interface {
// chunked then this will return the refs of th sub trees of the prolly-tree.
WalkRefs(*NomsBinFormat, RefCallback) error
// GetMarshalFunc takes in a Kind and returns a function that accepts a Value of the calling type.
// The returned function then marshals the given type into the given Kind.
GetMarshalFunc(NomsKind) (MarshalCallback, error)
// HumanReadableString returns a human-readable string version of this Value (not meant for re-parsing)
HumanReadableString() string