diff --git a/aclitem.go b/aclitem.go index 9f6587be..0c1f23b5 100644 --- a/aclitem.go +++ b/aclitem.go @@ -19,12 +19,12 @@ import ( // type ACLItem struct { String string - Status Status + Valid bool } func (dst *ACLItem) Set(src interface{}) error { if src == nil { - *dst = ACLItem{Status: Null} + *dst = ACLItem{} return nil } @@ -37,12 +37,12 @@ func (dst *ACLItem) Set(src interface{}) error { switch value := src.(type) { case string: - *dst = ACLItem{String: value, Status: Present} + *dst = ACLItem{String: value, Valid: true} case *string: if value == nil { - *dst = ACLItem{Status: Null} + *dst = ACLItem{} } else { - *dst = ACLItem{String: *value, Status: Present} + *dst = ACLItem{String: *value, Valid: true} } default: if originalSrc, ok := underlyingStringType(src); ok { @@ -55,52 +55,44 @@ func (dst *ACLItem) Set(src interface{}) error { } func (dst ACLItem) Get() interface{} { - switch dst.Status { - case Present: - return dst.String - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.String } func (src *ACLItem) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *string: - *v = src.String - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } + switch v := dst.(type) { + case *string: + *v = src.String + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } + return fmt.Errorf("cannot decode %#v into %T", src, dst) } func (dst *ACLItem) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = ACLItem{Status: Null} + *dst = ACLItem{} return nil } - *dst = ACLItem{String: string(src), Status: Present} + *dst = ACLItem{String: string(src), Valid: true} return nil } func (src ACLItem) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.String...), nil @@ -109,7 +101,7 @@ func (src ACLItem) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *ACLItem) Scan(src interface{}) error { if src == nil { - *dst = ACLItem{Status: Null} + *dst = ACLItem{} return nil } @@ -127,12 +119,9 @@ func (dst *ACLItem) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src ACLItem) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.String, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + return src.String, nil } diff --git a/aclitem_array.go b/aclitem_array.go index 4e3be3bd..fc1128b7 100644 --- a/aclitem_array.go +++ b/aclitem_array.go @@ -11,13 +11,13 @@ import ( type ACLItemArray struct { Elements []ACLItem Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *ACLItemArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} return nil } @@ -33,9 +33,9 @@ func (dst *ACLItemArray) Set(src interface{}) error { case []string: if value == nil { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} } else if len(value) == 0 { - *dst = ACLItemArray{Status: Present} + *dst = ACLItemArray{Valid: true} } else { elements := make([]ACLItem, len(value)) for i := range value { @@ -46,15 +46,15 @@ func (dst *ACLItemArray) Set(src interface{}) error { *dst = ACLItemArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} } else if len(value) == 0 { - *dst = ACLItemArray{Status: Present} + *dst = ACLItemArray{Valid: true} } else { elements := make([]ACLItem, len(value)) for i := range value { @@ -65,20 +65,20 @@ func (dst *ACLItemArray) Set(src interface{}) error { *dst = ACLItemArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []ACLItem: if value == nil { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} } else if len(value) == 0 { - *dst = ACLItemArray{Status: Present} + *dst = ACLItemArray{Valid: true} } else { *dst = ACLItemArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -87,7 +87,7 @@ func (dst *ACLItemArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} return nil } @@ -96,7 +96,7 @@ func (dst *ACLItemArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for ACLItemArray", src) } if elementsLength == 0 { - *dst = ACLItemArray{Status: Present} + *dst = ACLItemArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -109,7 +109,7 @@ func (dst *ACLItemArray) Set(src interface{}) error { *dst = ACLItemArray{ Elements: make([]ACLItem, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -176,84 +176,77 @@ func (dst *ACLItemArray) setRecursive(value reflect.Value, index, dimension int) } func (dst ACLItemArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *ACLItemArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *ACLItemArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -305,7 +298,7 @@ func (src *ACLItemArray) assignToRecursive(value reflect.Value, index, dimension func (dst *ACLItemArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = ACLItemArray{Status: Null} + *dst = ACLItemArray{} return nil } @@ -334,17 +327,14 @@ func (dst *ACLItemArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = ACLItemArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = ACLItemArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (src ACLItemArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { diff --git a/aclitem_array_test.go b/aclitem_array_test.go index 8f015f40..0d6adb1d 100644 --- a/aclitem_array_test.go +++ b/aclitem_array_test.go @@ -13,42 +13,42 @@ func TestACLItemArrayTranscode(t *testing.T) { &pgtype.ACLItemArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {Status: pgtype.Null}, + {String: "=r/postgres", Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.ACLItemArray{Status: pgtype.Null}, + &pgtype.ACLItemArray{}, &pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - //{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present}, - {String: `postgres=arwdDxt/postgres`, Status: pgtype.Present}, // todo: remove after fixing above case - {String: "=r/postgres", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "=r/postgres", Status: pgtype.Present}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + //{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}, + {String: `postgres=arwdDxt/postgres`, Valid: true}, // todo: remove after fixing above case + {String: "=r/postgres", Valid: true}, + {}, + {String: "=r/postgres", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -61,22 +61,22 @@ func TestACLItemArraySet(t *testing.T) { { source: []string{"=r/postgres"}, result: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, + Elements: []pgtype.ACLItem{{String: "=r/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]string)(nil)), - result: pgtype.ACLItemArray{Status: pgtype.Null}, + result: pgtype.ACLItemArray{}, }, { source: [][]string{{"=r/postgres"}, {"postgres=arwdDxt/postgres"}}, result: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]string{ @@ -90,27 +90,27 @@ func TestACLItemArraySet(t *testing.T) { "postgres=arwdDxt/postgres"}}}}, result: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]string{{"=r/postgres"}, {"postgres=arwdDxt/postgres"}}, result: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]string{ @@ -124,18 +124,18 @@ func TestACLItemArraySet(t *testing.T) { "postgres=arwdDxt/postgres"}}}}, result: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -168,57 +168,57 @@ func TestACLItemArrayAssignTo(t *testing.T) { }{ { src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, + Elements: []pgtype.ACLItem{{String: "=r/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, expected: []string{"=r/postgres"}, }, { src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}}, + Elements: []pgtype.ACLItem{{String: "=r/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedStringSlice, expected: _stringSlice{"=r/postgres"}, }, { - src: pgtype.ACLItemArray{Status: pgtype.Null}, + src: pgtype.ACLItemArray{}, dst: &stringSlice, expected: (([]string)(nil)), }, { - src: pgtype.ACLItemArray{Status: pgtype.Present}, + src: pgtype.ACLItemArray{Valid: true}, dst: &stringSlice, expected: []string{}, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim2, expected: [][]string{{"=r/postgres"}, {"postgres=arwdDxt/postgres"}}, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim4, expected: [][][][]string{ {{{ @@ -233,28 +233,28 @@ func TestACLItemArrayAssignTo(t *testing.T) { { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, expected: [2][1]string{{"=r/postgres"}, {"postgres=arwdDxt/postgres"}}, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, expected: [2][1][1][3]string{ {{{ @@ -285,37 +285,37 @@ func TestACLItemArrayAssignTo(t *testing.T) { }{ { src: pgtype.ACLItemArray{ - Elements: []pgtype.ACLItem{{Status: pgtype.Null}}, + Elements: []pgtype.ACLItem{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSlice, }, { src: pgtype.ACLItemArray{ Elements: []pgtype.ACLItem{ - {String: "=r/postgres", Status: pgtype.Present}, - {String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, + {String: "=r/postgres", Valid: true}, + {String: "postgres=arwdDxt/postgres", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, }, } diff --git a/aclitem_test.go b/aclitem_test.go index a37d7657..4e9bc5b0 100644 --- a/aclitem_test.go +++ b/aclitem_test.go @@ -10,9 +10,9 @@ import ( func TestACLItemTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "aclitem", []interface{}{ - &pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, - //&pgtype.ACLItem{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present}, - &pgtype.ACLItem{Status: pgtype.Null}, + &pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Valid: true}, + //&pgtype.ACLItem{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}, + &pgtype.ACLItem{}, }) } @@ -21,8 +21,8 @@ func TestACLItemSet(t *testing.T) { source interface{} result pgtype.ACLItem }{ - {source: "postgres=arwdDxt/postgres", result: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.ACLItem{Status: pgtype.Null}}, + {source: "postgres=arwdDxt/postgres", result: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Valid: true}}, + {source: (*string)(nil), result: pgtype.ACLItem{}}, } for i, tt := range successfulTests { @@ -47,8 +47,8 @@ func TestACLItemAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &s, expected: "postgres=arwdDxt/postgres"}, - {src: pgtype.ACLItem{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, + {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Valid: true}, dst: &s, expected: "postgres=arwdDxt/postgres"}, + {src: pgtype.ACLItem{}, dst: &ps, expected: ((*string)(nil))}, } for i, tt := range simpleTests { @@ -67,7 +67,7 @@ func TestACLItemAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &ps, expected: "postgres=arwdDxt/postgres"}, + {src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Valid: true}, dst: &ps, expected: "postgres=arwdDxt/postgres"}, } for i, tt := range pointerAllocTests { @@ -85,7 +85,7 @@ func TestACLItemAssignTo(t *testing.T) { src pgtype.ACLItem dst interface{} }{ - {src: pgtype.ACLItem{Status: pgtype.Null}, dst: &s}, + {src: pgtype.ACLItem{}, dst: &s}, } for i, tt := range errorTests { diff --git a/array_type.go b/array_type.go index 1bd0244b..1df1689f 100644 --- a/array_type.go +++ b/array_type.go @@ -20,7 +20,7 @@ type ArrayType struct { newElement func() ValueTranscoder elementOID uint32 - status Status + valid bool } func NewArrayType(typeName string, elementOID uint32, newElement func() ValueTranscoder) *ArrayType { @@ -31,7 +31,7 @@ func (at *ArrayType) NewTypeValue() Value { return &ArrayType{ elements: at.elements, dimensions: at.dimensions, - status: at.status, + valid: at.valid, typeName: at.typeName, elementOID: at.elementOID, @@ -46,7 +46,7 @@ func (at *ArrayType) TypeName() string { func (dst *ArrayType) setNil() { dst.elements = nil dst.dimensions = nil - dst.status = Null + dst.valid = false } func (dst *ArrayType) Set(src interface{}) error { @@ -77,24 +77,21 @@ func (dst *ArrayType) Set(src interface{}) error { dst.elements[i] = v } dst.dimensions = []ArrayDimension{{Length: int32(len(dst.elements)), LowerBound: 1}} - dst.status = Present + dst.valid = true return nil } -func (dst ArrayType) Get() interface{} { - switch dst.status { - case Present: - elementValues := make([]interface{}, len(dst.elements)) - for i := range dst.elements { - elementValues[i] = dst.elements[i].Get() - } - return elementValues - case Null: +func (src ArrayType) Get() interface{} { + if !src.valid { return nil - default: - return dst.status } + + elementValues := make([]interface{}, len(src.elements)) + for i := range src.elements { + elementValues[i] = src.elements[i].Get() + } + return elementValues } func (src *ArrayType) AssignTo(dst interface{}) error { @@ -110,8 +107,7 @@ func (src *ArrayType) AssignTo(dst interface{}) error { return fmt.Errorf("cannot assign to pointer to non-slice") } - switch src.status { - case Present: + if src.valid { slice := reflect.MakeSlice(sliceType, len(src.elements), len(src.elements)) elemType := sliceType.Elem() @@ -127,12 +123,10 @@ func (src *ArrayType) AssignTo(dst interface{}) error { sliceVal.Set(slice) return nil - case Null: + } else { sliceVal.Set(reflect.Zero(sliceType)) return nil } - - return fmt.Errorf("cannot decode %#v into %T", src, dst) } func (dst *ArrayType) DecodeText(ci *ConnInfo, src []byte) error { @@ -168,7 +162,7 @@ func (dst *ArrayType) DecodeText(ci *ConnInfo, src []byte) error { dst.elements = elements dst.dimensions = uta.Dimensions - dst.status = Present + dst.valid = true return nil } @@ -190,7 +184,7 @@ func (dst *ArrayType) DecodeBinary(ci *ConnInfo, src []byte) error { if len(arrayHeader.Dimensions) == 0 { dst.elements = elements dst.dimensions = arrayHeader.Dimensions - dst.status = Present + dst.valid = true return nil } @@ -220,17 +214,14 @@ func (dst *ArrayType) DecodeBinary(ci *ConnInfo, src []byte) error { dst.elements = elements dst.dimensions = arrayHeader.Dimensions - dst.status = Present + dst.valid = true return nil } func (src ArrayType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.status { - case Null: + if !src.valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.dimensions) == 0 { @@ -283,11 +274,8 @@ func (src ArrayType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src ArrayType) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.status { - case Null: + if !src.valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ diff --git a/bit_test.go b/bit_test.go index 2e9c9b6e..df5fe4cb 100644 --- a/bit_test.go +++ b/bit_test.go @@ -9,9 +9,9 @@ import ( func TestBitTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "bit(40)", []interface{}{ - &pgtype.Varbit{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Status: pgtype.Null}, + &pgtype.Varbit{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true}, + &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true}, + &pgtype.Varbit{}, }) } @@ -19,7 +19,7 @@ func TestBitNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select B'111111111'", - Value: &pgtype.Bit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present}, + Value: &pgtype.Bit{Bytes: []byte{255, 128}, Len: 9, Valid: true}, }, }) } diff --git a/bool.go b/bool.go index 676c8e5d..4fcc67e3 100644 --- a/bool.go +++ b/bool.go @@ -8,13 +8,13 @@ import ( ) type Bool struct { - Bool bool - Status Status + Bool bool + Valid bool } func (dst *Bool) Set(src interface{}) error { if src == nil { - *dst = Bool{Status: Null} + *dst = Bool{} return nil } @@ -27,22 +27,22 @@ func (dst *Bool) Set(src interface{}) error { switch value := src.(type) { case bool: - *dst = Bool{Bool: value, Status: Present} + *dst = Bool{Bool: value, Valid: true} case string: bb, err := strconv.ParseBool(value) if err != nil { return err } - *dst = Bool{Bool: bb, Status: Present} + *dst = Bool{Bool: bb, Valid: true} case *bool: if value == nil { - *dst = Bool{Status: Null} + *dst = Bool{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Bool{Status: Null} + *dst = Bool{} } else { return dst.Set(*value) } @@ -57,39 +57,33 @@ func (dst *Bool) Set(src interface{}) error { } func (dst Bool) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bool - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + + return dst.Bool } func (src *Bool) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *bool: - *v = src.Bool - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *bool: + *v = src.Bool + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Bool) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Bool{Status: Null} + *dst = Bool{} return nil } @@ -97,13 +91,13 @@ func (dst *Bool) DecodeText(ci *ConnInfo, src []byte) error { return fmt.Errorf("invalid length for bool: %v", len(src)) } - *dst = Bool{Bool: src[0] == 't', Status: Present} + *dst = Bool{Bool: src[0] == 't', Valid: true} return nil } func (dst *Bool) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Bool{Status: Null} + *dst = Bool{} return nil } @@ -111,16 +105,13 @@ func (dst *Bool) DecodeBinary(ci *ConnInfo, src []byte) error { return fmt.Errorf("invalid length for bool: %v", len(src)) } - *dst = Bool{Bool: src[0] == 1, Status: Present} + *dst = Bool{Bool: src[0] == 1, Valid: true} return nil } func (src Bool) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.Bool { @@ -133,11 +124,8 @@ func (src Bool) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Bool) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.Bool { @@ -152,13 +140,13 @@ func (src Bool) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Bool) Scan(src interface{}) error { if src == nil { - *dst = Bool{Status: Null} + *dst = Bool{} return nil } switch src := src.(type) { case bool: - *dst = Bool{Bool: src, Status: Present} + *dst = Bool{Bool: src, Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -173,31 +161,23 @@ func (dst *Bool) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Bool) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bool, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + return src.Bool, nil } func (src Bool) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - if src.Bool { - return []byte("true"), nil - } else { - return []byte("false"), nil - } - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - return nil, errBadStatus + if src.Bool { + return []byte("true"), nil + } else { + return []byte("false"), nil + } } func (dst *Bool) UnmarshalJSON(b []byte) error { @@ -208,9 +188,9 @@ func (dst *Bool) UnmarshalJSON(b []byte) error { } if v == nil { - *dst = Bool{Status: Null} + *dst = Bool{} } else { - *dst = Bool{Bool: *v, Status: Present} + *dst = Bool{Bool: *v, Valid: true} } return nil diff --git a/bool_array.go b/bool_array.go index 6558d971..a282fd6b 100644 --- a/bool_array.go +++ b/bool_array.go @@ -14,13 +14,13 @@ import ( type BoolArray struct { Elements []Bool Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *BoolArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} return nil } @@ -36,9 +36,9 @@ func (dst *BoolArray) Set(src interface{}) error { case []bool: if value == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} } else if len(value) == 0 { - *dst = BoolArray{Status: Present} + *dst = BoolArray{Valid: true} } else { elements := make([]Bool, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *BoolArray) Set(src interface{}) error { *dst = BoolArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*bool: if value == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} } else if len(value) == 0 { - *dst = BoolArray{Status: Present} + *dst = BoolArray{Valid: true} } else { elements := make([]Bool, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *BoolArray) Set(src interface{}) error { *dst = BoolArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Bool: if value == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} } else if len(value) == 0 { - *dst = BoolArray{Status: Present} + *dst = BoolArray{Valid: true} } else { *dst = BoolArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *BoolArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} return nil } @@ -99,7 +99,7 @@ func (dst *BoolArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for BoolArray", src) } if elementsLength == 0 { - *dst = BoolArray{Status: Present} + *dst = BoolArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *BoolArray) Set(src interface{}) error { *dst = BoolArray{ Elements: make([]Bool, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *BoolArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst BoolArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *BoolArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]bool: - *v = make([]bool, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*bool: - *v = make([]*bool, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]bool: + *v = make([]bool, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*bool: + *v = make([]*bool, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *BoolArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *BoolArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *BoolArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} return nil } @@ -337,14 +330,14 @@ func (dst *BoolArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = BoolArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = BoolArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *BoolArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = BoolArray{Status: Null} + *dst = BoolArray{} return nil } @@ -355,7 +348,7 @@ func (dst *BoolArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = BoolArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = BoolArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *BoolArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = BoolArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = BoolArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src BoolArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src BoolArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src BoolArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src BoolArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/bool_array_test.go b/bool_array_test.go index be567e59..cfb9ad79 100644 --- a/bool_array_test.go +++ b/bool_array_test.go @@ -13,41 +13,41 @@ func TestBoolArrayTranscode(t *testing.T) { &pgtype.BoolArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Bool: true, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.BoolArray{Status: pgtype.Null}, + &pgtype.BoolArray{}, &pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bool: false, Status: pgtype.Present}, + {Bool: true, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {}, + {Bool: false, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,61 +60,61 @@ func TestBoolArraySet(t *testing.T) { { source: []bool{true}, result: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]bool)(nil)), - result: pgtype.BoolArray{Status: pgtype.Null}, + result: pgtype.BoolArray{}, }, { source: [][]bool{{true}, {false}}, result: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]bool{{{{true, false, true}}}, {{{false, true, false}}}}, result: pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]bool{{true}, {false}}, result: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]bool{{{{true, false, true}}}, {{{false, true, false}}}}, result: pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -147,81 +147,81 @@ func TestBoolArrayAssignTo(t *testing.T) { }{ { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &boolSlice, expected: []bool{true}, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedBoolSlice, expected: _boolSlice{true}, }, { - src: pgtype.BoolArray{Status: pgtype.Null}, + src: pgtype.BoolArray{}, dst: &boolSlice, expected: (([]bool)(nil)), }, { - src: pgtype.BoolArray{Status: pgtype.Present}, + src: pgtype.BoolArray{Valid: true}, dst: &boolSlice, expected: []bool{}, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]bool{{true}, {false}}, dst: &boolSliceDim2, }, { src: pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]bool{{{{true, false, true}}}, {{{false, true, false}}}}, dst: &boolSliceDim4, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]bool{{true}, {false}}, dst: &boolArrayDim2, }, { src: pgtype.BoolArray{ Elements: []pgtype.Bool{ - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}, - {Bool: true, Status: pgtype.Present}, - {Bool: false, Status: pgtype.Present}}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}, + {Bool: true, Valid: true}, + {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]bool{{{{true, false, true}}}, {{{false, true, false}}}}, dst: &boolArrayDim4, }, @@ -244,31 +244,31 @@ func TestBoolArrayAssignTo(t *testing.T) { }{ { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Status: pgtype.Null}}, + Elements: []pgtype.Bool{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &boolSlice, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &boolArrayDim2, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &boolSlice, }, { src: pgtype.BoolArray{ - Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}, {Bool: false, Status: pgtype.Present}}, + Elements: []pgtype.Bool{{Bool: true, Valid: true}, {Bool: false, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &boolArrayDim4, }, } diff --git a/bool_test.go b/bool_test.go index 8e7a5220..a1ba9bb0 100644 --- a/bool_test.go +++ b/bool_test.go @@ -10,9 +10,9 @@ import ( func TestBoolTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "bool", []interface{}{ - &pgtype.Bool{Bool: false, Status: pgtype.Present}, - &pgtype.Bool{Bool: true, Status: pgtype.Present}, - &pgtype.Bool{Bool: false, Status: pgtype.Null}, + &pgtype.Bool{Bool: false, Valid: true}, + &pgtype.Bool{Bool: true, Valid: true}, + &pgtype.Bool{Bool: false}, }) } @@ -21,15 +21,15 @@ func TestBoolSet(t *testing.T) { source interface{} result pgtype.Bool }{ - {source: true, result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: false, result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: "true", result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: "false", result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: "t", result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: "f", result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: _bool(true), result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: _bool(false), result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, - {source: nil, result: pgtype.Bool{Status: pgtype.Null}}, + {source: true, result: pgtype.Bool{Bool: true, Valid: true}}, + {source: false, result: pgtype.Bool{Bool: false, Valid: true}}, + {source: "true", result: pgtype.Bool{Bool: true, Valid: true}}, + {source: "false", result: pgtype.Bool{Bool: false, Valid: true}}, + {source: "t", result: pgtype.Bool{Bool: true, Valid: true}}, + {source: "f", result: pgtype.Bool{Bool: false, Valid: true}}, + {source: _bool(true), result: pgtype.Bool{Bool: true, Valid: true}}, + {source: _bool(false), result: pgtype.Bool{Bool: false, Valid: true}}, + {source: nil, result: pgtype.Bool{}}, } for i, tt := range successfulTests { @@ -56,12 +56,12 @@ func TestBoolAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &b, expected: false}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &b, expected: true}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &_b, expected: _bool(false)}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_b, expected: _bool(true)}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &pb, expected: ((*bool)(nil))}, - {src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &_pb, expected: ((*_bool)(nil))}, + {src: pgtype.Bool{Bool: false, Valid: true}, dst: &b, expected: false}, + {src: pgtype.Bool{Bool: true, Valid: true}, dst: &b, expected: true}, + {src: pgtype.Bool{Bool: false, Valid: true}, dst: &_b, expected: _bool(false)}, + {src: pgtype.Bool{Bool: true, Valid: true}, dst: &_b, expected: _bool(true)}, + {src: pgtype.Bool{Bool: false}, dst: &pb, expected: ((*bool)(nil))}, + {src: pgtype.Bool{Bool: false}, dst: &_pb, expected: ((*_bool)(nil))}, } for i, tt := range simpleTests { @@ -80,8 +80,8 @@ func TestBoolAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &pb, expected: true}, - {src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_pb, expected: _bool(true)}, + {src: pgtype.Bool{Bool: true, Valid: true}, dst: &pb, expected: true}, + {src: pgtype.Bool{Bool: true, Valid: true}, dst: &_pb, expected: _bool(true)}, } for i, tt := range pointerAllocTests { @@ -101,9 +101,9 @@ func TestBoolMarshalJSON(t *testing.T) { source pgtype.Bool result string }{ - {source: pgtype.Bool{Status: pgtype.Null}, result: "null"}, - {source: pgtype.Bool{Bool: true, Status: pgtype.Present}, result: "true"}, - {source: pgtype.Bool{Bool: false, Status: pgtype.Present}, result: "false"}, + {source: pgtype.Bool{}, result: "null"}, + {source: pgtype.Bool{Bool: true, Valid: true}, result: "true"}, + {source: pgtype.Bool{Bool: false, Valid: true}, result: "false"}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -122,9 +122,9 @@ func TestBoolUnmarshalJSON(t *testing.T) { source string result pgtype.Bool }{ - {source: "null", result: pgtype.Bool{Status: pgtype.Null}}, - {source: "true", result: pgtype.Bool{Bool: true, Status: pgtype.Present}}, - {source: "false", result: pgtype.Bool{Bool: false, Status: pgtype.Present}}, + {source: "null", result: pgtype.Bool{}}, + {source: "true", result: pgtype.Bool{Bool: true, Valid: true}}, + {source: "false", result: pgtype.Bool{Bool: false, Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Bool diff --git a/box.go b/box.go index 27fb829e..868b40a2 100644 --- a/box.go +++ b/box.go @@ -12,8 +12,8 @@ import ( ) type Box struct { - P [2]Vec2 - Status Status + P [2]Vec2 + Valid bool } func (dst *Box) Set(src interface{}) error { @@ -21,14 +21,10 @@ func (dst *Box) Set(src interface{}) error { } func (dst Box) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Box) AssignTo(dst interface{}) error { @@ -37,7 +33,7 @@ func (src *Box) AssignTo(dst interface{}) error { func (dst *Box) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Box{Status: Null} + *dst = Box{} return nil } @@ -78,13 +74,13 @@ func (dst *Box) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present} + *dst = Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Valid: true} return nil } func (dst *Box) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Box{Status: Null} + *dst = Box{} return nil } @@ -102,17 +98,14 @@ func (dst *Box) DecodeBinary(ci *ConnInfo, src []byte) error { {math.Float64frombits(x1), math.Float64frombits(y1)}, {math.Float64frombits(x2), math.Float64frombits(y2)}, }, - Status: Present, + Valid: true, } return nil } func (src Box) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`, @@ -125,11 +118,8 @@ func (src Box) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Box) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X)) @@ -143,7 +133,7 @@ func (src Box) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Box) Scan(src interface{}) error { if src == nil { - *dst = Box{Status: Null} + *dst = Box{} return nil } diff --git a/box_test.go b/box_test.go index 643c74ec..c7e00553 100644 --- a/box_test.go +++ b/box_test.go @@ -10,14 +10,14 @@ import ( func TestBoxTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "box", []interface{}{ &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}}, - Status: pgtype.Present, + P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}}, + Valid: true, }, &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, - Status: pgtype.Present, + P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, + Valid: true, }, - &pgtype.Box{Status: pgtype.Null}, + &pgtype.Box{}, }) } @@ -26,8 +26,8 @@ func TestBoxNormalize(t *testing.T) { { SQL: "select '3.14, 1.678, 7.1, 5.234'::box", Value: &pgtype.Box{ - P: [2]pgtype.Vec2{{7.1, 5.234}, {3.14, 1.678}}, - Status: pgtype.Present, + P: [2]pgtype.Vec2{{7.1, 5.234}, {3.14, 1.678}}, + Valid: true, }, }, }) diff --git a/bpchar.go b/bpchar.go index c5fa42ea..2e899ea8 100644 --- a/bpchar.go +++ b/bpchar.go @@ -21,32 +21,31 @@ func (dst BPChar) Get() interface{} { // AssignTo assigns from src to dst. func (src *BPChar) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *rune: - runes := []rune(src.String) - if len(runes) == 1 { - *v = runes[0] - return nil - } - case *string: - *v = src.String - return nil - case *[]byte: - *v = make([]byte, len(src.String)) - copy(*v, src.String) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } + switch v := dst.(type) { + case *rune: + runes := []rune(src.String) + if len(runes) == 1 { + *v = runes[0] + return nil + } + case *string: + *v = src.String + return nil + case *[]byte: + *v = make([]byte, len(src.String)) + copy(*v, src.String) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } + return fmt.Errorf("cannot decode %#v into %T", src, dst) } diff --git a/bpchar_array.go b/bpchar_array.go index 8e792214..c73c78a3 100644 --- a/bpchar_array.go +++ b/bpchar_array.go @@ -14,13 +14,13 @@ import ( type BPCharArray struct { Elements []BPChar Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *BPCharArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} return nil } @@ -36,9 +36,9 @@ func (dst *BPCharArray) Set(src interface{}) error { case []string: if value == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} } else if len(value) == 0 { - *dst = BPCharArray{Status: Present} + *dst = BPCharArray{Valid: true} } else { elements := make([]BPChar, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *BPCharArray) Set(src interface{}) error { *dst = BPCharArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} } else if len(value) == 0 { - *dst = BPCharArray{Status: Present} + *dst = BPCharArray{Valid: true} } else { elements := make([]BPChar, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *BPCharArray) Set(src interface{}) error { *dst = BPCharArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []BPChar: if value == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} } else if len(value) == 0 { - *dst = BPCharArray{Status: Present} + *dst = BPCharArray{Valid: true} } else { *dst = BPCharArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *BPCharArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} return nil } @@ -99,7 +99,7 @@ func (dst *BPCharArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for BPCharArray", src) } if elementsLength == 0 { - *dst = BPCharArray{Status: Present} + *dst = BPCharArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *BPCharArray) Set(src interface{}) error { *dst = BPCharArray{ Elements: make([]BPChar, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *BPCharArray) setRecursive(value reflect.Value, index, dimension int) } func (dst BPCharArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *BPCharArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *BPCharArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *BPCharArray) assignToRecursive(value reflect.Value, index, dimension func (dst *BPCharArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} return nil } @@ -337,14 +330,14 @@ func (dst *BPCharArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = BPCharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = BPCharArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *BPCharArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = BPCharArray{Status: Null} + *dst = BPCharArray{} return nil } @@ -355,7 +348,7 @@ func (dst *BPCharArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = BPCharArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = BPCharArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *BPCharArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = BPCharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = BPCharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src BPCharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src BPCharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src BPCharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src BPCharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/bpchar_array_test.go b/bpchar_array_test.go index af6bf09a..277f6e3c 100644 --- a/bpchar_array_test.go +++ b/bpchar_array_test.go @@ -12,44 +12,44 @@ func TestBPCharArrayTranscode(t *testing.T) { &pgtype.BPCharArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.BPCharArray{ Elements: []pgtype.BPChar{ - pgtype.BPChar{String: "foo ", Status: pgtype.Present}, - pgtype.BPChar{Status: pgtype.Null}, + pgtype.BPChar{String: "foo ", Valid: true}, + pgtype.BPChar{}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.BPCharArray{Status: pgtype.Null}, + &pgtype.BPCharArray{}, &pgtype.BPCharArray{ Elements: []pgtype.BPChar{ - pgtype.BPChar{String: "bar ", Status: pgtype.Present}, - pgtype.BPChar{String: "NuLL ", Status: pgtype.Present}, - pgtype.BPChar{String: `wow"quz\`, Status: pgtype.Present}, - pgtype.BPChar{String: "1 ", Status: pgtype.Present}, - pgtype.BPChar{String: "1 ", Status: pgtype.Present}, - pgtype.BPChar{String: "null ", Status: pgtype.Present}, + pgtype.BPChar{String: "bar ", Valid: true}, + pgtype.BPChar{String: "NuLL ", Valid: true}, + pgtype.BPChar{String: `wow"quz\`, Valid: true}, + pgtype.BPChar{String: "1 ", Valid: true}, + pgtype.BPChar{String: "1 ", Valid: true}, + pgtype.BPChar{String: "null ", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}, }, - Status: pgtype.Present, + Valid: true, }, &pgtype.BPCharArray{ Elements: []pgtype.BPChar{ - pgtype.BPChar{String: " bar ", Status: pgtype.Present}, - pgtype.BPChar{String: " baz ", Status: pgtype.Present}, - pgtype.BPChar{String: " quz ", Status: pgtype.Present}, - pgtype.BPChar{String: "foo ", Status: pgtype.Present}, + pgtype.BPChar{String: " bar ", Valid: true}, + pgtype.BPChar{String: " baz ", Valid: true}, + pgtype.BPChar{String: " quz ", Valid: true}, + pgtype.BPChar{String: "foo ", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } diff --git a/bpchar_test.go b/bpchar_test.go index 7b8c1da3..fe7e651c 100644 --- a/bpchar_test.go +++ b/bpchar_test.go @@ -10,16 +10,16 @@ import ( func TestChar3Transcode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "char(3)", []interface{}{ - &pgtype.BPChar{String: "a ", Status: pgtype.Present}, - &pgtype.BPChar{String: " a ", Status: pgtype.Present}, - &pgtype.BPChar{String: "å—¨ ", Status: pgtype.Present}, - &pgtype.BPChar{String: " ", Status: pgtype.Present}, - &pgtype.BPChar{Status: pgtype.Null}, + &pgtype.BPChar{String: "a ", Valid: true}, + &pgtype.BPChar{String: " a ", Valid: true}, + &pgtype.BPChar{String: "å—¨ ", Valid: true}, + &pgtype.BPChar{String: " ", Valid: true}, + &pgtype.BPChar{}, }, func(aa, bb interface{}) bool { a := aa.(pgtype.BPChar) b := bb.(pgtype.BPChar) - return a.Status == b.Status && a.String == b.String + return a.Valid == b.Valid && a.String == b.String }) } @@ -33,8 +33,8 @@ func TestBPCharAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.BPChar{String: "simple", Status: pgtype.Present}, dst: &str, expected: "simple"}, - {src: pgtype.BPChar{String: "å—¨", Status: pgtype.Present}, dst: &run, expected: 'å—¨'}, + {src: pgtype.BPChar{String: "simple", Valid: true}, dst: &str, expected: "simple"}, + {src: pgtype.BPChar{String: "å—¨", Valid: true}, dst: &run, expected: 'å—¨'}, } for i, tt := range simpleTests { diff --git a/bytea.go b/bytea.go index 67eba350..d4c4e436 100644 --- a/bytea.go +++ b/bytea.go @@ -7,13 +7,13 @@ import ( ) type Bytea struct { - Bytes []byte - Status Status + Bytes []byte + Valid bool } func (dst *Bytea) Set(src interface{}) error { if src == nil { - *dst = Bytea{Status: Null} + *dst = Bytea{} return nil } @@ -27,9 +27,9 @@ func (dst *Bytea) Set(src interface{}) error { switch value := src.(type) { case []byte: if value != nil { - *dst = Bytea{Bytes: value, Status: Present} + *dst = Bytea{Bytes: value, Valid: true} } else { - *dst = Bytea{Status: Null} + *dst = Bytea{} } default: if originalSrc, ok := underlyingBytesType(src); ok { @@ -42,43 +42,36 @@ func (dst *Bytea) Set(src interface{}) error { } func (dst Bytea) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bytes - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Bytes } func (src *Bytea) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[]byte: - buf := make([]byte, len(src.Bytes)) - copy(buf, src.Bytes) - *v = buf - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *[]byte: + buf := make([]byte, len(src.Bytes)) + copy(buf, src.Bytes) + *v = buf + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } // DecodeText only supports the hex format. This has been the default since // PostgreSQL 9.0. func (dst *Bytea) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Bytea{Status: Null} + *dst = Bytea{} return nil } @@ -92,26 +85,23 @@ func (dst *Bytea) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Bytea{Bytes: buf, Status: Present} + *dst = Bytea{Bytes: buf, Valid: true} return nil } func (dst *Bytea) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Bytea{Status: Null} + *dst = Bytea{} return nil } - *dst = Bytea{Bytes: src, Status: Present} + *dst = Bytea{Bytes: src, Valid: true} return nil } func (src Bytea) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, `\x`...) @@ -120,11 +110,8 @@ func (src Bytea) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Bytea) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.Bytes...), nil @@ -133,7 +120,7 @@ func (src Bytea) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Bytea) Scan(src interface{}) error { if src == nil { - *dst = Bytea{Status: Null} + *dst = Bytea{} return nil } @@ -143,7 +130,7 @@ func (dst *Bytea) Scan(src interface{}) error { case []byte: buf := make([]byte, len(src)) copy(buf, src) - *dst = Bytea{Bytes: buf, Status: Present} + *dst = Bytea{Bytes: buf, Valid: true} return nil } @@ -152,12 +139,8 @@ func (dst *Bytea) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Bytea) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bytes, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return src.Bytes, nil } diff --git a/bytea_array.go b/bytea_array.go index 69d1ceb9..7c539e21 100644 --- a/bytea_array.go +++ b/bytea_array.go @@ -14,13 +14,13 @@ import ( type ByteaArray struct { Elements []Bytea Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *ByteaArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} return nil } @@ -36,9 +36,9 @@ func (dst *ByteaArray) Set(src interface{}) error { case [][]byte: if value == nil { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} } else if len(value) == 0 { - *dst = ByteaArray{Status: Present} + *dst = ByteaArray{Valid: true} } else { elements := make([]Bytea, len(value)) for i := range value { @@ -49,20 +49,20 @@ func (dst *ByteaArray) Set(src interface{}) error { *dst = ByteaArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Bytea: if value == nil { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} } else if len(value) == 0 { - *dst = ByteaArray{Status: Present} + *dst = ByteaArray{Valid: true} } else { *dst = ByteaArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -71,7 +71,7 @@ func (dst *ByteaArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} return nil } @@ -80,7 +80,7 @@ func (dst *ByteaArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for ByteaArray", src) } if elementsLength == 0 { - *dst = ByteaArray{Status: Present} + *dst = ByteaArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -93,7 +93,7 @@ func (dst *ByteaArray) Set(src interface{}) error { *dst = ByteaArray{ Elements: make([]Bytea, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -160,75 +160,68 @@ func (dst *ByteaArray) setRecursive(value reflect.Value, index, dimension int) ( } func (dst ByteaArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *ByteaArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[][]byte: - *v = make([][]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[][]byte: + *v = make([][]byte, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -280,7 +273,7 @@ func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension i func (dst *ByteaArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} return nil } @@ -309,14 +302,14 @@ func (dst *ByteaArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = ByteaArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = ByteaArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = ByteaArray{Status: Null} + *dst = ByteaArray{} return nil } @@ -327,7 +320,7 @@ func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = ByteaArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = ByteaArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -352,16 +345,13 @@ func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = ByteaArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = ByteaArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src ByteaArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -414,11 +404,8 @@ func (src ByteaArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src ByteaArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -432,7 +419,7 @@ func (src ByteaArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/bytea_array_test.go b/bytea_array_test.go index 27c0382e..1473eb9c 100644 --- a/bytea_array_test.go +++ b/bytea_array_test.go @@ -13,41 +13,41 @@ func TestByteaArrayTranscode(t *testing.T) { &pgtype.ByteaArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.ByteaArray{Status: pgtype.Null}, + &pgtype.ByteaArray{}, &pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: []byte{1}, Status: pgtype.Present}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{}, Valid: true}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {}, + {Bytes: []byte{1}, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{}, Status: pgtype.Present}, - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{1}, Status: pgtype.Present}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{}, Valid: true}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{1}, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,61 +60,61 @@ func TestByteaArraySet(t *testing.T) { { source: [][]byte{{1, 2, 3}}, result: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([][]byte)(nil)), - result: pgtype.ByteaArray{Status: pgtype.Null}, + result: pgtype.ByteaArray{}, }, { source: [][][]byte{{{1}}, {{2}}}, result: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1}, Status: pgtype.Present}, {Bytes: []byte{2}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}}, result: pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{4, 5, 6}, Status: pgtype.Present}, - {Bytes: []byte{7, 8, 9}, Status: pgtype.Present}, - {Bytes: []byte{10, 11, 12}, Status: pgtype.Present}, - {Bytes: []byte{13, 14, 15}, Status: pgtype.Present}, - {Bytes: []byte{16, 17, 18}, Status: pgtype.Present}}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{4, 5, 6}, Valid: true}, + {Bytes: []byte{7, 8, 9}, Valid: true}, + {Bytes: []byte{10, 11, 12}, Valid: true}, + {Bytes: []byte{13, 14, 15}, Valid: true}, + {Bytes: []byte{16, 17, 18}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][]byte{{{1}}, {{2}}}, result: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1}, Status: pgtype.Present}, {Bytes: []byte{2}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}}, result: pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{4, 5, 6}, Status: pgtype.Present}, - {Bytes: []byte{7, 8, 9}, Status: pgtype.Present}, - {Bytes: []byte{10, 11, 12}, Status: pgtype.Present}, - {Bytes: []byte{13, 14, 15}, Status: pgtype.Present}, - {Bytes: []byte{16, 17, 18}, Status: pgtype.Present}}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{4, 5, 6}, Valid: true}, + {Bytes: []byte{7, 8, 9}, Valid: true}, + {Bytes: []byte{10, 11, 12}, Valid: true}, + {Bytes: []byte{13, 14, 15}, Valid: true}, + {Bytes: []byte{16, 17, 18}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -145,72 +145,72 @@ func TestByteaArrayAssignTo(t *testing.T) { }{ { src: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &byteByteSlice, expected: [][]byte{{1, 2, 3}}, }, { - src: pgtype.ByteaArray{Status: pgtype.Null}, + src: pgtype.ByteaArray{}, dst: &byteByteSlice, expected: (([][]byte)(nil)), }, { - src: pgtype.ByteaArray{Status: pgtype.Present}, + src: pgtype.ByteaArray{Valid: true}, dst: &byteByteSlice, expected: [][]byte{}, }, { src: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1}, Status: pgtype.Present}, {Bytes: []byte{2}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteByteSliceDim2, expected: [][][]byte{{{1}}, {{2}}}, }, { src: pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{4, 5, 6}, Status: pgtype.Present}, - {Bytes: []byte{7, 8, 9}, Status: pgtype.Present}, - {Bytes: []byte{10, 11, 12}, Status: pgtype.Present}, - {Bytes: []byte{13, 14, 15}, Status: pgtype.Present}, - {Bytes: []byte{16, 17, 18}, Status: pgtype.Present}}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{4, 5, 6}, Valid: true}, + {Bytes: []byte{7, 8, 9}, Valid: true}, + {Bytes: []byte{10, 11, 12}, Valid: true}, + {Bytes: []byte{13, 14, 15}, Valid: true}, + {Bytes: []byte{16, 17, 18}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteByteSliceDim4, expected: [][][][][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}}, }, { src: pgtype.ByteaArray{ - Elements: []pgtype.Bytea{{Bytes: []byte{1}, Status: pgtype.Present}, {Bytes: []byte{2}, Status: pgtype.Present}}, + Elements: []pgtype.Bytea{{Bytes: []byte{1}, Valid: true}, {Bytes: []byte{2}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteByteArraySliceDim2, expected: [2][1][]byte{{{1}}, {{2}}}, }, { src: pgtype.ByteaArray{ Elements: []pgtype.Bytea{ - {Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - {Bytes: []byte{4, 5, 6}, Status: pgtype.Present}, - {Bytes: []byte{7, 8, 9}, Status: pgtype.Present}, - {Bytes: []byte{10, 11, 12}, Status: pgtype.Present}, - {Bytes: []byte{13, 14, 15}, Status: pgtype.Present}, - {Bytes: []byte{16, 17, 18}, Status: pgtype.Present}}, + {Bytes: []byte{1, 2, 3}, Valid: true}, + {Bytes: []byte{4, 5, 6}, Valid: true}, + {Bytes: []byte{7, 8, 9}, Valid: true}, + {Bytes: []byte{10, 11, 12}, Valid: true}, + {Bytes: []byte{13, 14, 15}, Valid: true}, + {Bytes: []byte{16, 17, 18}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteByteArraySliceDim4, expected: [2][1][1][3][]byte{{{{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}}}, {{{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}}}}, }, diff --git a/bytea_test.go b/bytea_test.go index c8c49ff7..0f47cb7f 100644 --- a/bytea_test.go +++ b/bytea_test.go @@ -10,9 +10,9 @@ import ( func TestByteaTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "bytea", []interface{}{ - &pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, - &pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present}, - &pgtype.Bytea{Bytes: nil, Status: pgtype.Null}, + &pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, + &pgtype.Bytea{Bytes: []byte{}, Valid: true}, + &pgtype.Bytea{Bytes: nil}, }) } @@ -21,11 +21,11 @@ func TestByteaSet(t *testing.T) { source interface{} result pgtype.Bytea }{ - {source: []byte{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - {source: []byte{}, result: pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present}}, - {source: []byte(nil), result: pgtype.Bytea{Status: pgtype.Null}}, - {source: _byteSlice{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}}, - {source: _byteSlice(nil), result: pgtype.Bytea{Status: pgtype.Null}}, + {source: []byte{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}}, + {source: []byte{}, result: pgtype.Bytea{Bytes: []byte{}, Valid: true}}, + {source: []byte(nil), result: pgtype.Bytea{}}, + {source: _byteSlice{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}}, + {source: _byteSlice(nil), result: pgtype.Bytea{}}, } for i, tt := range successfulTests { @@ -52,12 +52,12 @@ func TestByteaAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &buf, expected: []byte{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_buf, expected: _byteSlice{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &pbuf, expected: &[]byte{1, 2, 3}}, - {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_pbuf, expected: &_byteSlice{1, 2, 3}}, - {src: pgtype.Bytea{Status: pgtype.Null}, dst: &pbuf, expected: ((*[]byte)(nil))}, - {src: pgtype.Bytea{Status: pgtype.Null}, dst: &_pbuf, expected: ((*_byteSlice)(nil))}, + {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &buf, expected: []byte{1, 2, 3}}, + {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &_buf, expected: _byteSlice{1, 2, 3}}, + {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &pbuf, expected: &[]byte{1, 2, 3}}, + {src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Valid: true}, dst: &_pbuf, expected: &_byteSlice{1, 2, 3}}, + {src: pgtype.Bytea{}, dst: &pbuf, expected: ((*[]byte)(nil))}, + {src: pgtype.Bytea{}, dst: &_pbuf, expected: ((*_byteSlice)(nil))}, } for i, tt := range simpleTests { diff --git a/cid_test.go b/cid_test.go index 5b1150eb..041cb805 100644 --- a/cid_test.go +++ b/cid_test.go @@ -11,8 +11,8 @@ import ( func TestCIDTranscode(t *testing.T) { pgTypeName := "cid" values := []interface{}{ - &pgtype.CID{Uint: 42, Status: pgtype.Present}, - &pgtype.CID{Status: pgtype.Null}, + &pgtype.CID{Uint: 42, Valid: true}, + &pgtype.CID{}, } eqFunc := func(a, b interface{}) bool { return reflect.DeepEqual(a, b) @@ -27,7 +27,7 @@ func TestCIDSet(t *testing.T) { source interface{} result pgtype.CID }{ - {source: uint32(1), result: pgtype.CID{Uint: 1, Status: pgtype.Present}}, + {source: uint32(1), result: pgtype.CID{Uint: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -52,8 +52,8 @@ func TestCIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.CID{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, + {src: pgtype.CID{Uint: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.CID{}, dst: &pui32, expected: ((*uint32)(nil))}, } for i, tt := range simpleTests { @@ -72,7 +72,7 @@ func TestCIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, + {src: pgtype.CID{Uint: 42, Valid: true}, dst: &pui32, expected: uint32(42)}, } for i, tt := range pointerAllocTests { @@ -90,7 +90,7 @@ func TestCIDAssignTo(t *testing.T) { src pgtype.CID dst interface{} }{ - {src: pgtype.CID{Status: pgtype.Null}, dst: &ui32}, + {src: pgtype.CID{}, dst: &ui32}, } for i, tt := range errorTests { diff --git a/cidr_array.go b/cidr_array.go index 783c599c..48a6a4c1 100644 --- a/cidr_array.go +++ b/cidr_array.go @@ -15,13 +15,13 @@ import ( type CIDRArray struct { Elements []CIDR Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *CIDRArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} return nil } @@ -37,9 +37,9 @@ func (dst *CIDRArray) Set(src interface{}) error { case []*net.IPNet: if value == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} + *dst = CIDRArray{Valid: true} } else { elements := make([]CIDR, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *CIDRArray) Set(src interface{}) error { *dst = CIDRArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []net.IP: if value == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} + *dst = CIDRArray{Valid: true} } else { elements := make([]CIDR, len(value)) for i := range value { @@ -69,15 +69,15 @@ func (dst *CIDRArray) Set(src interface{}) error { *dst = CIDRArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*net.IP: if value == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} + *dst = CIDRArray{Valid: true} } else { elements := make([]CIDR, len(value)) for i := range value { @@ -88,20 +88,20 @@ func (dst *CIDRArray) Set(src interface{}) error { *dst = CIDRArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []CIDR: if value == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} } else if len(value) == 0 { - *dst = CIDRArray{Status: Present} + *dst = CIDRArray{Valid: true} } else { *dst = CIDRArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -110,7 +110,7 @@ func (dst *CIDRArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} return nil } @@ -119,7 +119,7 @@ func (dst *CIDRArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for CIDRArray", src) } if elementsLength == 0 { - *dst = CIDRArray{Status: Present} + *dst = CIDRArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -132,7 +132,7 @@ func (dst *CIDRArray) Set(src interface{}) error { *dst = CIDRArray{ Elements: make([]CIDR, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -199,93 +199,86 @@ func (dst *CIDRArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst CIDRArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *CIDRArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]*net.IPNet: - *v = make([]*net.IPNet, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]net.IP: - *v = make([]net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*net.IP: - *v = make([]*net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]*net.IPNet: + *v = make([]*net.IPNet, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]net.IP: + *v = make([]net.IP, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*net.IP: + *v = make([]*net.IP, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *CIDRArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -337,7 +330,7 @@ func (src *CIDRArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *CIDRArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} return nil } @@ -366,14 +359,14 @@ func (dst *CIDRArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = CIDRArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = CIDRArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *CIDRArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = CIDRArray{Status: Null} + *dst = CIDRArray{} return nil } @@ -384,7 +377,7 @@ func (dst *CIDRArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = CIDRArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = CIDRArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -409,16 +402,13 @@ func (dst *CIDRArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = CIDRArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = CIDRArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src CIDRArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -471,11 +461,8 @@ func (src CIDRArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src CIDRArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -489,7 +476,7 @@ func (src CIDRArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/cidr_array_test.go b/cidr_array_test.go index 74c063fa..7821cf44 100644 --- a/cidr_array_test.go +++ b/cidr_array_test.go @@ -14,41 +14,41 @@ func TestCIDRArrayTranscode(t *testing.T) { &pgtype.CIDRArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {Status: pgtype.Null}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.CIDRArray{Status: pgtype.Null}, + &pgtype.CIDRArray{}, &pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - {Status: pgtype.Null}, - {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Valid: true}, + {}, + {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -61,33 +61,33 @@ func TestCIDRArraySet(t *testing.T) { { source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, result: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]*net.IPNet)(nil)), - result: pgtype.CIDRArray{Status: pgtype.Null}, + result: pgtype.CIDRArray{}, }, { source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, result: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]net.IP)(nil)), - result: pgtype.CIDRArray{Status: pgtype.Null}, + result: pgtype.CIDRArray{}, }, { source: [][]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, result: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]*net.IPNet{ @@ -101,27 +101,27 @@ func TestCIDRArraySet(t *testing.T) { mustParseCIDR(t, "169.168.0.1/16")}}}}, result: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, result: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]*net.IPNet{ @@ -135,18 +135,18 @@ func TestCIDRArraySet(t *testing.T) { mustParseCIDR(t, "169.168.0.1/16")}}}}, result: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -178,85 +178,85 @@ func TestCIDRArrayAssignTo(t *testing.T) { }{ { src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipnetSlice, expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, }, { src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{Status: pgtype.Null}}, + Elements: []pgtype.CIDR{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipnetSlice, expected: []*net.IPNet{nil}, }, { src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipSlice, expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, }, { src: pgtype.CIDRArray{ - Elements: []pgtype.CIDR{{Status: pgtype.Null}}, + Elements: []pgtype.CIDR{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipSlice, expected: []net.IP{nil}, }, { - src: pgtype.CIDRArray{Status: pgtype.Null}, + src: pgtype.CIDRArray{}, dst: &ipnetSlice, expected: (([]*net.IPNet)(nil)), }, { - src: pgtype.CIDRArray{Status: pgtype.Present}, + src: pgtype.CIDRArray{Valid: true}, dst: &ipnetSlice, expected: []*net.IPNet{}, }, { - src: pgtype.CIDRArray{Status: pgtype.Null}, + src: pgtype.CIDRArray{}, dst: &ipSlice, expected: (([]net.IP)(nil)), }, { - src: pgtype.CIDRArray{Status: pgtype.Present}, + src: pgtype.CIDRArray{Valid: true}, dst: &ipSlice, expected: []net.IP{}, }, { src: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipSliceDim2, expected: [][]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, }, { src: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipnetSliceDim4, expected: [][][][]*net.IPNet{ {{{ @@ -271,28 +271,28 @@ func TestCIDRArrayAssignTo(t *testing.T) { { src: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipArrayDim2, expected: [2][1]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, }, { src: pgtype.CIDRArray{ Elements: []pgtype.CIDR{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipnetArrayDim4, expected: [2][1][1][3]*net.IPNet{ {{{ diff --git a/circle.go b/circle.go index 4279650e..7524d7b9 100644 --- a/circle.go +++ b/circle.go @@ -12,9 +12,9 @@ import ( ) type Circle struct { - P Vec2 - R float64 - Status Status + P Vec2 + R float64 + Valid bool } func (dst *Circle) Set(src interface{}) error { @@ -22,14 +22,10 @@ func (dst *Circle) Set(src interface{}) error { } func (dst Circle) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Circle) AssignTo(dst interface{}) error { @@ -38,7 +34,7 @@ func (src *Circle) AssignTo(dst interface{}) error { func (dst *Circle) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Circle{Status: Null} + *dst = Circle{} return nil } @@ -68,13 +64,13 @@ func (dst *Circle) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Circle{P: Vec2{x, y}, R: r, Status: Present} + *dst = Circle{P: Vec2{x, y}, R: r, Valid: true} return nil } func (dst *Circle) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Circle{Status: Null} + *dst = Circle{} return nil } @@ -87,19 +83,16 @@ func (dst *Circle) DecodeBinary(ci *ConnInfo, src []byte) error { r := binary.BigEndian.Uint64(src[16:]) *dst = Circle{ - P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, - R: math.Float64frombits(r), - Status: Present, + P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, + R: math.Float64frombits(r), + Valid: true, } return nil } func (src Circle) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`, @@ -112,11 +105,8 @@ func (src Circle) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Circle) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X)) @@ -128,7 +118,7 @@ func (src Circle) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Circle) Scan(src interface{}) error { if src == nil { - *dst = Circle{Status: Null} + *dst = Circle{} return nil } diff --git a/circle_test.go b/circle_test.go index ba4f408b..416a1a41 100644 --- a/circle_test.go +++ b/circle_test.go @@ -9,8 +9,8 @@ import ( func TestCircleTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "circle", []interface{}{ - &pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Status: pgtype.Present}, - &pgtype.Circle{P: pgtype.Vec2{-1.234, -5.6789}, R: 12.9, Status: pgtype.Present}, - &pgtype.Circle{Status: pgtype.Null}, + &pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true}, + &pgtype.Circle{P: pgtype.Vec2{-1.234, -5.6789}, R: 12.9, Valid: true}, + &pgtype.Circle{}, }) } diff --git a/composite_bench_test.go b/composite_bench_test.go index 7aef8c4f..a1d91f8e 100644 --- a/composite_bench_test.go +++ b/composite_bench_test.go @@ -44,7 +44,7 @@ func (dst *MyCompositeRaw) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { } dst.A = a.Int - if b.Status == pgtype.Present { + if b.Valid { dst.B = &b.String } else { dst.B = nil diff --git a/composite_fields_test.go b/composite_fields_test.go index dc4d4c29..be0b8125 100644 --- a/composite_fields_test.go +++ b/composite_fields_test.go @@ -230,7 +230,7 @@ create type cf_encode as ( for _, simpleProtocol := range simpleProtocols { err := conn.QueryRow(context.Background(), "select $1::cf_encode", pgx.QuerySimpleProtocol(simpleProtocol), - pgtype.CompositeFields{&pgtype.Text{Status: pgtype.Null}, int32(1), "null", &pgtype.Float8{Status: pgtype.Null}, &pgtype.Text{Status: pgtype.Null}}, + pgtype.CompositeFields{&pgtype.Text{}, int32(1), "null", &pgtype.Float8{}, &pgtype.Text{}}, ).Scan( pgtype.CompositeFields{&a, &b, &c, &d, &e}, ) diff --git a/composite_type.go b/composite_type.go index 32e0aa26..90b7b6ff 100644 --- a/composite_type.go +++ b/composite_type.go @@ -16,7 +16,7 @@ type CompositeTypeField struct { } type CompositeType struct { - status Status + valid bool typeName string @@ -58,18 +58,15 @@ func NewCompositeTypeValues(typeName string, fields []CompositeTypeField, values } func (src CompositeType) Get() interface{} { - switch src.status { - case Present: - results := make(map[string]interface{}, len(src.valueTranscoders)) - for i := range src.valueTranscoders { - results[src.fields[i].Name] = src.valueTranscoders[i].Get() - } - return results - case Null: + if !src.valid { return nil - default: - return src.status } + + results := make(map[string]interface{}, len(src.valueTranscoders)) + for i := range src.valueTranscoders { + results[src.fields[i].Name] = src.valueTranscoders[i].Get() + } + return results } func (ct *CompositeType) NewTypeValue() Value { @@ -96,7 +93,7 @@ func (ct *CompositeType) Fields() []CompositeTypeField { func (dst *CompositeType) Set(src interface{}) error { if src == nil { - dst.status = Null + dst.valid = false return nil } @@ -110,10 +107,10 @@ func (dst *CompositeType) Set(src interface{}) error { return err } } - dst.status = Present + dst.valid = true case *[]interface{}: if value == nil { - dst.status = Null + dst.valid = false return nil } return dst.Set(*value) @@ -126,40 +123,38 @@ func (dst *CompositeType) Set(src interface{}) error { // AssignTo should never be called on composite value directly func (src CompositeType) AssignTo(dst interface{}) error { - switch src.status { - case Present: - switch v := dst.(type) { - case []interface{}: - if len(v) != len(src.valueTranscoders) { - return fmt.Errorf("Number of fields don't match. CompositeType has %d fields", len(src.valueTranscoders)) - } - for i := range src.valueTranscoders { - if v[i] == nil { - continue - } - - err := assignToOrSet(src.valueTranscoders[i], v[i]) - if err != nil { - return fmt.Errorf("unable to assign to dst[%d]: %v", i, err) - } - } - return nil - case *[]interface{}: - return src.AssignTo(*v) - default: - if isPtrStruct, err := src.assignToPtrStruct(dst); isPtrStruct { - return err - } - - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + + switch v := dst.(type) { + case []interface{}: + if len(v) != len(src.valueTranscoders) { + return fmt.Errorf("Number of fields don't match. CompositeType has %d fields", len(src.valueTranscoders)) + } + for i := range src.valueTranscoders { + if v[i] == nil { + continue + } + + err := assignToOrSet(src.valueTranscoders[i], v[i]) + if err != nil { + return fmt.Errorf("unable to assign to dst[%d]: %v", i, err) + } + } + return nil + case *[]interface{}: + return src.AssignTo(*v) + default: + if isPtrStruct, err := src.assignToPtrStruct(dst); isPtrStruct { + return err + } + + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func assignToOrSet(src Value, dst interface{}) error { @@ -219,11 +214,8 @@ func (src CompositeType) assignToPtrStruct(dst interface{}) (bool, error) { } func (src CompositeType) EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error) { - switch src.status { - case Null: + if !src.valid { return nil, nil - case Undefined: - return nil, errUndefined } b := NewCompositeBinaryBuilder(ci, buf) @@ -240,7 +232,7 @@ func (src CompositeType) EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, // type mismatch func (dst *CompositeType) DecodeBinary(ci *ConnInfo, buf []byte) error { if buf == nil { - dst.status = Null + dst.valid = false return nil } @@ -254,14 +246,14 @@ func (dst *CompositeType) DecodeBinary(ci *ConnInfo, buf []byte) error { return scanner.Err() } - dst.status = Present + dst.valid = true return nil } func (dst *CompositeType) DecodeText(ci *ConnInfo, buf []byte) error { if buf == nil { - dst.status = Null + dst.valid = false return nil } @@ -275,17 +267,14 @@ func (dst *CompositeType) DecodeText(ci *ConnInfo, buf []byte) error { return scanner.Err() } - dst.status = Present + dst.valid = true return nil } func (src CompositeType) EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error) { - switch src.status { - case Null: + if !src.valid { return nil, nil - case Undefined: - return nil, errUndefined } b := NewCompositeTextBuilder(ci, buf) diff --git a/composite_type_test.go b/composite_type_test.go index 2349a67d..e06927fa 100644 --- a/composite_type_test.go +++ b/composite_type_test.go @@ -20,7 +20,7 @@ func TestCompositeTypeSetAndGet(t *testing.T) { {"b", pgtype.Int4OID}, }, ci) require.NoError(t, err) - assert.Equal(t, pgtype.Undefined, ct.Get()) + assert.Equal(t, nil, ct.Get()) nilTests := []struct { src interface{} @@ -48,7 +48,7 @@ func TestCompositeTypeSetAndGet(t *testing.T) { expected: map[string]interface{}{"a": nil, "b": nil}, }, { - src: []interface{}{&pgtype.Text{String: "hi", Status: pgtype.Present}, &pgtype.Int4{Int: 7, Status: pgtype.Present}}, + src: []interface{}{&pgtype.Text{String: "hi", Valid: true}, &pgtype.Int4{Int: 7, Valid: true}}, expected: map[string]interface{}{"a": "hi", "b": int32(7)}, }, } @@ -92,8 +92,8 @@ func TestCompositeTypeAssignTo(t *testing.T) { err = ct.AssignTo([]interface{}{&a, &b}) assert.NoError(t, err) - assert.Equal(t, pgtype.Text{String: "foo", Status: pgtype.Present}, a) - assert.Equal(t, pgtype.Int4{Int: 42, Status: pgtype.Present}, b) + assert.Equal(t, pgtype.Text{String: "foo", Valid: true}, a) + assert.Equal(t, pgtype.Int4{Int: 42, Valid: true}, b) } // Allow nil destination component as no-op @@ -137,8 +137,8 @@ func TestCompositeTypeAssignTo(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, dst) - assert.Equal(t, pgtype.Text{String: "foo", Status: pgtype.Present}, a) - assert.Equal(t, pgtype.Int4{Int: 42, Status: pgtype.Present}, b) + assert.Equal(t, pgtype.Text{String: "foo", Valid: true}, a) + assert.Equal(t, pgtype.Int4{Int: 42, Valid: true}, b) } // Struct fields positionally via reflection diff --git a/convert.go b/convert.go index de9ba9ba..21e208f5 100644 --- a/convert.go +++ b/convert.go @@ -208,8 +208,8 @@ func underlyingSliceType(val interface{}) (interface{}, bool) { return nil, false } -func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error { - if srcStatus == Present { +func int64AssignTo(srcVal int64, srcValid bool, dst interface{}) error { + if srcValid { switch v := dst.(type) { case *int: if srcVal < int64(minInt) { @@ -291,7 +291,7 @@ func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error { // allocate destination el.Set(reflect.New(el.Type().Elem())) } - return int64AssignTo(srcVal, srcStatus, el.Interface()) + return int64AssignTo(srcVal, srcValid, el.Interface()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if el.OverflowInt(int64(srcVal)) { return fmt.Errorf("cannot put %d into %T", srcVal, dst) @@ -314,7 +314,7 @@ func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error { return nil } - // if dst is a pointer to pointer and srcStatus is not Present, nil it out + // if dst is a pointer to pointer and srcStatus is not Valid, nil it out if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { el := v.Elem() if el.Kind() == reflect.Ptr { @@ -323,11 +323,11 @@ func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error { } } - return fmt.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst) + return fmt.Errorf("cannot assign %v %v into %T", srcVal, srcValid, dst) } -func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { - if srcStatus == Present { +func float64AssignTo(srcVal float64, srcValid bool, dst interface{}) error { + if srcValid { switch v := dst.(type) { case *float32: *v = float32(srcVal) @@ -343,11 +343,11 @@ func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { // allocate destination el.Set(reflect.New(el.Type().Elem())) } - return float64AssignTo(srcVal, srcStatus, el.Interface()) + return float64AssignTo(srcVal, srcValid, el.Interface()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: i64 := int64(srcVal) if float64(i64) == srcVal { - return int64AssignTo(i64, srcStatus, dst) + return int64AssignTo(i64, srcValid, dst) } } } @@ -356,7 +356,7 @@ func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { return nil } - // if dst is a pointer to pointer and srcStatus is not Present, nil it out + // if dst is a pointer to pointer and srcStatus is not Valid, nil it out if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { el := v.Elem() if el.Kind() == reflect.Ptr { @@ -365,7 +365,7 @@ func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error { } } - return fmt.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst) + return fmt.Errorf("cannot assign %v %v into %T", srcVal, srcValid, dst) } func NullAssignTo(dst interface{}) error { diff --git a/custom_composite_test.go b/custom_composite_test.go index 9ca8dd5e..86203828 100644 --- a/custom_composite_test.go +++ b/custom_composite_test.go @@ -28,12 +28,12 @@ func (dst *MyType) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { } func (src MyType) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) (newBuf []byte, err error) { - a := pgtype.Int4{src.a, pgtype.Present} + a := pgtype.Int4{src.a, true} var b pgtype.Text if src.b != nil { - b = pgtype.Text{*src.b, pgtype.Present} + b = pgtype.Text{*src.b, true} } else { - b = pgtype.Text{Status: pgtype.Null} + b = pgtype.Text{} } return (pgtype.CompositeFields{&a, &b}).EncodeBinary(ci, buf) diff --git a/date.go b/date.go index e8d21a78..5b7f47e6 100644 --- a/date.go +++ b/date.go @@ -12,7 +12,7 @@ import ( type Date struct { Time time.Time - Status Status + Valid bool InfinityModifier InfinityModifier } @@ -23,7 +23,7 @@ const ( func (dst *Date) Set(src interface{}) error { if src == nil { - *dst = Date{Status: Null} + *dst = Date{} return nil } @@ -36,18 +36,18 @@ func (dst *Date) Set(src interface{}) error { switch value := src.(type) { case time.Time: - *dst = Date{Time: value, Status: Present} + *dst = Date{Time: value, Valid: true} case string: return dst.DecodeText(nil, []byte(value)) case *time.Time: if value == nil { - *dst = Date{Status: Null} + *dst = Date{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Date{Status: Null} + *dst = Date{} } else { return dst.Set(*value) } @@ -62,61 +62,54 @@ func (dst *Date) Set(src interface{}) error { } func (dst Date) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + if dst.InfinityModifier != None { + return dst.InfinityModifier + } + return dst.Time } func (src *Date) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return fmt.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *time.Time: + if src.InfinityModifier != None { + return fmt.Errorf("cannot assign %v to %T", src, dst) + } + *v = src.Time + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Date{Status: Null} + *dst = Date{} return nil } sbuf := string(src) switch sbuf { case "infinity": - *dst = Date{Status: Present, InfinityModifier: Infinity} + *dst = Date{Valid: true, InfinityModifier: Infinity} case "-infinity": - *dst = Date{Status: Present, InfinityModifier: -Infinity} + *dst = Date{Valid: true, InfinityModifier: -Infinity} default: t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC) if err != nil { return err } - *dst = Date{Time: t, Status: Present} + *dst = Date{Time: t, Valid: true} } return nil @@ -124,7 +117,7 @@ func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Date{Status: Null} + *dst = Date{} return nil } @@ -136,23 +129,20 @@ func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error { switch dayOffset { case infinityDayOffset: - *dst = Date{Status: Present, InfinityModifier: Infinity} + *dst = Date{Valid: true, InfinityModifier: Infinity} case negativeInfinityDayOffset: - *dst = Date{Status: Present, InfinityModifier: -Infinity} + *dst = Date{Valid: true, InfinityModifier: -Infinity} default: t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC) - *dst = Date{Time: t, Status: Present} + *dst = Date{Time: t, Valid: true} } return nil } func (src Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var s string @@ -170,11 +160,8 @@ func (src Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var daysSinceDateEpoch int32 @@ -197,7 +184,7 @@ func (src Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Date) Scan(src interface{}) error { if src == nil { - *dst = Date{Status: Null} + *dst = Date{} return nil } @@ -209,7 +196,7 @@ func (dst *Date) Scan(src interface{}) error { copy(srcCopy, src) return dst.DecodeText(nil, srcCopy) case time.Time: - *dst = Date{Time: src, Status: Present} + *dst = Date{Time: src, Valid: true} return nil } @@ -218,29 +205,19 @@ func (dst *Date) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Date) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + if src.InfinityModifier != None { + return src.InfinityModifier.String(), nil + } + return src.Time, nil } func (src Date) MarshalJSON() ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - if src.Status != Present { - return nil, errBadStatus } var s string @@ -265,22 +242,22 @@ func (dst *Date) UnmarshalJSON(b []byte) error { } if s == nil { - *dst = Date{Status: Null} + *dst = Date{} return nil } switch *s { case "infinity": - *dst = Date{Status: Present, InfinityModifier: Infinity} + *dst = Date{Valid: true, InfinityModifier: Infinity} case "-infinity": - *dst = Date{Status: Present, InfinityModifier: -Infinity} + *dst = Date{Valid: true, InfinityModifier: -Infinity} default: t, err := time.ParseInLocation("2006-01-02", *s, time.UTC) if err != nil { return err } - *dst = Date{Time: t, Status: Present} + *dst = Date{Time: t, Valid: true} } return nil diff --git a/date_array.go b/date_array.go index 24152fa0..9d3b32e2 100644 --- a/date_array.go +++ b/date_array.go @@ -15,13 +15,13 @@ import ( type DateArray struct { Elements []Date Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *DateArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} return nil } @@ -37,9 +37,9 @@ func (dst *DateArray) Set(src interface{}) error { case []time.Time: if value == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} } else if len(value) == 0 { - *dst = DateArray{Status: Present} + *dst = DateArray{Valid: true} } else { elements := make([]Date, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *DateArray) Set(src interface{}) error { *dst = DateArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*time.Time: if value == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} } else if len(value) == 0 { - *dst = DateArray{Status: Present} + *dst = DateArray{Valid: true} } else { elements := make([]Date, len(value)) for i := range value { @@ -69,20 +69,20 @@ func (dst *DateArray) Set(src interface{}) error { *dst = DateArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Date: if value == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} } else if len(value) == 0 { - *dst = DateArray{Status: Present} + *dst = DateArray{Valid: true} } else { *dst = DateArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -91,7 +91,7 @@ func (dst *DateArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = DateArray{Status: Null} + *dst = DateArray{} return nil } @@ -100,7 +100,7 @@ func (dst *DateArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for DateArray", src) } if elementsLength == 0 { - *dst = DateArray{Status: Present} + *dst = DateArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -113,7 +113,7 @@ func (dst *DateArray) Set(src interface{}) error { *dst = DateArray{ Elements: make([]Date, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -180,84 +180,77 @@ func (dst *DateArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst DateArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *DateArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*time.Time: - *v = make([]*time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]time.Time: + *v = make([]time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*time.Time: + *v = make([]*time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *DateArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -309,7 +302,7 @@ func (src *DateArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *DateArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} return nil } @@ -338,14 +331,14 @@ func (dst *DateArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = DateArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = DateArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *DateArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = DateArray{Status: Null} + *dst = DateArray{} return nil } @@ -356,7 +349,7 @@ func (dst *DateArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = DateArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = DateArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -381,16 +374,13 @@ func (dst *DateArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = DateArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = DateArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src DateArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -443,11 +433,8 @@ func (src DateArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src DateArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -461,7 +448,7 @@ func (src DateArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/date_array_test.go b/date_array_test.go index 4458abfe..421427cd 100644 --- a/date_array_test.go +++ b/date_array_test.go @@ -14,41 +14,41 @@ func TestDateArrayTranscode(t *testing.T) { &pgtype.DateArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.DateArray{Status: pgtype.Null}, + &pgtype.DateArray{}, &pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -61,13 +61,13 @@ func TestDateArraySet(t *testing.T) { { source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, result: pgtype.DateArray{ - Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]time.Time)(nil)), - result: pgtype.DateArray{Status: pgtype.Null}, + result: pgtype.DateArray{}, }, { source: [][]time.Time{ @@ -75,10 +75,10 @@ func TestDateArraySet(t *testing.T) { {time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC)}}, result: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]time.Time{ @@ -92,18 +92,18 @@ func TestDateArraySet(t *testing.T) { time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC)}}}}, result: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]time.Time{ @@ -111,10 +111,10 @@ func TestDateArraySet(t *testing.T) { {time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC)}}, result: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]time.Time{ @@ -128,18 +128,18 @@ func TestDateArraySet(t *testing.T) { time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC)}}}}, result: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -170,30 +170,30 @@ func TestDateArrayAssignTo(t *testing.T) { }{ { src: pgtype.DateArray{ - Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, }, { - src: pgtype.DateArray{Status: pgtype.Null}, + src: pgtype.DateArray{}, dst: &timeSlice, expected: (([]time.Time)(nil)), }, { - src: pgtype.DateArray{Status: pgtype.Present}, + src: pgtype.DateArray{Valid: true}, dst: &timeSlice, expected: []time.Time{}, }, { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim2, expected: [][]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -202,18 +202,18 @@ func TestDateArrayAssignTo(t *testing.T) { { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim4, expected: [][][][]time.Time{ {{{ @@ -228,10 +228,10 @@ func TestDateArrayAssignTo(t *testing.T) { { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, expected: [2][1]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -240,18 +240,18 @@ func TestDateArrayAssignTo(t *testing.T) { { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, expected: [2][1][1][3]time.Time{ {{{ @@ -282,37 +282,37 @@ func TestDateArrayAssignTo(t *testing.T) { }{ { src: pgtype.DateArray{ - Elements: []pgtype.Date{{Status: pgtype.Null}}, + Elements: []pgtype.Date{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, }, { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, }, { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSlice, }, { src: pgtype.DateArray{ Elements: []pgtype.Date{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, }, } diff --git a/date_test.go b/date_test.go index 5c38e7a3..87425540 100644 --- a/date_test.go +++ b/date_test.go @@ -11,20 +11,20 @@ import ( func TestDateTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "date", []interface{}{ - &pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Date{Status: pgtype.Null}, - &pgtype.Date{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Date{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, + &pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Date{}, + &pgtype.Date{Valid: true, InfinityModifier: pgtype.Infinity}, + &pgtype.Date{Valid: true, InfinityModifier: -pgtype.Infinity}, }, func(a, b interface{}) bool { at := a.(pgtype.Date) bt := b.(pgtype.Date) - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier + return at.Time.Equal(bt.Time) && at.Valid == bt.Valid && at.InfinityModifier == bt.InfinityModifier }) } @@ -35,14 +35,14 @@ func TestDateSet(t *testing.T) { source interface{} result pgtype.Date }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: "1999-12-31", result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: "1999-12-31", result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}}, } for i, tt := range successfulTests { @@ -67,8 +67,8 @@ func TestDateAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - {src: pgtype.Date{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, + {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, + {src: pgtype.Date{Time: time.Time{}}, dst: &ptim, expected: ((*time.Time)(nil))}, } for i, tt := range simpleTests { @@ -87,7 +87,7 @@ func TestDateAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, + {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, } for i, tt := range pointerAllocTests { @@ -105,9 +105,9 @@ func TestDateAssignTo(t *testing.T) { src pgtype.Date dst interface{} }{ - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, + {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Valid: true}, dst: &tim}, + {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Valid: true}, dst: &tim}, + {src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, dst: &tim}, } for i, tt := range errorTests { @@ -123,12 +123,12 @@ func TestDateMarshalJSON(t *testing.T) { source pgtype.Date result string }{ - {source: pgtype.Date{Status: pgtype.Null}, result: "null"}, - {source: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, result: "\"2012-03-29\""}, - {source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Status: pgtype.Present}, result: "\"2012-03-29\""}, - {source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Status: pgtype.Present}, result: "\"2012-03-29\""}, - {source: pgtype.Date{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, result: "\"infinity\""}, - {source: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, result: "\"-infinity\""}, + {source: pgtype.Date{}, result: "null"}, + {source: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Valid: true}, result: "\"2012-03-29\""}, + {source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Valid: true}, result: "\"2012-03-29\""}, + {source: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Valid: true}, result: "\"2012-03-29\""}, + {source: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}, result: "\"infinity\""}, + {source: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: "\"-infinity\""}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -147,12 +147,12 @@ func TestDateUnmarshalJSON(t *testing.T) { source string result pgtype.Date }{ - {source: "null", result: pgtype.Date{Status: pgtype.Null}}, - {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Status: pgtype.Present}}, - {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Status: pgtype.Present}}, - {source: "\"infinity\"", result: pgtype.Date{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}}, - {source: "\"-infinity\"", result: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}}, + {source: "null", result: pgtype.Date{}}, + {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Valid: true}}, + {source: "\"2012-03-29\"", result: pgtype.Date{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Valid: true}}, + {source: "\"infinity\"", result: pgtype.Date{InfinityModifier: pgtype.Infinity, Valid: true}}, + {source: "\"-infinity\"", result: pgtype.Date{InfinityModifier: pgtype.NegativeInfinity, Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Date @@ -161,7 +161,7 @@ func TestDateUnmarshalJSON(t *testing.T) { t.Errorf("%d: %v", i, err) } - if r.Time.Year() != tt.result.Time.Year() || r.Time.Month() != tt.result.Time.Month() || r.Time.Day() != tt.result.Time.Day() || r.Status != tt.result.Status || r.InfinityModifier != tt.result.InfinityModifier { + if r.Time.Year() != tt.result.Time.Year() || r.Time.Month() != tt.result.Time.Month() || r.Time.Day() != tt.result.Time.Day() || r.Valid != tt.result.Valid || r.InfinityModifier != tt.result.InfinityModifier { t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) } } diff --git a/daterange.go b/daterange.go index 63164a5a..8b0c03f1 100644 --- a/daterange.go +++ b/daterange.go @@ -12,13 +12,13 @@ type Daterange struct { Upper Date LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Daterange) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Daterange{Status: Null} + *dst = Daterange{} return nil } @@ -36,15 +36,11 @@ func (dst *Daterange) Set(src interface{}) error { return nil } -func (dst Daterange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Daterange) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Daterange) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Daterange) AssignTo(dst interface{}) error { func (dst *Daterange) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Daterange{Status: Null} + *dst = Daterange{} return nil } @@ -62,7 +58,7 @@ func (dst *Daterange) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Daterange{Status: Present} + *dst = Daterange{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Daterange) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Daterange) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Daterange{Status: Null} + *dst = Daterange{} return nil } @@ -97,7 +93,7 @@ func (dst *Daterange) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Daterange{Status: Present} + *dst = Daterange{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Daterange) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Daterange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Daterange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Daterange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Daterange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Daterange) Scan(src interface{}) error { if src == nil { - *dst = Daterange{Status: Null} + *dst = Daterange{} return nil } diff --git a/daterange_test.go b/daterange_test.go index 54d51e2d..830942d0 100644 --- a/daterange_test.go +++ b/daterange_test.go @@ -10,32 +10,32 @@ import ( func TestDaterangeTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "daterange", []interface{}{ - &pgtype.Daterange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, + &pgtype.Daterange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, &pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, &pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Daterange{Status: pgtype.Null}, + &pgtype.Daterange{}, }, func(aa, bb interface{}) bool { a := aa.(pgtype.Daterange) b := bb.(pgtype.Daterange) - return a.Status == b.Status && + return a.Valid == b.Valid && a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && + a.Lower.Valid == b.Lower.Valid && a.Lower.InfinityModifier == b.Lower.InfinityModifier && a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && + a.Upper.Valid == b.Upper.Valid && a.Upper.InfinityModifier == b.Upper.InfinityModifier }) } @@ -45,23 +45,23 @@ func TestDaterangeNormalize(t *testing.T) { { SQL: "select daterange('2010-01-01', '2010-01-11', '(]')", Value: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(2010, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2010, 1, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(2010, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2010, 1, 12, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, }, }, func(aa, bb interface{}) bool { a := aa.(pgtype.Daterange) b := bb.(pgtype.Daterange) - return a.Status == b.Status && + return a.Valid == b.Valid && a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && + a.Lower.Valid == b.Lower.Valid && a.Lower.InfinityModifier == b.Lower.InfinityModifier && a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && + a.Upper.Valid == b.Upper.Valid && a.Upper.InfinityModifier == b.Upper.InfinityModifier }) } @@ -73,48 +73,48 @@ func TestDaterangeSet(t *testing.T) { }{ { source: nil, - result: pgtype.Daterange{Status: pgtype.Null}, + result: pgtype.Daterange{}, }, { source: &pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, result: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, }, { source: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, result: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, }, { source: "[1990-12-31,2028-01-01)", result: pgtype.Daterange{ - Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, }, } diff --git a/enum_array.go b/enum_array.go index 59b5a3ed..dbfb211d 100644 --- a/enum_array.go +++ b/enum_array.go @@ -11,13 +11,13 @@ import ( type EnumArray struct { Elements []GenericText Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *EnumArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} return nil } @@ -33,9 +33,9 @@ func (dst *EnumArray) Set(src interface{}) error { case []string: if value == nil { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} } else if len(value) == 0 { - *dst = EnumArray{Status: Present} + *dst = EnumArray{Valid: true} } else { elements := make([]GenericText, len(value)) for i := range value { @@ -46,15 +46,15 @@ func (dst *EnumArray) Set(src interface{}) error { *dst = EnumArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} } else if len(value) == 0 { - *dst = EnumArray{Status: Present} + *dst = EnumArray{Valid: true} } else { elements := make([]GenericText, len(value)) for i := range value { @@ -65,20 +65,20 @@ func (dst *EnumArray) Set(src interface{}) error { *dst = EnumArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []GenericText: if value == nil { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} } else if len(value) == 0 { - *dst = EnumArray{Status: Present} + *dst = EnumArray{Valid: true} } else { *dst = EnumArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -87,7 +87,7 @@ func (dst *EnumArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} return nil } @@ -96,7 +96,7 @@ func (dst *EnumArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for EnumArray", src) } if elementsLength == 0 { - *dst = EnumArray{Status: Present} + *dst = EnumArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -109,7 +109,7 @@ func (dst *EnumArray) Set(src interface{}) error { *dst = EnumArray{ Elements: make([]GenericText, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -176,84 +176,77 @@ func (dst *EnumArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst EnumArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *EnumArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *EnumArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -305,7 +298,7 @@ func (src *EnumArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *EnumArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = EnumArray{Status: Null} + *dst = EnumArray{} return nil } @@ -334,17 +327,14 @@ func (dst *EnumArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = EnumArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = EnumArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (src EnumArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { diff --git a/enum_array_test.go b/enum_array_test.go index 659340f0..7d0ff864 100644 --- a/enum_array_test.go +++ b/enum_array_test.go @@ -24,29 +24,29 @@ func TestEnumArrayTranscode(t *testing.T) { &pgtype.EnumArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "red", Status: pgtype.Present}, - {Status: pgtype.Null}, + {String: "red", Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.EnumArray{Status: pgtype.Null}, + &pgtype.EnumArray{}, &pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "red", Status: pgtype.Present}, - {String: "green", Status: pgtype.Present}, - {String: "blue", Status: pgtype.Present}, - {String: "red", Status: pgtype.Present}, + {String: "red", Valid: true}, + {String: "green", Valid: true}, + {String: "blue", Valid: true}, + {String: "red", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -59,61 +59,61 @@ func TestEnumArrayArraySet(t *testing.T) { { source: []string{"foo"}, result: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]string)(nil)), - result: pgtype.EnumArray{Status: pgtype.Null}, + result: pgtype.EnumArray{}, }, { source: [][]string{{"foo"}, {"bar"}}, result: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]string{{"foo"}, {"bar"}}, result: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -146,81 +146,81 @@ func TestEnumArrayArrayAssignTo(t *testing.T) { }{ { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, expected: []string{"foo"}, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedStringSlice, expected: _stringSlice{"bar"}, }, { - src: pgtype.EnumArray{Status: pgtype.Null}, + src: pgtype.EnumArray{}, dst: &stringSlice, expected: (([]string)(nil)), }, { - src: pgtype.EnumArray{Status: pgtype.Present}, + src: pgtype.EnumArray{Valid: true}, dst: &stringSlice, expected: []string{}, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim2, expected: [][]string{{"foo"}, {"bar"}}, }, { src: pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim4, expected: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, expected: [2][1]string{{"foo"}, {"bar"}}, }, { src: pgtype.EnumArray{ Elements: []pgtype.GenericText{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, expected: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, @@ -243,31 +243,31 @@ func TestEnumArrayArrayAssignTo(t *testing.T) { }{ { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{Status: pgtype.Null}}, + Elements: []pgtype.GenericText{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSlice, }, { src: pgtype.EnumArray{ - Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.GenericText{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, }, } diff --git a/enum_type.go b/enum_type.go index d340320f..73ee3823 100644 --- a/enum_type.go +++ b/enum_type.go @@ -5,8 +5,8 @@ import "fmt" // EnumType represents a enum type. While it implements Value, this is only in service of its type conversion duties // when registered as a data type in a ConnType. It should not be used directly as a Value. type EnumType struct { - value string - status Status + value string + valid bool typeName string // PostgreSQL type name members []string // enum members @@ -25,8 +25,8 @@ func NewEnumType(typeName string, members []string) *EnumType { func (et *EnumType) NewTypeValue() Value { return &EnumType{ - value: et.value, - status: et.status, + value: et.value, + valid: et.valid, typeName: et.typeName, members: et.members, @@ -46,7 +46,7 @@ func (et *EnumType) Members() []string { // operation in the event the PostgreSQL enum type is modified during a connection. func (dst *EnumType) Set(src interface{}) error { if src == nil { - dst.status = Null + dst.valid = false return nil } @@ -60,20 +60,20 @@ func (dst *EnumType) Set(src interface{}) error { switch value := src.(type) { case string: dst.value = value - dst.status = Present + dst.valid = true case *string: if value == nil { - dst.status = Null + dst.valid = false } else { dst.value = *value - dst.status = Present + dst.valid = true } case []byte: if value == nil { - dst.status = Null + dst.valid = false } else { dst.value = string(value) - dst.status = Present + dst.valid = true } default: if originalSrc, ok := underlyingStringType(src); ok { @@ -86,38 +86,31 @@ func (dst *EnumType) Set(src interface{}) error { } func (dst EnumType) Get() interface{} { - switch dst.status { - case Present: - return dst.value - case Null: + if !dst.valid { return nil - default: - return dst.status } + return dst.value } func (src *EnumType) AssignTo(dst interface{}) error { - switch src.status { - case Present: - switch v := dst.(type) { - case *string: - *v = src.value - return nil - case *[]byte: - *v = make([]byte, len(src.value)) - copy(*v, src.value) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *string: + *v = src.value + return nil + case *[]byte: + *v = make([]byte, len(src.value)) + copy(*v, src.value) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (EnumType) PreferredResultFormat() int16 { @@ -126,7 +119,7 @@ func (EnumType) PreferredResultFormat() int16 { func (dst *EnumType) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - dst.status = Null + dst.valid = false return nil } @@ -139,7 +132,7 @@ func (dst *EnumType) DecodeText(ci *ConnInfo, src []byte) error { // and membersMap between connections. dst.value = string(src) } - dst.status = Present + dst.valid = true return nil } @@ -153,11 +146,8 @@ func (EnumType) PreferredParamFormat() int16 { } func (src EnumType) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.status { - case Null: + if !src.valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.value...), nil diff --git a/ext/gofrs-uuid/uuid.go b/ext/gofrs-uuid/uuid.go index a5e0a3c3..0e0ebed3 100644 --- a/ext/gofrs-uuid/uuid.go +++ b/ext/gofrs-uuid/uuid.go @@ -2,24 +2,20 @@ package uuid import ( "database/sql/driver" - "errors" "fmt" "github.com/gofrs/uuid" "github.com/jackc/pgtype" ) -var errUndefined = errors.New("cannot encode status undefined") -var errBadStatus = errors.New("invalid status") - type UUID struct { - UUID uuid.UUID - Status pgtype.Status + UUID uuid.UUID + Valid bool } func (dst *UUID) Set(src interface{}) error { if src == nil { - *dst = UUID{Status: pgtype.Null} + *dst = UUID{} return nil } @@ -32,21 +28,21 @@ func (dst *UUID) Set(src interface{}) error { switch value := src.(type) { case uuid.UUID: - *dst = UUID{UUID: value, Status: pgtype.Present} + *dst = UUID{UUID: value, Valid: true} case [16]byte: - *dst = UUID{UUID: uuid.UUID(value), Status: pgtype.Present} + *dst = UUID{UUID: uuid.UUID(value), Valid: true} case []byte: if len(value) != 16 { return fmt.Errorf("[]byte must be 16 bytes to convert to UUID: %d", len(value)) } - *dst = UUID{Status: pgtype.Present} + *dst = UUID{Valid: true} copy(dst.UUID[:], value) case string: uuid, err := uuid.FromString(value) if err != nil { return err } - *dst = UUID{UUID: uuid, Status: pgtype.Present} + *dst = UUID{UUID: uuid, Valid: true} default: // If all else fails see if pgtype.UUID can handle it. If so, translate through that. pgUUID := &pgtype.UUID{} @@ -54,56 +50,49 @@ func (dst *UUID) Set(src interface{}) error { return fmt.Errorf("cannot convert %v to UUID", value) } - *dst = UUID{UUID: uuid.UUID(pgUUID.Bytes), Status: pgUUID.Status} + *dst = UUID{UUID: uuid.UUID(pgUUID.Bytes), Valid: pgUUID.Valid} } return nil } func (dst UUID) Get() interface{} { - switch dst.Status { - case pgtype.Present: - return dst.UUID - case pgtype.Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.UUID } func (src *UUID) AssignTo(dst interface{}) error { - switch src.Status { - case pgtype.Present: - switch v := dst.(type) { - case *uuid.UUID: - *v = src.UUID - return nil - case *[16]byte: - *v = [16]byte(src.UUID) - return nil - case *[]byte: - *v = make([]byte, 16) - copy(*v, src.UUID[:]) - return nil - case *string: - *v = src.UUID.String() - return nil - default: - if nextDst, retry := pgtype.GetAssignToDstType(v); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case pgtype.Null: + if !src.Valid { return pgtype.NullAssignTo(dst) } - return fmt.Errorf("cannot assign %v into %T", src, dst) + switch v := dst.(type) { + case *uuid.UUID: + *v = src.UUID + return nil + case *[16]byte: + *v = [16]byte(src.UUID) + return nil + case *[]byte: + *v = make([]byte, 16) + copy(*v, src.UUID[:]) + return nil + case *string: + *v = src.UUID.String() + return nil + default: + if nextDst, retry := pgtype.GetAssignToDstType(v); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *UUID) DecodeText(ci *pgtype.ConnInfo, src []byte) error { if src == nil { - *dst = UUID{Status: pgtype.Null} + *dst = UUID{} return nil } @@ -112,13 +101,13 @@ func (dst *UUID) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - *dst = UUID{UUID: u, Status: pgtype.Present} + *dst = UUID{UUID: u, Valid: true} return nil } func (dst *UUID) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { if src == nil { - *dst = UUID{Status: pgtype.Null} + *dst = UUID{} return nil } @@ -126,37 +115,29 @@ func (dst *UUID) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return fmt.Errorf("invalid length for UUID: %v", len(src)) } - *dst = UUID{Status: pgtype.Present} + *dst = UUID{Valid: true} copy(dst.UUID[:], src) return nil } func (src UUID) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: + if !src.Valid { return nil, nil - case pgtype.Undefined: - return nil, errUndefined } - return append(buf, src.UUID.String()...), nil } func (src UUID) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: + if !src.Valid { return nil, nil - case pgtype.Undefined: - return nil, errUndefined } - return append(buf, src.UUID[:]...), nil } // Scan implements the database/sql Scanner interface. func (dst *UUID) Scan(src interface{}) error { if src == nil { - *dst = UUID{Status: pgtype.Null} + *dst = UUID{} return nil } @@ -176,16 +157,10 @@ func (src UUID) Value() (driver.Value, error) { } func (src UUID) MarshalJSON() ([]byte, error) { - switch src.Status { - case pgtype.Present: - return []byte(`"` + src.UUID.String() + `"`), nil - case pgtype.Null: + if !src.Valid { return []byte("null"), nil - case pgtype.Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return []byte(`"` + src.UUID.String() + `"`), nil } func (dst *UUID) UnmarshalJSON(b []byte) error { @@ -195,11 +170,7 @@ func (dst *UUID) UnmarshalJSON(b []byte) error { return err } - status := pgtype.Null - if u.Valid { - status = pgtype.Present - } - *dst = UUID{UUID: u.UUID, Status: status} + *dst = UUID{UUID: u.UUID, Valid: u.Valid} return nil } diff --git a/ext/gofrs-uuid/uuid_test.go b/ext/gofrs-uuid/uuid_test.go index 56814524..3e5e4d82 100644 --- a/ext/gofrs-uuid/uuid_test.go +++ b/ext/gofrs-uuid/uuid_test.go @@ -4,15 +4,14 @@ import ( "bytes" "testing" - "github.com/jackc/pgtype" gofrs "github.com/jackc/pgtype/ext/gofrs-uuid" "github.com/jackc/pgtype/testutil" ) func TestUUIDTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{ - &gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - &gofrs.UUID{Status: pgtype.Null}, + &gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + &gofrs.UUID{}, }) } @@ -22,20 +21,20 @@ func TestUUIDSet(t *testing.T) { result gofrs.UUID }{ { - source: &gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + source: &gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: "00010203-0405-0607-0809-0a0b0c0d0e0f", - result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, } @@ -54,7 +53,7 @@ func TestUUIDSet(t *testing.T) { func TestUUIDAssignTo(t *testing.T) { { - src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst [16]byte expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -69,7 +68,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst []byte expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -84,7 +83,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := gofrs.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst string expected := "00010203-0405-0607-0809-0a0b0c0d0e0f" diff --git a/ext/shopspring-numeric/decimal.go b/ext/shopspring-numeric/decimal.go index ef3ce201..3a9d99ba 100644 --- a/ext/shopspring-numeric/decimal.go +++ b/ext/shopspring-numeric/decimal.go @@ -2,7 +2,6 @@ package numeric import ( "database/sql/driver" - "errors" "fmt" "strconv" @@ -10,17 +9,14 @@ import ( "github.com/shopspring/decimal" ) -var errUndefined = errors.New("cannot encode status undefined") -var errBadStatus = errors.New("invalid status") - type Numeric struct { Decimal decimal.Decimal - Status pgtype.Status + Valid bool } func (dst *Numeric) Set(src interface{}) error { if src == nil { - *dst = Numeric{Status: pgtype.Null} + *dst = Numeric{} return nil } @@ -33,53 +29,53 @@ func (dst *Numeric) Set(src interface{}) error { switch value := src.(type) { case decimal.Decimal: - *dst = Numeric{Decimal: value, Status: pgtype.Present} + *dst = Numeric{Decimal: value, Valid: true} case decimal.NullDecimal: if value.Valid { - *dst = Numeric{Decimal: value.Decimal, Status: pgtype.Present} + *dst = Numeric{Decimal: value.Decimal, Valid: true} } else { - *dst = Numeric{Status: pgtype.Null} + *dst = Numeric{} } case float32: - *dst = Numeric{Decimal: decimal.NewFromFloat(float64(value)), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.NewFromFloat(float64(value)), Valid: true} case float64: - *dst = Numeric{Decimal: decimal.NewFromFloat(value), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.NewFromFloat(value), Valid: true} case int8: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case uint8: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case int16: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case uint16: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case int32: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case uint32: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case int64: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case uint64: // uint64 could be greater than int64 so convert to string then to decimal dec, err := decimal.NewFromString(strconv.FormatUint(value, 10)) if err != nil { return err } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} + *dst = Numeric{Decimal: dec, Valid: true} case int: - *dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.New(int64(value), 0), Valid: true} case uint: // uint could be greater than int64 so convert to string then to decimal dec, err := decimal.NewFromString(strconv.FormatUint(uint64(value), 10)) if err != nil { return err } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} + *dst = Numeric{Decimal: dec, Valid: true} case string: dec, err := decimal.NewFromString(value) if err != nil { return err } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} + *dst = Numeric{Decimal: dec, Valid: true} default: // If all else fails see if pgtype.Numeric can handle it. If so, translate through that. num := &pgtype.Numeric{} @@ -96,140 +92,136 @@ func (dst *Numeric) Set(src interface{}) error { if err != nil { return fmt.Errorf("cannot convert %v to Numeric", value) } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} + *dst = Numeric{Decimal: dec, Valid: true} } return nil } func (dst Numeric) Get() interface{} { - switch dst.Status { - case pgtype.Present: - return dst.Decimal - case pgtype.Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Decimal } func (src *Numeric) AssignTo(dst interface{}) error { - switch src.Status { - case pgtype.Present: - switch v := dst.(type) { - case *decimal.Decimal: - *v = src.Decimal - case *decimal.NullDecimal: - (*v).Valid = true - (*v).Decimal = src.Decimal - case *float32: - f, _ := src.Decimal.Float64() - *v = float32(f) - case *float64: - f, _ := src.Decimal.Float64() - *v = f - case *int: - if src.Decimal.Exponent() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, strconv.IntSize) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int(n) - case *int8: - if src.Decimal.Exponent() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 8) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int8(n) - case *int16: - if src.Decimal.Exponent() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 16) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int16(n) - case *int32: - if src.Decimal.Exponent() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 32) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int32(n) - case *int64: - if src.Decimal.Exponent() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseInt(src.Decimal.String(), 10, 64) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = int64(n) - case *uint: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, strconv.IntSize) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint(n) - case *uint8: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 8) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint8(n) - case *uint16: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 16) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint16(n) - case *uint32: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 32) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint32(n) - case *uint64: - if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - n, err := strconv.ParseUint(src.Decimal.String(), 10, 64) - if err != nil { - return fmt.Errorf("cannot convert %v to %T", dst, *v) - } - *v = uint64(n) - default: - if nextDst, retry := pgtype.GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case pgtype.Null: + if !src.Valid { if v, ok := dst.(*decimal.NullDecimal); ok { (*v).Valid = false - } else { - return pgtype.NullAssignTo(dst) + (*v).Decimal = src.Decimal + return nil } + return pgtype.NullAssignTo(dst) + } + + switch v := dst.(type) { + case *decimal.Decimal: + *v = src.Decimal + case *decimal.NullDecimal: + (*v).Valid = true + (*v).Decimal = src.Decimal + case *float32: + f, _ := src.Decimal.Float64() + *v = float32(f) + case *float64: + f, _ := src.Decimal.Float64() + *v = f + case *int: + if src.Decimal.Exponent() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseInt(src.Decimal.String(), 10, strconv.IntSize) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = int(n) + case *int8: + if src.Decimal.Exponent() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseInt(src.Decimal.String(), 10, 8) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = int8(n) + case *int16: + if src.Decimal.Exponent() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseInt(src.Decimal.String(), 10, 16) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = int16(n) + case *int32: + if src.Decimal.Exponent() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseInt(src.Decimal.String(), 10, 32) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = int32(n) + case *int64: + if src.Decimal.Exponent() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseInt(src.Decimal.String(), 10, 64) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = int64(n) + case *uint: + if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseUint(src.Decimal.String(), 10, strconv.IntSize) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = uint(n) + case *uint8: + if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseUint(src.Decimal.String(), 10, 8) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = uint8(n) + case *uint16: + if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseUint(src.Decimal.String(), 10, 16) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = uint16(n) + case *uint32: + if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseUint(src.Decimal.String(), 10, 32) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = uint32(n) + case *uint64: + if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + n, err := strconv.ParseUint(src.Decimal.String(), 10, 64) + if err != nil { + return fmt.Errorf("cannot convert %v to %T", dst, *v) + } + *v = uint64(n) + default: + if nextDst, retry := pgtype.GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) } return nil @@ -237,7 +229,7 @@ func (src *Numeric) AssignTo(dst interface{}) error { func (dst *Numeric) DecodeText(ci *pgtype.ConnInfo, src []byte) error { if src == nil { - *dst = Numeric{Status: pgtype.Null} + *dst = Numeric{} return nil } @@ -246,13 +238,13 @@ func (dst *Numeric) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - *dst = Numeric{Decimal: dec, Status: pgtype.Present} + *dst = Numeric{Decimal: dec, Valid: true} return nil } func (dst *Numeric) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { if src == nil { - *dst = Numeric{Status: pgtype.Null} + *dst = Numeric{} return nil } @@ -263,28 +255,21 @@ func (dst *Numeric) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - *dst = Numeric{Decimal: decimal.NewFromBigInt(num.Int, num.Exp), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.NewFromBigInt(num.Int, num.Exp), Valid: true} return nil } func (src Numeric) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: + if !src.Valid { return nil, nil - case pgtype.Undefined: - return nil, errUndefined } - return append(buf, src.Decimal.String()...), nil } func (src Numeric) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case pgtype.Null: + if !src.Valid { return nil, nil - case pgtype.Undefined: - return nil, errUndefined } // For now at least, implement this in terms of pgtype.Numeric @@ -299,13 +284,13 @@ func (src Numeric) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) // Scan implements the database/sql Scanner interface. func (dst *Numeric) Scan(src interface{}) error { if src == nil { - *dst = Numeric{Status: pgtype.Null} + *dst = Numeric{} return nil } switch src := src.(type) { case float64: - *dst = Numeric{Decimal: decimal.NewFromFloat(src), Status: pgtype.Present} + *dst = Numeric{Decimal: decimal.NewFromFloat(src), Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -318,27 +303,17 @@ func (dst *Numeric) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Numeric) Value() (driver.Value, error) { - switch src.Status { - case pgtype.Present: - return src.Decimal.Value() - case pgtype.Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return src.Decimal.Value() } func (src Numeric) MarshalJSON() ([]byte, error) { - switch src.Status { - case pgtype.Present: - return src.Decimal.MarshalJSON() - case pgtype.Null: + if !src.Valid { return []byte("null"), nil - case pgtype.Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return src.Decimal.MarshalJSON() } func (dst *Numeric) UnmarshalJSON(b []byte) error { @@ -348,11 +323,7 @@ func (dst *Numeric) UnmarshalJSON(b []byte) error { return err } - status := pgtype.Null - if d.Valid { - status = pgtype.Present - } - *dst = Numeric{Decimal: d.Decimal, Status: status} + *dst = Numeric{Decimal: d.Decimal, Valid: d.Valid} return nil } diff --git a/ext/shopspring-numeric/decimal_test.go b/ext/shopspring-numeric/decimal_test.go index e635da41..d130a69a 100644 --- a/ext/shopspring-numeric/decimal_test.go +++ b/ext/shopspring-numeric/decimal_test.go @@ -7,7 +7,6 @@ import ( "reflect" "testing" - "github.com/jackc/pgtype" shopspring "github.com/jackc/pgtype/ext/shopspring-numeric" "github.com/jackc/pgtype/testutil" "github.com/shopspring/decimal" @@ -26,100 +25,100 @@ func TestNumericNormalize(t *testing.T) { testutil.TestSuccessfulNormalizeEqFunc(t, []testutil.NormalizeTest{ { SQL: "select '0'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Valid: true}, }, { SQL: "select '1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}, }, { SQL: "select '10.00'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10.00"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10.00"), Valid: true}, }, { SQL: "select '1e-3'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Valid: true}, }, { SQL: "select '-1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, }, { SQL: "select '10000'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10000"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10000"), Valid: true}, }, { SQL: "select '3.14'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Valid: true}, }, { SQL: "select '1.1'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.1"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.1"), Valid: true}, }, { SQL: "select '100010001'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001"), Valid: true}, }, { SQL: "select '100010001.0001'::numeric", - Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001.0001"), Status: pgtype.Present}, + Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001.0001"), Valid: true}, }, { SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric", Value: &shopspring.Numeric{ Decimal: mustParseDecimal(t, "4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981"), - Status: pgtype.Present, + Valid: true, }, }, { SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric", Value: &shopspring.Numeric{ Decimal: mustParseDecimal(t, "0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), - Status: pgtype.Present, + Valid: true, }, }, { SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric", Value: &shopspring.Numeric{ Decimal: mustParseDecimal(t, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123"), - Status: pgtype.Present, + Valid: true, }, }, }, func(aa, bb interface{}) bool { a := aa.(shopspring.Numeric) b := bb.(shopspring.Numeric) - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) + return a.Valid == b.Valid && a.Decimal.Equal(b.Decimal) }) } func TestNumericTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{ - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "100000"), Status: pgtype.Present}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "100000"), Valid: true}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.1"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.01"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00001"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000001"), Status: pgtype.Present}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.1"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.01"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0001"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00001"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000001"), Valid: true}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000123"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456789"), Status: pgtype.Present}, - &shopspring.Numeric{Decimal: mustParseDecimal(t, "4309132809320932980457137401234890237489238912983572189348951289375283573984571892758234678903467889512893489128589347891272139.8489235871258912789347891235879148795891238915678189467128957812395781238579189025891238901583915890128973578957912385798125789012378905238905471598123758923478294374327894237892234"), Status: pgtype.Present}, - &shopspring.Numeric{Status: pgtype.Null}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000123"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000000123"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0000000123"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000123"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456789"), Valid: true}, + &shopspring.Numeric{Decimal: mustParseDecimal(t, "4309132809320932980457137401234890237489238912983572189348951289375283573984571892758234678903467889512893489128589347891272139.8489235871258912789347891235879148795891238915678189467128957812395781238579189025891238901583915890128973578957912385798125789012378905238905471598123758923478294374327894237892234"), Valid: true}, + &shopspring.Numeric{}, }, func(aa, bb interface{}) bool { a := aa.(shopspring.Numeric) b := bb.(shopspring.Numeric) - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) + return a.Valid == b.Valid && a.Decimal.Equal(b.Decimal) }) } @@ -133,8 +132,8 @@ func TestNumericTranscodeFuzz(t *testing.T) { for i := 0; i < 500; i++ { num := fmt.Sprintf("%s.%s", (&big.Int{}).Rand(r, max).String(), (&big.Int{}).Rand(r, max).String()) negNum := "-" + num - values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, num), Status: pgtype.Present}) - values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, negNum), Status: pgtype.Present}) + values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, num), Valid: true}) + values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, negNum), Valid: true}) } testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", values, @@ -142,7 +141,7 @@ func TestNumericTranscodeFuzz(t *testing.T) { a := aa.(shopspring.Numeric) b := bb.(shopspring.Numeric) - return a.Status == b.Status && a.Decimal.Equal(b.Decimal) + return a.Valid == b.Valid && a.Decimal.Equal(b.Decimal) }) } @@ -153,29 +152,29 @@ func TestNumericSet(t *testing.T) { source interface{} result *shopspring.Numeric }{ - {source: decimal.New(1, 0), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: decimal.NullDecimal{Valid: true, Decimal: decimal.New(1, 0)}, result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: decimal.NullDecimal{Valid: false}, result: &shopspring.Numeric{Status: pgtype.Null}}, - {source: float32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: float64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: int8(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int16(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int32(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: int64(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}}, - {source: uint8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: uint64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: "1", result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: _int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}}, - {source: float64(1000), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1000"), Status: pgtype.Present}}, - {source: float64(1234), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1234"), Status: pgtype.Present}}, - {source: float64(12345678900), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "12345678900"), Status: pgtype.Present}}, - {source: float64(1.25), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.25"), Status: pgtype.Present}}, + {source: decimal.New(1, 0), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: decimal.NullDecimal{Valid: true, Decimal: decimal.New(1, 0)}, result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: decimal.NullDecimal{Valid: false}, result: &shopspring.Numeric{}}, + {source: float32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: float64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: int16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: int32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: int64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: int8(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}}, + {source: int16(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}}, + {source: int32(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}}, + {source: int64(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}}, + {source: uint8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: uint16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: uint32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: uint64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: "1", result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: _int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Valid: true}}, + {source: float64(1000), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1000"), Valid: true}}, + {source: float64(1234), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1234"), Valid: true}}, + {source: float64(12345678900), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "12345678900"), Valid: true}}, + {source: float64(1.25), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.25"), Valid: true}}, } for i, tt := range successfulTests { @@ -185,7 +184,7 @@ func TestNumericSet(t *testing.T) { t.Errorf("%d: %v", i, err) } - if !(r.Status == tt.result.Status && r.Decimal.Equal(tt.result.Decimal)) { + if !(r.Valid == tt.result.Valid && r.Decimal.Equal(tt.result.Decimal)) { t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) } } @@ -219,28 +218,28 @@ func TestNumericAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f32, expected: float32(4.2)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f64, expected: float64(4.2)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Status: pgtype.Present}, dst: &i64, expected: int64(42000)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &d, expected: decimal.New(42, 0)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Status: pgtype.Present}, dst: &d, expected: decimal.New(42, 3)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.042"), Status: pgtype.Present}, dst: &d, expected: decimal.New(42, -3)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &nd, expected: decimal.NullDecimal{Valid: true, Decimal: decimal.New(42, 0)}}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &nd, expected: decimal.NullDecimal{Valid: false}}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &f32, expected: float32(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &f64, expected: float64(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Valid: true}, dst: &f32, expected: float32(4.2)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Valid: true}, dst: &f64, expected: float64(4.2)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &i16, expected: int16(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &i32, expected: int32(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &i64, expected: int64(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Valid: true}, dst: &i64, expected: int64(42000)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &i, expected: int(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &ui, expected: uint(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: &shopspring.Numeric{}, dst: &pi8, expected: ((*int8)(nil))}, + {src: &shopspring.Numeric{}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &d, expected: decimal.New(42, 0)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Valid: true}, dst: &d, expected: decimal.New(42, 3)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.042"), Valid: true}, dst: &d, expected: decimal.New(42, -3)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &nd, expected: decimal.NullDecimal{Valid: true, Decimal: decimal.New(42, 0)}}, + {src: &shopspring.Numeric{}, dst: &nd, expected: decimal.NullDecimal{Valid: false}}, } for i, tt := range simpleTests { @@ -280,8 +279,8 @@ func TestNumericAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &pf32, expected: float32(42)}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Valid: true}, dst: &pf64, expected: float64(42)}, } for i, tt := range pointerAllocTests { @@ -299,14 +298,14 @@ func TestNumericAssignTo(t *testing.T) { src *shopspring.Numeric dst interface{} }{ - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "150"), Status: pgtype.Present}, dst: &i8}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "40000"), Status: pgtype.Present}, dst: &i16}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui8}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui16}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui32}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui64}, - {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui}, - {src: &shopspring.Numeric{Status: pgtype.Null}, dst: &i32}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "150"), Valid: true}, dst: &i8}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "40000"), Valid: true}, dst: &i16}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, dst: &ui8}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, dst: &ui16}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, dst: &ui32}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, dst: &ui64}, + {src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Valid: true}, dst: &ui}, + {src: &shopspring.Numeric{}, dst: &i32}, } for i, tt := range errorTests { diff --git a/float4.go b/float4.go index 89b9e8fa..36c46346 100644 --- a/float4.go +++ b/float4.go @@ -11,13 +11,13 @@ import ( ) type Float4 struct { - Float float32 - Status Status + Float float32 + Valid bool } func (dst *Float4) Set(src interface{}) error { if src == nil { - *dst = Float4{Status: Null} + *dst = Float4{} return nil } @@ -30,56 +30,56 @@ func (dst *Float4) Set(src interface{}) error { switch value := src.(type) { case float32: - *dst = Float4{Float: value, Status: Present} + *dst = Float4{Float: value, Valid: true} case float64: - *dst = Float4{Float: float32(value), Status: Present} + *dst = Float4{Float: float32(value), Valid: true} case int8: - *dst = Float4{Float: float32(value), Status: Present} + *dst = Float4{Float: float32(value), Valid: true} case uint8: - *dst = Float4{Float: float32(value), Status: Present} + *dst = Float4{Float: float32(value), Valid: true} case int16: - *dst = Float4{Float: float32(value), Status: Present} + *dst = Float4{Float: float32(value), Valid: true} case uint16: - *dst = Float4{Float: float32(value), Status: Present} + *dst = Float4{Float: float32(value), Valid: true} case int32: f32 := float32(value) if int32(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } case uint32: f32 := float32(value) if uint32(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } case int64: f32 := float32(value) if int64(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } case uint64: f32 := float32(value) if uint64(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } case int: f32 := float32(value) if int(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } case uint: f32 := float32(value) if uint(f32) == value { - *dst = Float4{Float: f32, Status: Present} + *dst = Float4{Float: f32, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float32", value) } @@ -88,82 +88,82 @@ func (dst *Float4) Set(src interface{}) error { if err != nil { return err } - *dst = Float4{Float: float32(num), Status: Present} + *dst = Float4{Float: float32(num), Valid: true} case *float64: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *int8: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Float4{Status: Null} + *dst = Float4{} } else { return dst.Set(*value) } @@ -178,23 +178,19 @@ func (dst *Float4) Set(src interface{}) error { } func (dst Float4) Get() interface{} { - switch dst.Status { - case Present: - return dst.Float - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Float } func (src *Float4) AssignTo(dst interface{}) error { - return float64AssignTo(float64(src.Float), src.Status, dst) + return float64AssignTo(float64(src.Float), src.Valid, dst) } func (dst *Float4) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float4{Status: Null} + *dst = Float4{} return nil } @@ -203,13 +199,13 @@ func (dst *Float4) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Float4{Float: float32(n), Status: Present} + *dst = Float4{Float: float32(n), Valid: true} return nil } func (dst *Float4) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float4{Status: Null} + *dst = Float4{} return nil } @@ -219,16 +215,13 @@ func (dst *Float4) DecodeBinary(ci *ConnInfo, src []byte) error { n := int32(binary.BigEndian.Uint32(src)) - *dst = Float4{Float: math.Float32frombits(uint32(n)), Status: Present} + *dst = Float4{Float: math.Float32frombits(uint32(n)), Valid: true} return nil } func (src Float4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 32)...) @@ -236,11 +229,8 @@ func (src Float4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Float4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint32(buf, math.Float32bits(src.Float)) @@ -250,13 +240,13 @@ func (src Float4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Float4) Scan(src interface{}) error { if src == nil { - *dst = Float4{Status: Null} + *dst = Float4{} return nil } switch src := src.(type) { case float64: - *dst = Float4{Float: float32(src), Status: Present} + *dst = Float4{Float: float32(src), Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -271,12 +261,8 @@ func (dst *Float4) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Float4) Value() (driver.Value, error) { - switch src.Status { - case Present: - return float64(src.Float), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return float64(src.Float), nil } diff --git a/float4_array.go b/float4_array.go index 41f2ec8f..dcf6c1f7 100644 --- a/float4_array.go +++ b/float4_array.go @@ -14,13 +14,13 @@ import ( type Float4Array struct { Elements []Float4 Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *Float4Array) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} return nil } @@ -36,9 +36,9 @@ func (dst *Float4Array) Set(src interface{}) error { case []float32: if value == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} } else if len(value) == 0 { - *dst = Float4Array{Status: Present} + *dst = Float4Array{Valid: true} } else { elements := make([]Float4, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *Float4Array) Set(src interface{}) error { *dst = Float4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*float32: if value == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} } else if len(value) == 0 { - *dst = Float4Array{Status: Present} + *dst = Float4Array{Valid: true} } else { elements := make([]Float4, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *Float4Array) Set(src interface{}) error { *dst = Float4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Float4: if value == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} } else if len(value) == 0 { - *dst = Float4Array{Status: Present} + *dst = Float4Array{Valid: true} } else { *dst = Float4Array{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *Float4Array) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} return nil } @@ -99,7 +99,7 @@ func (dst *Float4Array) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for Float4Array", src) } if elementsLength == 0 { - *dst = Float4Array{Status: Present} + *dst = Float4Array{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *Float4Array) Set(src interface{}) error { *dst = Float4Array{ Elements: make([]Float4, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *Float4Array) setRecursive(value reflect.Value, index, dimension int) } func (dst Float4Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Float4Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]float32: - *v = make([]float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*float32: - *v = make([]*float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]float32: + *v = make([]float32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*float32: + *v = make([]*float32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *Float4Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *Float4Array) assignToRecursive(value reflect.Value, index, dimension func (dst *Float4Array) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} return nil } @@ -337,14 +330,14 @@ func (dst *Float4Array) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Float4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = Float4Array{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *Float4Array) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float4Array{Status: Null} + *dst = Float4Array{} return nil } @@ -355,7 +348,7 @@ func (dst *Float4Array) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = Float4Array{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Float4Array{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *Float4Array) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = Float4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Float4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src Float4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src Float4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Float4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src Float4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/float4_array_test.go b/float4_array_test.go index db438999..9b401ac8 100644 --- a/float4_array_test.go +++ b/float4_array_test.go @@ -13,41 +13,41 @@ func TestFloat4ArrayTranscode(t *testing.T) { &pgtype.Float4Array{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Float: 1, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Float4Array{Status: pgtype.Null}, + &pgtype.Float4Array{}, &pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Float: 6, Status: pgtype.Present}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {}, + {Float: 6, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,61 +60,61 @@ func TestFloat4ArraySet(t *testing.T) { { source: []float32{1}, result: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]float32)(nil)), - result: pgtype.Float4Array{Status: pgtype.Null}, + result: pgtype.Float4Array{}, }, { source: [][]float32{{1}, {2}}, result: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]float32{{1}, {2}}, result: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -146,81 +146,81 @@ func TestFloat4ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1.23, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float32Slice, expected: []float32{1.23}, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1.23, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedFloat32Slice, expected: _float32Slice{1.23}, }, { - src: pgtype.Float4Array{Status: pgtype.Null}, + src: pgtype.Float4Array{}, dst: &float32Slice, expected: (([]float32)(nil)), }, { - src: pgtype.Float4Array{Status: pgtype.Present}, + src: pgtype.Float4Array{Valid: true}, dst: &float32Slice, expected: []float32{}, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]float32{{1}, {2}}, dst: &float32SliceDim2, }, { src: pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &float32SliceDim4, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]float32{{1}, {2}}, dst: &float32ArrayDim2, }, { src: pgtype.Float4Array{ Elements: []pgtype.Float4{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &float32ArrayDim4, }, @@ -243,31 +243,31 @@ func TestFloat4ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Status: pgtype.Null}}, + Elements: []pgtype.Float4{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float32Slice, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim2, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32Slice, }, { src: pgtype.Float4Array{ - Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float4{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim4, }, } diff --git a/float4_test.go b/float4_test.go index d2524cda..191df65e 100644 --- a/float4_test.go +++ b/float4_test.go @@ -10,12 +10,12 @@ import ( func TestFloat4Transcode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "float4", []interface{}{ - &pgtype.Float4{Float: -1, Status: pgtype.Present}, - &pgtype.Float4{Float: 0, Status: pgtype.Present}, - &pgtype.Float4{Float: 0.00001, Status: pgtype.Present}, - &pgtype.Float4{Float: 1, Status: pgtype.Present}, - &pgtype.Float4{Float: 9999.99, Status: pgtype.Present}, - &pgtype.Float4{Float: 0, Status: pgtype.Null}, + &pgtype.Float4{Float: -1, Valid: true}, + &pgtype.Float4{Float: 0, Valid: true}, + &pgtype.Float4{Float: 0.00001, Valid: true}, + &pgtype.Float4{Float: 1, Valid: true}, + &pgtype.Float4{Float: 9999.99, Valid: true}, + &pgtype.Float4{Float: 0}, }) } @@ -24,22 +24,22 @@ func TestFloat4Set(t *testing.T) { source interface{} result pgtype.Float4 }{ - {source: float32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}}, + {source: float32(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: float64(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: int8(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: int16(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: int32(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: int64(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: int8(-1), result: pgtype.Float4{Float: -1, Valid: true}}, + {source: int16(-1), result: pgtype.Float4{Float: -1, Valid: true}}, + {source: int32(-1), result: pgtype.Float4{Float: -1, Valid: true}}, + {source: int64(-1), result: pgtype.Float4{Float: -1, Valid: true}}, + {source: uint8(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: uint16(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: uint32(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: uint64(1), result: pgtype.Float4{Float: 1, Valid: true}}, + {source: "1", result: pgtype.Float4{Float: 1, Valid: true}}, + {source: _int8(1), result: pgtype.Float4{Float: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -79,20 +79,20 @@ func TestFloat4AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &f32, expected: float32(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &f64, expected: float64(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.Float4{Float: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.Float4{Float: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -111,8 +111,8 @@ func TestFloat4AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &pf32, expected: float32(42)}, + {src: pgtype.Float4{Float: 42, Valid: true}, dst: &pf64, expected: float64(42)}, } for i, tt := range pointerAllocTests { @@ -130,14 +130,14 @@ func TestFloat4AssignTo(t *testing.T) { src pgtype.Float4 dst interface{} }{ - {src: pgtype.Float4{Float: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Float4{Float: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &i32}, + {src: pgtype.Float4{Float: 150, Valid: true}, dst: &i8}, + {src: pgtype.Float4{Float: 40000, Valid: true}, dst: &i16}, + {src: pgtype.Float4{Float: -1, Valid: true}, dst: &ui8}, + {src: pgtype.Float4{Float: -1, Valid: true}, dst: &ui16}, + {src: pgtype.Float4{Float: -1, Valid: true}, dst: &ui32}, + {src: pgtype.Float4{Float: -1, Valid: true}, dst: &ui64}, + {src: pgtype.Float4{Float: -1, Valid: true}, dst: &ui}, + {src: pgtype.Float4{Float: 0}, dst: &i32}, } for i, tt := range errorTests { diff --git a/float8.go b/float8.go index 4d9e7116..1038d283 100644 --- a/float8.go +++ b/float8.go @@ -11,13 +11,13 @@ import ( ) type Float8 struct { - Float float64 - Status Status + Float float64 + Valid bool } func (dst *Float8) Set(src interface{}) error { if src == nil { - *dst = Float8{Status: Null} + *dst = Float8{} return nil } @@ -30,46 +30,46 @@ func (dst *Float8) Set(src interface{}) error { switch value := src.(type) { case float32: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case float64: - *dst = Float8{Float: value, Status: Present} + *dst = Float8{Float: value, Valid: true} case int8: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case uint8: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case int16: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case uint16: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case int32: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case uint32: - *dst = Float8{Float: float64(value), Status: Present} + *dst = Float8{Float: float64(value), Valid: true} case int64: f64 := float64(value) if int64(f64) == value { - *dst = Float8{Float: f64, Status: Present} + *dst = Float8{Float: f64, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float64", value) } case uint64: f64 := float64(value) if uint64(f64) == value { - *dst = Float8{Float: f64, Status: Present} + *dst = Float8{Float: f64, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float64", value) } case int: f64 := float64(value) if int(f64) == value { - *dst = Float8{Float: f64, Status: Present} + *dst = Float8{Float: f64, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float64", value) } case uint: f64 := float64(value) if uint(f64) == value { - *dst = Float8{Float: f64, Status: Present} + *dst = Float8{Float: f64, Valid: true} } else { return fmt.Errorf("%v cannot be exactly represented as float64", value) } @@ -78,82 +78,82 @@ func (dst *Float8) Set(src interface{}) error { if err != nil { return err } - *dst = Float8{Float: float64(num), Status: Present} + *dst = Float8{Float: float64(num), Valid: true} case *float64: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *int8: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Float8{Status: Null} + *dst = Float8{} } else { return dst.Set(*value) } @@ -168,23 +168,19 @@ func (dst *Float8) Set(src interface{}) error { } func (dst Float8) Get() interface{} { - switch dst.Status { - case Present: - return dst.Float - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Float } func (src *Float8) AssignTo(dst interface{}) error { - return float64AssignTo(src.Float, src.Status, dst) + return float64AssignTo(src.Float, src.Valid, dst) } func (dst *Float8) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float8{Status: Null} + *dst = Float8{} return nil } @@ -193,13 +189,13 @@ func (dst *Float8) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Float8{Float: n, Status: Present} + *dst = Float8{Float: n, Valid: true} return nil } func (dst *Float8) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float8{Status: Null} + *dst = Float8{} return nil } @@ -209,16 +205,13 @@ func (dst *Float8) DecodeBinary(ci *ConnInfo, src []byte) error { n := int64(binary.BigEndian.Uint64(src)) - *dst = Float8{Float: math.Float64frombits(uint64(n)), Status: Present} + *dst = Float8{Float: math.Float64frombits(uint64(n)), Valid: true} return nil } func (src Float8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 64)...) @@ -226,11 +219,8 @@ func (src Float8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Float8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.Float)) @@ -240,13 +230,13 @@ func (src Float8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Float8) Scan(src interface{}) error { if src == nil { - *dst = Float8{Status: Null} + *dst = Float8{} return nil } switch src := src.(type) { case float64: - *dst = Float8{Float: src, Status: Present} + *dst = Float8{Float: src, Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -261,12 +251,8 @@ func (dst *Float8) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Float8) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Float, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return src.Float, nil } diff --git a/float8_array.go b/float8_array.go index 836ee19d..5e85e236 100644 --- a/float8_array.go +++ b/float8_array.go @@ -14,13 +14,13 @@ import ( type Float8Array struct { Elements []Float8 Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *Float8Array) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} return nil } @@ -36,9 +36,9 @@ func (dst *Float8Array) Set(src interface{}) error { case []float64: if value == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} } else if len(value) == 0 { - *dst = Float8Array{Status: Present} + *dst = Float8Array{Valid: true} } else { elements := make([]Float8, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *Float8Array) Set(src interface{}) error { *dst = Float8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*float64: if value == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} } else if len(value) == 0 { - *dst = Float8Array{Status: Present} + *dst = Float8Array{Valid: true} } else { elements := make([]Float8, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *Float8Array) Set(src interface{}) error { *dst = Float8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Float8: if value == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} } else if len(value) == 0 { - *dst = Float8Array{Status: Present} + *dst = Float8Array{Valid: true} } else { *dst = Float8Array{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *Float8Array) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} return nil } @@ -99,7 +99,7 @@ func (dst *Float8Array) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for Float8Array", src) } if elementsLength == 0 { - *dst = Float8Array{Status: Present} + *dst = Float8Array{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *Float8Array) Set(src interface{}) error { *dst = Float8Array{ Elements: make([]Float8, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *Float8Array) setRecursive(value reflect.Value, index, dimension int) } func (dst Float8Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Float8Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]float64: - *v = make([]float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*float64: - *v = make([]*float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]float64: + *v = make([]float64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*float64: + *v = make([]*float64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *Float8Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *Float8Array) assignToRecursive(value reflect.Value, index, dimension func (dst *Float8Array) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} return nil } @@ -337,14 +330,14 @@ func (dst *Float8Array) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Float8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = Float8Array{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *Float8Array) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Float8Array{Status: Null} + *dst = Float8Array{} return nil } @@ -355,7 +348,7 @@ func (dst *Float8Array) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = Float8Array{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Float8Array{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *Float8Array) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = Float8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Float8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src Float8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src Float8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Float8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src Float8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/float8_array_test.go b/float8_array_test.go index 85cb8f43..52209238 100644 --- a/float8_array_test.go +++ b/float8_array_test.go @@ -13,41 +13,41 @@ func TestFloat8ArrayTranscode(t *testing.T) { &pgtype.Float8Array{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Float: 1, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Float8Array{Status: pgtype.Null}, + &pgtype.Float8Array{}, &pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Float: 6, Status: pgtype.Present}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {}, + {Float: 6, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,37 +60,37 @@ func TestFloat8ArraySet(t *testing.T) { { source: []float64{1}, result: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]float64)(nil)), - result: pgtype.Float8Array{Status: pgtype.Null}, + result: pgtype.Float8Array{}, }, { source: [][]float64{{1}, {2}}, result: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]float64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -122,81 +122,81 @@ func TestFloat8ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1.23, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float64Slice, expected: []float64{1.23}, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1.23, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedFloat64Slice, expected: _float64Slice{1.23}, }, { - src: pgtype.Float8Array{Status: pgtype.Null}, + src: pgtype.Float8Array{}, dst: &float64Slice, expected: (([]float64)(nil)), }, { - src: pgtype.Float8Array{Status: pgtype.Present}, + src: pgtype.Float8Array{Valid: true}, dst: &float64Slice, expected: []float64{}, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]float64{{1}, {2}}, dst: &float64SliceDim2, }, { src: pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]float64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &float64SliceDim4, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]float64{{1}, {2}}, dst: &float64ArrayDim2, }, { src: pgtype.Float8Array{ Elements: []pgtype.Float8{ - {Float: 1, Status: pgtype.Present}, - {Float: 2, Status: pgtype.Present}, - {Float: 3, Status: pgtype.Present}, - {Float: 4, Status: pgtype.Present}, - {Float: 5, Status: pgtype.Present}, - {Float: 6, Status: pgtype.Present}}, + {Float: 1, Valid: true}, + {Float: 2, Valid: true}, + {Float: 3, Valid: true}, + {Float: 4, Valid: true}, + {Float: 5, Valid: true}, + {Float: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]float64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &float64ArrayDim4, }, @@ -219,31 +219,31 @@ func TestFloat8ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Status: pgtype.Null}}, + Elements: []pgtype.Float8{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float64Slice, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float64ArrayDim2, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float64Slice, }, { src: pgtype.Float8Array{ - Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}, {Float: 2, Status: pgtype.Present}}, + Elements: []pgtype.Float8{{Float: 1, Valid: true}, {Float: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &float64ArrayDim4, }, } diff --git a/float8_test.go b/float8_test.go index 6bc7c652..dcc45879 100644 --- a/float8_test.go +++ b/float8_test.go @@ -10,12 +10,12 @@ import ( func TestFloat8Transcode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "float8", []interface{}{ - &pgtype.Float8{Float: -1, Status: pgtype.Present}, - &pgtype.Float8{Float: 0, Status: pgtype.Present}, - &pgtype.Float8{Float: 0.00001, Status: pgtype.Present}, - &pgtype.Float8{Float: 1, Status: pgtype.Present}, - &pgtype.Float8{Float: 9999.99, Status: pgtype.Present}, - &pgtype.Float8{Float: 0, Status: pgtype.Null}, + &pgtype.Float8{Float: -1, Valid: true}, + &pgtype.Float8{Float: 0, Valid: true}, + &pgtype.Float8{Float: 0.00001, Valid: true}, + &pgtype.Float8{Float: 1, Valid: true}, + &pgtype.Float8{Float: 9999.99, Valid: true}, + &pgtype.Float8{Float: 0}, }) } @@ -24,22 +24,22 @@ func TestFloat8Set(t *testing.T) { source interface{} result pgtype.Float8 }{ - {source: float32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}}, + {source: float32(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: float64(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: int8(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: int16(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: int32(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: int64(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: int8(-1), result: pgtype.Float8{Float: -1, Valid: true}}, + {source: int16(-1), result: pgtype.Float8{Float: -1, Valid: true}}, + {source: int32(-1), result: pgtype.Float8{Float: -1, Valid: true}}, + {source: int64(-1), result: pgtype.Float8{Float: -1, Valid: true}}, + {source: uint8(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: uint16(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: uint32(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: uint64(1), result: pgtype.Float8{Float: 1, Valid: true}}, + {source: "1", result: pgtype.Float8{Float: 1, Valid: true}}, + {source: _int8(1), result: pgtype.Float8{Float: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -79,20 +79,20 @@ func TestFloat8AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &f32, expected: float32(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &f64, expected: float64(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.Float8{Float: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.Float8{Float: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -111,8 +111,8 @@ func TestFloat8AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &pf32, expected: float32(42)}, + {src: pgtype.Float8{Float: 42, Valid: true}, dst: &pf64, expected: float64(42)}, } for i, tt := range pointerAllocTests { @@ -130,14 +130,14 @@ func TestFloat8AssignTo(t *testing.T) { src pgtype.Float8 dst interface{} }{ - {src: pgtype.Float8{Float: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Float8{Float: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &i32}, + {src: pgtype.Float8{Float: 150, Valid: true}, dst: &i8}, + {src: pgtype.Float8{Float: 40000, Valid: true}, dst: &i16}, + {src: pgtype.Float8{Float: -1, Valid: true}, dst: &ui8}, + {src: pgtype.Float8{Float: -1, Valid: true}, dst: &ui16}, + {src: pgtype.Float8{Float: -1, Valid: true}, dst: &ui32}, + {src: pgtype.Float8{Float: -1, Valid: true}, dst: &ui64}, + {src: pgtype.Float8{Float: -1, Valid: true}, dst: &ui}, + {src: pgtype.Float8{Float: 0}, dst: &i32}, } for i, tt := range errorTests { diff --git a/go.mod b/go.mod index 63bae879..99c5b26e 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530 github.com/jackc/pgio v1.0.0 github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c - github.com/lib/pq v1.10.2 github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.7.0 ) diff --git a/hstore.go b/hstore.go index f46eeaf6..25406a74 100644 --- a/hstore.go +++ b/hstore.go @@ -16,13 +16,13 @@ import ( // Hstore represents an hstore column that can be null or have null values // associated with its keys. type Hstore struct { - Map map[string]Text - Status Status + Map map[string]Text + Valid bool } func (dst *Hstore) Set(src interface{}) error { if src == nil { - *dst = Hstore{Status: Null} + *dst = Hstore{} return nil } @@ -37,19 +37,19 @@ func (dst *Hstore) Set(src interface{}) error { case map[string]string: m := make(map[string]Text, len(value)) for k, v := range value { - m[k] = Text{String: v, Status: Present} + m[k] = Text{String: v, Valid: true} } - *dst = Hstore{Map: m, Status: Present} + *dst = Hstore{Map: m, Valid: true} case map[string]*string: m := make(map[string]Text, len(value)) for k, v := range value { if v == nil { - m[k] = Text{Status: Null} + m[k] = Text{} } else { - m[k] = Text{String: *v, Status: Present} + m[k] = Text{String: *v, Valid: true} } } - *dst = Hstore{Map: m, Status: Present} + *dst = Hstore{Map: m, Valid: true} default: return fmt.Errorf("cannot convert %v to Hstore", src) } @@ -58,58 +58,48 @@ func (dst *Hstore) Set(src interface{}) error { } func (dst Hstore) Get() interface{} { - switch dst.Status { - case Present: - return dst.Map - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Map } func (src *Hstore) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *map[string]string: - *v = make(map[string]string, len(src.Map)) - for k, val := range src.Map { - if val.Status != Present { - return fmt.Errorf("cannot decode %#v into %T", src, dst) - } - (*v)[k] = val.String - } - return nil - case *map[string]*string: - *v = make(map[string]*string, len(src.Map)) - for k, val := range src.Map { - switch val.Status { - case Null: - (*v)[k] = nil - case Present: - (*v)[k] = &val.String - default: - return fmt.Errorf("cannot decode %#v into %T", src, dst) - } - } - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *map[string]string: + *v = make(map[string]string, len(src.Map)) + for k, val := range src.Map { + if !val.Valid { + return fmt.Errorf("cannot decode %#v into %T", src, dst) + } + (*v)[k] = val.String + } + return nil + case *map[string]*string: + *v = make(map[string]*string, len(src.Map)) + for k, val := range src.Map { + if val.Valid { + (*v)[k] = &val.String + } else { + (*v)[k] = nil + } + } + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Hstore) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Hstore{Status: Null} + *dst = Hstore{} return nil } @@ -123,13 +113,13 @@ func (dst *Hstore) DecodeText(ci *ConnInfo, src []byte) error { m[keys[i]] = values[i] } - *dst = Hstore{Map: m, Status: Present} + *dst = Hstore{Map: m, Valid: true} return nil } func (dst *Hstore) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Hstore{Status: Null} + *dst = Hstore{} return nil } @@ -176,17 +166,14 @@ func (dst *Hstore) DecodeBinary(ci *ConnInfo, src []byte) error { m[key] = value } - *dst = Hstore{Map: m, Status: Present} + *dst = Hstore{Map: m, Valid: true} return nil } func (src Hstore) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } firstPair := true @@ -218,11 +205,8 @@ func (src Hstore) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Hstore) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendInt32(buf, int32(len(src.Map))) @@ -372,7 +356,7 @@ func parseHstore(s string) (k []string, v []Text, err error) { case hsVal: switch r { case '"': //End of the value - values = append(values, Text{String: buf.String(), Status: Present}) + values = append(values, Text{String: buf.String(), Valid: true}) buf = bytes.Buffer{} state = hsNext case '\\': //Potential escaped character @@ -401,7 +385,7 @@ func parseHstore(s string) (k []string, v []Text, err error) { nulBuf[i] = r } if nulBuf[0] == 'U' && nulBuf[1] == 'L' && nulBuf[2] == 'L' { - values = append(values, Text{Status: Null}) + values = append(values, Text{}) state = hsNext } else { err = fmt.Errorf("Invalid NULL value: 'N%s'", string(nulBuf)) @@ -440,7 +424,7 @@ func parseHstore(s string) (k []string, v []Text, err error) { // Scan implements the database/sql Scanner interface. func (dst *Hstore) Scan(src interface{}) error { if src == nil { - *dst = Hstore{Status: Null} + *dst = Hstore{} return nil } diff --git a/hstore_array.go b/hstore_array.go index 47b4b3ff..0ca5d4fb 100644 --- a/hstore_array.go +++ b/hstore_array.go @@ -14,13 +14,13 @@ import ( type HstoreArray struct { Elements []Hstore Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *HstoreArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} return nil } @@ -36,9 +36,9 @@ func (dst *HstoreArray) Set(src interface{}) error { case []map[string]string: if value == nil { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} } else if len(value) == 0 { - *dst = HstoreArray{Status: Present} + *dst = HstoreArray{Valid: true} } else { elements := make([]Hstore, len(value)) for i := range value { @@ -49,20 +49,20 @@ func (dst *HstoreArray) Set(src interface{}) error { *dst = HstoreArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Hstore: if value == nil { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} } else if len(value) == 0 { - *dst = HstoreArray{Status: Present} + *dst = HstoreArray{Valid: true} } else { *dst = HstoreArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -71,7 +71,7 @@ func (dst *HstoreArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} return nil } @@ -80,7 +80,7 @@ func (dst *HstoreArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for HstoreArray", src) } if elementsLength == 0 { - *dst = HstoreArray{Status: Present} + *dst = HstoreArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -93,7 +93,7 @@ func (dst *HstoreArray) Set(src interface{}) error { *dst = HstoreArray{ Elements: make([]Hstore, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -160,75 +160,68 @@ func (dst *HstoreArray) setRecursive(value reflect.Value, index, dimension int) } func (dst HstoreArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *HstoreArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]map[string]string: - *v = make([]map[string]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]map[string]string: + *v = make([]map[string]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *HstoreArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -280,7 +273,7 @@ func (src *HstoreArray) assignToRecursive(value reflect.Value, index, dimension func (dst *HstoreArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} return nil } @@ -309,14 +302,14 @@ func (dst *HstoreArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = HstoreArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = HstoreArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *HstoreArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = HstoreArray{Status: Null} + *dst = HstoreArray{} return nil } @@ -327,7 +320,7 @@ func (dst *HstoreArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = HstoreArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = HstoreArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -352,16 +345,13 @@ func (dst *HstoreArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = HstoreArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = HstoreArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src HstoreArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -414,11 +404,8 @@ func (src HstoreArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src HstoreArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -432,7 +419,7 @@ func (src HstoreArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/hstore_array_test.go b/hstore_array_test.go index 672eca4a..11290fb1 100644 --- a/hstore_array_test.go +++ b/hstore_array_test.go @@ -29,16 +29,16 @@ func TestHstoreArrayTranscode(t *testing.T) { conn.ConnInfo().RegisterDataType(pgtype.DataType{Value: &pgtype.HstoreArray{}, Name: "_hstore", OID: hstoreArrayOID}) text := func(s string) pgtype.Text { - return pgtype.Text{String: s, Status: pgtype.Present} + return pgtype.Text{String: s, Valid: true} } values := []pgtype.Hstore{ - {Map: map[string]pgtype.Text{}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present}, - {Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Map: map[string]pgtype.Text{}, Valid: true}, + {Map: map[string]pgtype.Text{"foo": text("bar")}, Valid: true}, + {Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Valid: true}, + {Map: map[string]pgtype.Text{"NULL": text("bar")}, Valid: true}, + {Map: map[string]pgtype.Text{"foo": text("NULL")}, Valid: true}, + {}, } specialStrings := []string{ @@ -52,22 +52,22 @@ func TestHstoreArrayTranscode(t *testing.T) { } for _, s := range specialStrings { // Special key values - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Valid: true}) // at beginning + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Valid: true}) // in middle + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Valid: true}) // at end + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Valid: true}) // is key // Special value values - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end - values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Valid: true}) // at beginning + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Valid: true}) // in middle + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Valid: true}) // at end + values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Valid: true}) // is key } src := &pgtype.HstoreArray{ Elements: values, Dimensions: []pgtype.ArrayDimension{{Length: int32(len(values)), LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, } _, err = conn.Prepare(context.Background(), "test", "select $1::hstore[]") @@ -98,8 +98,8 @@ func TestHstoreArrayTranscode(t *testing.T) { continue } - if result.Status != src.Status { - t.Errorf("%v: expected Status %v, got %v", fc.formatCode, src.Status, result.Status) + if result.Valid != src.Valid { + t.Errorf("%v: expected Valid %v, got %v", fc.formatCode, src.Valid, result.Valid) continue } @@ -112,8 +112,8 @@ func TestHstoreArrayTranscode(t *testing.T) { a := src.Elements[i] b := result.Elements[i] - if a.Status != b.Status { - t.Errorf("%v element idx %d: expected status %v, got %v", fc.formatCode, i, a.Status, b.Status) + if a.Valid != b.Valid { + t.Errorf("%v element idx %d: expected Valid %v, got %v", fc.formatCode, i, a.Valid, b.Valid) } if len(a.Map) != len(b.Map) { @@ -139,12 +139,12 @@ func TestHstoreArraySet(t *testing.T) { result: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, }, { @@ -152,16 +152,16 @@ func TestHstoreArraySet(t *testing.T) { result: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, }, { @@ -171,28 +171,28 @@ func TestHstoreArraySet(t *testing.T) { result: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"bar": {String: "baz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wibble": {String: "wobble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wubble": {String: "wabble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wabble": {String: "wobble", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{ @@ -200,7 +200,7 @@ func TestHstoreArraySet(t *testing.T) { {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present, + Valid: true, }, }, { @@ -208,16 +208,16 @@ func TestHstoreArraySet(t *testing.T) { result: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, }, { @@ -227,28 +227,28 @@ func TestHstoreArraySet(t *testing.T) { result: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"bar": {String: "baz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wibble": {String: "wobble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wubble": {String: "wabble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wabble": {String: "wobble", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{ @@ -256,7 +256,7 @@ func TestHstoreArraySet(t *testing.T) { {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present, + Valid: true, }, }, } @@ -290,35 +290,35 @@ func TestHstoreArrayAssignTo(t *testing.T) { src: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &hstoreSlice, expected: []map[string]string{{"foo": "bar"}}}, { - src: pgtype.HstoreArray{Status: pgtype.Null}, dst: &hstoreSlice, expected: (([]map[string]string)(nil)), + src: pgtype.HstoreArray{}, dst: &hstoreSlice, expected: (([]map[string]string)(nil)), }, { - src: pgtype.HstoreArray{Status: pgtype.Present}, dst: &hstoreSlice, expected: []map[string]string{}, + src: pgtype.HstoreArray{Valid: true}, dst: &hstoreSlice, expected: []map[string]string{}, }, { src: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &hstoreSliceDim2, expected: [][]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}}, @@ -327,28 +327,28 @@ func TestHstoreArrayAssignTo(t *testing.T) { src: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"bar": {String: "baz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wibble": {String: "wobble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wubble": {String: "wabble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wabble": {String: "wobble", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{ @@ -356,7 +356,7 @@ func TestHstoreArrayAssignTo(t *testing.T) { {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present, + Valid: true, }, dst: &hstoreSliceDim4, expected: [][][][]map[string]string{ @@ -367,16 +367,16 @@ func TestHstoreArrayAssignTo(t *testing.T) { src: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &hstoreArrayDim2, expected: [2][1]map[string]string{{{"foo": "bar"}}, {{"baz": "quz"}}}, @@ -385,28 +385,28 @@ func TestHstoreArrayAssignTo(t *testing.T) { src: pgtype.HstoreArray{ Elements: []pgtype.Hstore{ { - Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"baz": {String: "quz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"baz": {String: "quz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"bar": {String: "baz", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"bar": {String: "baz", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wibble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wibble": {String: "wobble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wubble": {String: "wabble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wubble": {String: "wabble", Valid: true}}, + Valid: true, }, { - Map: map[string]pgtype.Text{"wabble": {String: "wobble", Status: pgtype.Present}}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"wabble": {String: "wobble", Valid: true}}, + Valid: true, }, }, Dimensions: []pgtype.ArrayDimension{ @@ -414,7 +414,7 @@ func TestHstoreArrayAssignTo(t *testing.T) { {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present, + Valid: true, }, dst: &hstoreArrayDim4, expected: [2][1][1][3]map[string]string{ diff --git a/hstore_test.go b/hstore_test.go index 73ee0612..9c26a3df 100644 --- a/hstore_test.go +++ b/hstore_test.go @@ -10,22 +10,22 @@ import ( func TestHstoreTranscode(t *testing.T) { text := func(s string) pgtype.Text { - return pgtype.Text{String: s, Status: pgtype.Present} + return pgtype.Text{String: s, Valid: true} } values := []interface{}{ - &pgtype.Hstore{Map: map[string]pgtype.Text{}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(""), "bar": text(""), "baz": text("123")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present}, - &pgtype.Hstore{Map: map[string]pgtype.Text{"": text("bar")}, Status: pgtype.Present}, + &pgtype.Hstore{Map: map[string]pgtype.Text{}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(""), "bar": text(""), "baz": text("123")}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar")}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"NULL": text("bar")}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("NULL")}, Valid: true}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"": text("bar")}, Valid: true}, &pgtype.Hstore{ - Map: map[string]pgtype.Text{"a": text("a"), "b": {Status: pgtype.Null}, "c": text("c"), "d": {Status: pgtype.Null}, "e": text("e")}, - Status: pgtype.Present, + Map: map[string]pgtype.Text{"a": text("a"), "b": {}, "c": text("c"), "d": {}, "e": text("e")}, + Valid: true, }, - &pgtype.Hstore{Status: pgtype.Null}, + &pgtype.Hstore{}, } specialStrings := []string{ @@ -39,23 +39,23 @@ func TestHstoreTranscode(t *testing.T) { } for _, s := range specialStrings { // Special key values - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Valid: true}) // at beginning + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Valid: true}) // in middle + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Valid: true}) // at end + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Valid: true}) // is key // Special value values - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Valid: true}) // at beginning + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Valid: true}) // in middle + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Valid: true}) // at end + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Valid: true}) // is key } testutil.TestSuccessfulTranscodeEqFunc(t, "hstore", values, func(ai, bi interface{}) bool { a := ai.(pgtype.Hstore) b := bi.(pgtype.Hstore) - if len(a.Map) != len(b.Map) || a.Status != b.Status { + if len(a.Map) != len(b.Map) || a.Valid != b.Valid { return false } @@ -70,12 +70,12 @@ func TestHstoreTranscode(t *testing.T) { } func TestHstoreTranscodeNullable(t *testing.T) { - text := func(s string, status pgtype.Status) pgtype.Text { - return pgtype.Text{String: s, Status: status} + text := func(s string, valid bool) pgtype.Text { + return pgtype.Text{String: s, Valid: valid} } values := []interface{}{ - &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("", pgtype.Null)}, Status: pgtype.Present}, + &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("", false)}, Valid: true}, } specialStrings := []string{ @@ -89,17 +89,17 @@ func TestHstoreTranscodeNullable(t *testing.T) { } for _, s := range specialStrings { // Special key values - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("", pgtype.Null)}, Status: pgtype.Present}) // at beginning - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("", pgtype.Null)}, Status: pgtype.Present}) // in middle - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("", pgtype.Null)}, Status: pgtype.Present}) // at end - values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("", pgtype.Null)}, Status: pgtype.Present}) // is key + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("", false)}, Valid: true}) // at beginning + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("", false)}, Valid: true}) // in middle + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("", false)}, Valid: true}) // at end + values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("", false)}, Valid: true}) // is key } testutil.TestSuccessfulTranscodeEqFunc(t, "hstore", values, func(ai, bi interface{}) bool { a := ai.(pgtype.Hstore) b := bi.(pgtype.Hstore) - if len(a.Map) != len(b.Map) || a.Status != b.Status { + if len(a.Map) != len(b.Map) || a.Valid != b.Valid { return false } @@ -118,7 +118,7 @@ func TestHstoreSet(t *testing.T) { src map[string]string result pgtype.Hstore }{ - {src: map[string]string{"foo": "bar"}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}}, + {src: map[string]string{"foo": "bar"}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, Valid: true}}, } for i, tt := range successfulTests { @@ -139,7 +139,7 @@ func TestHstoreSetNullable(t *testing.T) { src map[string]*string result pgtype.Hstore }{ - {src: map[string]*string{"foo": nil}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {Status: pgtype.Null}}, Status: pgtype.Present}}, + {src: map[string]*string{"foo": nil}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {}}, Valid: true}}, } for i, tt := range successfulTests { @@ -163,8 +163,8 @@ func TestHstoreAssignTo(t *testing.T) { dst *map[string]string expected map[string]string }{ - {src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}, dst: &m, expected: map[string]string{"foo": "bar"}}, - {src: pgtype.Hstore{Status: pgtype.Null}, dst: &m, expected: ((map[string]string)(nil))}, + {src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Valid: true}}, Valid: true}, dst: &m, expected: map[string]string{"foo": "bar"}}, + {src: pgtype.Hstore{}, dst: &m, expected: ((map[string]string)(nil))}, } for i, tt := range simpleTests { @@ -187,8 +187,8 @@ func TestHstoreAssignToNullable(t *testing.T) { dst *map[string]*string expected map[string]*string }{ - {src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {Status: pgtype.Null}}, Status: pgtype.Present}, dst: &m, expected: map[string]*string{"foo": nil}}, - {src: pgtype.Hstore{Status: pgtype.Null}, dst: &m, expected: ((map[string]*string)(nil))}, + {src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {}}, Valid: true}, dst: &m, expected: map[string]*string{"foo": nil}}, + {src: pgtype.Hstore{}, dst: &m, expected: ((map[string]*string)(nil))}, } for i, tt := range simpleTests { diff --git a/inet.go b/inet.go index f35f88ba..4b3217a9 100644 --- a/inet.go +++ b/inet.go @@ -16,13 +16,13 @@ const ( // Inet represents both inet and cidr PostgreSQL types. type Inet struct { - IPNet *net.IPNet - Status Status + IPNet *net.IPNet + Valid bool } func (dst *Inet) Set(src interface{}) error { if src == nil { - *dst = Inet{Status: Null} + *dst = Inet{} return nil } @@ -35,14 +35,14 @@ func (dst *Inet) Set(src interface{}) error { switch value := src.(type) { case net.IPNet: - *dst = Inet{IPNet: &value, Status: Present} + *dst = Inet{IPNet: &value, Valid: true} case net.IP: if len(value) == 0 { - *dst = Inet{Status: Null} + *dst = Inet{} } else { bitCount := len(value) * 8 mask := net.CIDRMask(bitCount, bitCount) - *dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Status: Present} + *dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Valid: true} } case string: ip, ipnet, err := net.ParseCIDR(value) @@ -58,22 +58,22 @@ func (dst *Inet) Set(src interface{}) error { } } ipnet.IP = ip - *dst = Inet{IPNet: ipnet, Status: Present} + *dst = Inet{IPNet: ipnet, Valid: true} case *net.IPNet: if value == nil { - *dst = Inet{Status: Null} + *dst = Inet{} } else { return dst.Set(*value) } case *net.IP: if value == nil { - *dst = Inet{Status: Null} + *dst = Inet{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Inet{Status: Null} + *dst = Inet{} } else { return dst.Set(*value) } @@ -88,51 +88,44 @@ func (dst *Inet) Set(src interface{}) error { } func (dst Inet) Get() interface{} { - switch dst.Status { - case Present: - return dst.IPNet - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.IPNet } func (src *Inet) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *net.IPNet: - *v = net.IPNet{ - IP: make(net.IP, len(src.IPNet.IP)), - Mask: make(net.IPMask, len(src.IPNet.Mask)), - } - copy(v.IP, src.IPNet.IP) - copy(v.Mask, src.IPNet.Mask) - return nil - case *net.IP: - if oneCount, bitCount := src.IPNet.Mask.Size(); oneCount != bitCount { - return fmt.Errorf("cannot assign %v to %T", src, dst) - } - *v = make(net.IP, len(src.IPNet.IP)) - copy(*v, src.IPNet.IP) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *net.IPNet: + *v = net.IPNet{ + IP: make(net.IP, len(src.IPNet.IP)), + Mask: make(net.IPMask, len(src.IPNet.Mask)), + } + copy(v.IP, src.IPNet.IP) + copy(v.Mask, src.IPNet.Mask) + return nil + case *net.IP: + if oneCount, bitCount := src.IPNet.Mask.Size(); oneCount != bitCount { + return fmt.Errorf("cannot assign %v to %T", src, dst) + } + *v = make(net.IP, len(src.IPNet.IP)) + copy(*v, src.IPNet.IP) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Inet) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Inet{Status: Null} + *dst = Inet{} return nil } @@ -158,13 +151,13 @@ func (dst *Inet) DecodeText(ci *ConnInfo, src []byte) error { *ipnet = net.IPNet{IP: ip, Mask: net.CIDRMask(ones, len(ip)*8)} } - *dst = Inet{IPNet: ipnet, Status: Present} + *dst = Inet{IPNet: ipnet, Valid: true} return nil } func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Inet{Status: Null} + *dst = Inet{} return nil } @@ -185,17 +178,14 @@ func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error { } ipnet.Mask = net.CIDRMask(int(bits), len(ipnet.IP)*8) - *dst = Inet{IPNet: &ipnet, Status: Present} + *dst = Inet{IPNet: &ipnet, Valid: true} return nil } func (src Inet) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.IPNet.String()...), nil @@ -203,11 +193,8 @@ func (src Inet) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // EncodeBinary encodes src into w. func (src Inet) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var family byte @@ -236,7 +223,7 @@ func (src Inet) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Inet) Scan(src interface{}) error { if src == nil { - *dst = Inet{Status: Null} + *dst = Inet{} return nil } diff --git a/inet_array.go b/inet_array.go index 2460a1c4..7f41c4e5 100644 --- a/inet_array.go +++ b/inet_array.go @@ -15,13 +15,13 @@ import ( type InetArray struct { Elements []Inet Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *InetArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} return nil } @@ -37,9 +37,9 @@ func (dst *InetArray) Set(src interface{}) error { case []*net.IPNet: if value == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} } else if len(value) == 0 { - *dst = InetArray{Status: Present} + *dst = InetArray{Valid: true} } else { elements := make([]Inet, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *InetArray) Set(src interface{}) error { *dst = InetArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []net.IP: if value == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} } else if len(value) == 0 { - *dst = InetArray{Status: Present} + *dst = InetArray{Valid: true} } else { elements := make([]Inet, len(value)) for i := range value { @@ -69,15 +69,15 @@ func (dst *InetArray) Set(src interface{}) error { *dst = InetArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*net.IP: if value == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} } else if len(value) == 0 { - *dst = InetArray{Status: Present} + *dst = InetArray{Valid: true} } else { elements := make([]Inet, len(value)) for i := range value { @@ -88,20 +88,20 @@ func (dst *InetArray) Set(src interface{}) error { *dst = InetArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Inet: if value == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} } else if len(value) == 0 { - *dst = InetArray{Status: Present} + *dst = InetArray{Valid: true} } else { *dst = InetArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -110,7 +110,7 @@ func (dst *InetArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = InetArray{Status: Null} + *dst = InetArray{} return nil } @@ -119,7 +119,7 @@ func (dst *InetArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for InetArray", src) } if elementsLength == 0 { - *dst = InetArray{Status: Present} + *dst = InetArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -132,7 +132,7 @@ func (dst *InetArray) Set(src interface{}) error { *dst = InetArray{ Elements: make([]Inet, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -199,93 +199,86 @@ func (dst *InetArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst InetArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *InetArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]*net.IPNet: - *v = make([]*net.IPNet, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]net.IP: - *v = make([]net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*net.IP: - *v = make([]*net.IP, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]*net.IPNet: + *v = make([]*net.IPNet, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]net.IP: + *v = make([]net.IP, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*net.IP: + *v = make([]*net.IP, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *InetArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -337,7 +330,7 @@ func (src *InetArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *InetArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} return nil } @@ -366,14 +359,14 @@ func (dst *InetArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = InetArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = InetArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *InetArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = InetArray{Status: Null} + *dst = InetArray{} return nil } @@ -384,7 +377,7 @@ func (dst *InetArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = InetArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = InetArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -409,16 +402,13 @@ func (dst *InetArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = InetArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = InetArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src InetArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -471,11 +461,8 @@ func (src InetArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src InetArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -489,7 +476,7 @@ func (src InetArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/inet_array_test.go b/inet_array_test.go index 46dc7d12..1019c7eb 100644 --- a/inet_array_test.go +++ b/inet_array_test.go @@ -14,41 +14,41 @@ func TestInetArrayTranscode(t *testing.T) { &pgtype.InetArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {Status: pgtype.Null}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.InetArray{Status: pgtype.Null}, + &pgtype.InetArray{}, &pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - {Status: pgtype.Null}, - {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Valid: true}, + {}, + {IPNet: mustParseCIDR(t, "255.0.0.0/8"), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -61,33 +61,33 @@ func TestInetArraySet(t *testing.T) { { source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, result: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]*net.IPNet)(nil)), - result: pgtype.InetArray{Status: pgtype.Null}, + result: pgtype.InetArray{}, }, { source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, result: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]net.IP)(nil)), - result: pgtype.InetArray{Status: pgtype.Null}, + result: pgtype.InetArray{}, }, { source: [][]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, result: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]*net.IPNet{ @@ -101,27 +101,27 @@ func TestInetArraySet(t *testing.T) { mustParseCIDR(t, "169.168.0.1/16")}}}}, result: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, result: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]*net.IPNet{ @@ -135,18 +135,18 @@ func TestInetArraySet(t *testing.T) { mustParseCIDR(t, "169.168.0.1/16")}}}}, result: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -178,85 +178,85 @@ func TestInetArrayAssignTo(t *testing.T) { }{ { src: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipnetSlice, expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")}, }, { src: pgtype.InetArray{ - Elements: []pgtype.Inet{{Status: pgtype.Null}}, + Elements: []pgtype.Inet{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipnetSlice, expected: []*net.IPNet{nil}, }, { src: pgtype.InetArray{ - Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, + Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipSlice, expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP}, }, { src: pgtype.InetArray{ - Elements: []pgtype.Inet{{Status: pgtype.Null}}, + Elements: []pgtype.Inet{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &ipSlice, expected: []net.IP{nil}, }, { - src: pgtype.InetArray{Status: pgtype.Null}, + src: pgtype.InetArray{}, dst: &ipnetSlice, expected: (([]*net.IPNet)(nil)), }, { - src: pgtype.InetArray{Status: pgtype.Present}, + src: pgtype.InetArray{Valid: true}, dst: &ipnetSlice, expected: []*net.IPNet{}, }, { - src: pgtype.InetArray{Status: pgtype.Null}, + src: pgtype.InetArray{}, dst: &ipSlice, expected: (([]net.IP)(nil)), }, { - src: pgtype.InetArray{Status: pgtype.Present}, + src: pgtype.InetArray{Valid: true}, dst: &ipSlice, expected: []net.IP{}, }, { src: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipSliceDim2, expected: [][]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, }, { src: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipnetSliceDim4, expected: [][][][]*net.IPNet{ {{{ @@ -271,28 +271,28 @@ func TestInetArrayAssignTo(t *testing.T) { { src: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/32"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipArrayDim2, expected: [2][1]net.IP{{mustParseCIDR(t, "127.0.0.1/32").IP}, {mustParseCIDR(t, "10.0.0.1/32").IP}}, }, { src: pgtype.InetArray{ Elements: []pgtype.Inet{ - {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Status: pgtype.Present}, - {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Status: pgtype.Present}}, + {IPNet: mustParseCIDR(t, "127.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "10.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "172.16.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "192.168.0.1/16"), Valid: true}, + {IPNet: mustParseCIDR(t, "224.0.0.1/24"), Valid: true}, + {IPNet: mustParseCIDR(t, "169.168.0.1/16"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &ipnetArrayDim4, expected: [2][1][1][3]*net.IPNet{ {{{ diff --git a/inet_test.go b/inet_test.go index 09c6b21f..c2a5dc28 100644 --- a/inet_test.go +++ b/inet_test.go @@ -12,35 +12,35 @@ import ( func TestInetTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "inet", []interface{}{ - &pgtype.Inet{IPNet: mustParseInet(t, "0.0.0.0/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "127.0.0.1/8"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "12.34.56.65/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "192.168.1.16/24"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "255.0.0.0/8"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "255.255.255.255/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "10.0.0.1"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "::1/64"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "::/0"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "::1/128"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e/64"), Status: pgtype.Present}, - &pgtype.Inet{Status: pgtype.Null}, + &pgtype.Inet{IPNet: mustParseInet(t, "0.0.0.0/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "127.0.0.1/8"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "12.34.56.65/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "192.168.1.16/24"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "255.0.0.0/8"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "255.255.255.255/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "10.0.0.1"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "::1/64"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "::/0"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "::1/128"), Valid: true}, + &pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e/64"), Valid: true}, + &pgtype.Inet{}, }) } func TestCidrTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "cidr", []interface{}{ - &pgtype.Inet{IPNet: mustParseCIDR(t, "0.0.0.0/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.1.0/24"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "255.255.255.255/32"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::/128"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::/0"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "::1/128"), Status: pgtype.Present}, - &pgtype.Inet{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present}, - &pgtype.Inet{Status: pgtype.Null}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "0.0.0.0/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.1.0/24"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "255.255.255.255/32"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "::/128"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "::/0"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "::1/128"), Valid: true}, + &pgtype.Inet{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Valid: true}, + &pgtype.Inet{}, }) } @@ -49,13 +49,13 @@ func TestInetSet(t *testing.T) { source interface{} result pgtype.Inet }{ - {source: mustParseCIDR(t, "127.0.0.1/32"), result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - {source: mustParseCIDR(t, "127.0.0.1/32").IP, result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - {source: "127.0.0.1/32", result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}}, - {source: "1.2.3.4/24", result: pgtype.Inet{IPNet: &net.IPNet{IP: net.ParseIP("1.2.3.4"), Mask: net.CIDRMask(24, 32)}, Status: pgtype.Present}}, - {source: "10.0.0.1", result: pgtype.Inet{IPNet: mustParseInet(t, "10.0.0.1"), Status: pgtype.Present}}, - {source: "2607:f8b0:4009:80b::200e", result: pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e"), Status: pgtype.Present}}, - {source: net.ParseIP(""), result: pgtype.Inet{Status: pgtype.Null}}, + {source: mustParseCIDR(t, "127.0.0.1/32"), result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, + {source: mustParseCIDR(t, "127.0.0.1/32").IP, result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, + {source: "127.0.0.1/32", result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}}, + {source: "1.2.3.4/24", result: pgtype.Inet{IPNet: &net.IPNet{IP: net.ParseIP("1.2.3.4"), Mask: net.CIDRMask(24, 32)}, Valid: true}}, + {source: "10.0.0.1", result: pgtype.Inet{IPNet: mustParseInet(t, "10.0.0.1"), Valid: true}}, + {source: "2607:f8b0:4009:80b::200e", result: pgtype.Inet{IPNet: mustParseInet(t, "2607:f8b0:4009:80b::200e"), Valid: true}}, + {source: net.ParseIP(""), result: pgtype.Inet{}}, } for i, tt := range successfulTests { @@ -66,8 +66,8 @@ func TestInetSet(t *testing.T) { continue } - assert.Equalf(t, tt.result.Status, r.Status, "%d: Status", i) - if tt.result.Status == pgtype.Present { + assert.Equalf(t, tt.result.Valid, r.Valid, "%d: Status", i) + if tt.result.Valid { assert.Equalf(t, tt.result.IPNet.Mask, r.IPNet.Mask, "%d: IP", i) assert.Truef(t, tt.result.IPNet.IP.Equal(r.IPNet.IP), "%d: Mask", i) } @@ -85,10 +85,10 @@ func TestInetAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &pipnet, expected: ((*net.IPNet)(nil))}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &pip, expected: ((*net.IP)(nil))}, + {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, dst: &ipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, + {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, dst: &ip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, + {src: pgtype.Inet{}, dst: &pipnet, expected: ((*net.IPNet)(nil))}, + {src: pgtype.Inet{}, dst: &pip, expected: ((*net.IP)(nil))}, } for i, tt := range simpleTests { @@ -107,8 +107,8 @@ func TestInetAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, + {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, dst: &pipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")}, + {src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Valid: true}, dst: &pip, expected: mustParseCIDR(t, "127.0.0.1/32").IP}, } for i, tt := range pointerAllocTests { @@ -126,8 +126,8 @@ func TestInetAssignTo(t *testing.T) { src pgtype.Inet dst interface{} }{ - {src: pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.0.0/16"), Status: pgtype.Present}, dst: &ip}, - {src: pgtype.Inet{Status: pgtype.Null}, dst: &ipnet}, + {src: pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.0.0/16"), Valid: true}, dst: &ip}, + {src: pgtype.Inet{}, dst: &ipnet}, } for i, tt := range errorTests { diff --git a/int2.go b/int2.go index 3eb5aeb5..bbfee1cf 100644 --- a/int2.go +++ b/int2.go @@ -11,13 +11,13 @@ import ( ) type Int2 struct { - Int int16 - Status Status + Int int16 + Valid bool } func (dst *Int2) Set(src interface{}) error { if src == nil { - *dst = Int2{Status: Null} + *dst = Int2{} return nil } @@ -30,16 +30,16 @@ func (dst *Int2) Set(src interface{}) error { switch value := src.(type) { case int8: - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case uint8: - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case int16: - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case uint16: if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case int32: if value < math.MinInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) @@ -47,12 +47,12 @@ func (dst *Int2) Set(src interface{}) error { if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case uint32: if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case int64: if value < math.MinInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) @@ -60,12 +60,12 @@ func (dst *Int2) Set(src interface{}) error { if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case uint64: if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case int: if value < math.MinInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) @@ -73,103 +73,103 @@ func (dst *Int2) Set(src interface{}) error { if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case uint: if value > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case string: num, err := strconv.ParseInt(value, 10, 16) if err != nil { return err } - *dst = Int2{Int: int16(num), Status: Present} + *dst = Int2{Int: int16(num), Valid: true} case float32: if value > math.MaxInt16 { return fmt.Errorf("%f is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case float64: if value > math.MaxInt16 { return fmt.Errorf("%f is greater than maximum value for Int2", value) } - *dst = Int2{Int: int16(value), Status: Present} + *dst = Int2{Int: int16(value), Valid: true} case *int8: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } case *float64: if value == nil { - *dst = Int2{Status: Null} + *dst = Int2{} } else { return dst.Set(*value) } @@ -184,23 +184,19 @@ func (dst *Int2) Set(src interface{}) error { } func (dst Int2) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Int } func (src *Int2) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) + return int64AssignTo(int64(src.Int), src.Valid, dst) } func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int2{Status: Null} + *dst = Int2{} return nil } @@ -209,13 +205,13 @@ func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Int2{Int: int16(n), Status: Present} + *dst = Int2{Int: int16(n), Valid: true} return nil } func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int2{Status: Null} + *dst = Int2{} return nil } @@ -224,27 +220,21 @@ func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error { } n := int16(binary.BigEndian.Uint16(src)) - *dst = Int2{Int: n, Status: Present} + *dst = Int2{Int: n, Valid: true} return nil } func (src Int2) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil } func (src Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return pgio.AppendInt16(buf, src.Int), nil @@ -253,7 +243,7 @@ func (src Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Int2) Scan(src interface{}) error { if src == nil { - *dst = Int2{Status: Null} + *dst = Int2{} return nil } @@ -265,7 +255,7 @@ func (dst *Int2) Scan(src interface{}) error { if src > math.MaxInt16 { return fmt.Errorf("%d is greater than maximum value for Int2", src) } - *dst = Int2{Int: int16(src), Status: Present} + *dst = Int2{Int: int16(src), Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -280,25 +270,15 @@ func (dst *Int2) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Int2) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return int64(src.Int), nil } func (src Int2) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(int64(src.Int), 10)), nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return []byte(strconv.FormatInt(int64(src.Int), 10)), nil } diff --git a/int2_array.go b/int2_array.go index a5133845..d96240dc 100644 --- a/int2_array.go +++ b/int2_array.go @@ -14,13 +14,13 @@ import ( type Int2Array struct { Elements []Int2 Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *Int2Array) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} return nil } @@ -36,9 +36,9 @@ func (dst *Int2Array) Set(src interface{}) error { case []int16: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int16: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -68,15 +68,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint16: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -87,15 +87,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint16: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -106,15 +106,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int32: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -125,15 +125,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int32: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -144,15 +144,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint32: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -163,15 +163,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint32: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -182,15 +182,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int64: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -201,15 +201,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int64: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -220,15 +220,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint64: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -239,15 +239,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint64: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -258,15 +258,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -277,15 +277,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -296,15 +296,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -315,15 +315,15 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { elements := make([]Int2, len(value)) for i := range value { @@ -334,20 +334,20 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Int2: if value == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} } else if len(value) == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} } else { *dst = Int2Array{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -356,7 +356,7 @@ func (dst *Int2Array) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} return nil } @@ -365,7 +365,7 @@ func (dst *Int2Array) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for Int2Array", src) } if elementsLength == 0 { - *dst = Int2Array{Status: Present} + *dst = Int2Array{Valid: true} return nil } if len(dimensions) == 0 { @@ -378,7 +378,7 @@ func (dst *Int2Array) Set(src interface{}) error { *dst = Int2Array{ Elements: make([]Int2, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -445,210 +445,203 @@ func (dst *Int2Array) setRecursive(value reflect.Value, index, dimension int) (i } func (dst Int2Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Int2Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]int16: - *v = make([]int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int16: - *v = make([]*int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint16: - *v = make([]uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint16: - *v = make([]*uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int32: - *v = make([]int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int32: - *v = make([]*int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint32: - *v = make([]uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint32: - *v = make([]*uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int64: - *v = make([]*int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint64: - *v = make([]*uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int: - *v = make([]int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int: - *v = make([]*int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint: - *v = make([]uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint: - *v = make([]*uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]int16: + *v = make([]int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int16: + *v = make([]*int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint16: + *v = make([]uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint16: + *v = make([]*uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int32: + *v = make([]int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int32: + *v = make([]*int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint32: + *v = make([]uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint32: + *v = make([]*uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int64: + *v = make([]int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int64: + *v = make([]*int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint64: + *v = make([]uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint64: + *v = make([]*uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int: + *v = make([]int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int: + *v = make([]*int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint: + *v = make([]uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint: + *v = make([]*uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *Int2Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -700,7 +693,7 @@ func (src *Int2Array) assignToRecursive(value reflect.Value, index, dimension in func (dst *Int2Array) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} return nil } @@ -729,14 +722,14 @@ func (dst *Int2Array) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Int2Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = Int2Array{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *Int2Array) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int2Array{Status: Null} + *dst = Int2Array{} return nil } @@ -747,7 +740,7 @@ func (dst *Int2Array) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = Int2Array{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int2Array{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -772,16 +765,13 @@ func (dst *Int2Array) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = Int2Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int2Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src Int2Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -834,11 +824,8 @@ func (src Int2Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Int2Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -852,7 +839,7 @@ func (src Int2Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/int2_array_test.go b/int2_array_test.go index 17c37360..78dc532a 100644 --- a/int2_array_test.go +++ b/int2_array_test.go @@ -13,41 +13,41 @@ func TestInt2ArrayTranscode(t *testing.T) { &pgtype.Int2Array{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Int: 1, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Int2Array{Status: pgtype.Null}, + &pgtype.Int2Array{}, &pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {}, + {Int: 6, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,103 +60,103 @@ func TestInt2ArraySet(t *testing.T) { { source: []int64{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int32{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int16{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint64{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint32{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint16{1}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]int16)(nil)), - result: pgtype.Int2Array{Status: pgtype.Null}, + result: pgtype.Int2Array{}, }, { source: [][]int16{{1}, {2}}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]int16{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]int16{{1}, {2}}, result: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]int16{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -189,90 +189,90 @@ func TestInt2ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int16Slice, expected: []int16{1}, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint16Slice, expected: []uint16{1}, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedInt16Slice, expected: _int16Slice{1}, }, { - src: pgtype.Int2Array{Status: pgtype.Null}, + src: pgtype.Int2Array{}, dst: &int16Slice, expected: (([]int16)(nil)), }, { - src: pgtype.Int2Array{Status: pgtype.Present}, + src: pgtype.Int2Array{Valid: true}, dst: &int16Slice, expected: []int16{}, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]int16{{1}, {2}}, dst: &int16SliceDim2, }, { src: pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]int16{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int16SliceDim4, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]int16{{1}, {2}}, dst: &int16ArrayDim2, }, { src: pgtype.Int2Array{ Elements: []pgtype.Int2{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]int16{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int16ArrayDim4, }, @@ -295,39 +295,39 @@ func TestInt2ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Status: pgtype.Null}}, + Elements: []pgtype.Int2{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int16Slice, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: -1, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: -1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint16Slice, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int16ArrayDim2, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int16Slice, }, { src: pgtype.Int2Array{ - Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int2{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &int16ArrayDim4, }, } diff --git a/int2_test.go b/int2_test.go index 178eb278..6ed8fe90 100644 --- a/int2_test.go +++ b/int2_test.go @@ -11,12 +11,12 @@ import ( func TestInt2Transcode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "int2", []interface{}{ - &pgtype.Int2{Int: math.MinInt16, Status: pgtype.Present}, - &pgtype.Int2{Int: -1, Status: pgtype.Present}, - &pgtype.Int2{Int: 0, Status: pgtype.Present}, - &pgtype.Int2{Int: 1, Status: pgtype.Present}, - &pgtype.Int2{Int: math.MaxInt16, Status: pgtype.Present}, - &pgtype.Int2{Int: 0, Status: pgtype.Null}, + &pgtype.Int2{Int: math.MinInt16, Valid: true}, + &pgtype.Int2{Int: -1, Valid: true}, + &pgtype.Int2{Int: 0, Valid: true}, + &pgtype.Int2{Int: 1, Valid: true}, + &pgtype.Int2{Int: math.MaxInt16, Valid: true}, + &pgtype.Int2{Int: 0}, }) } @@ -25,22 +25,22 @@ func TestInt2Set(t *testing.T) { source interface{} result pgtype.Int2 }{ - {source: int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: float32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}}, + {source: int8(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: int16(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: int32(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: int64(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: int8(-1), result: pgtype.Int2{Int: -1, Valid: true}}, + {source: int16(-1), result: pgtype.Int2{Int: -1, Valid: true}}, + {source: int32(-1), result: pgtype.Int2{Int: -1, Valid: true}}, + {source: int64(-1), result: pgtype.Int2{Int: -1, Valid: true}}, + {source: uint8(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: uint16(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: uint32(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: uint64(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: float32(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: float64(1), result: pgtype.Int2{Int: 1, Valid: true}}, + {source: "1", result: pgtype.Int2{Int: 1, Valid: true}}, + {source: _int8(1), result: pgtype.Int2{Int: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -76,19 +76,19 @@ func TestInt2AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &i8, expected: int8(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.Int2{Int: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.Int2{Int: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -107,8 +107,8 @@ func TestInt2AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &pi8, expected: int8(42)}, + {src: pgtype.Int2{Int: 42, Valid: true}, dst: &_pi8, expected: _int8(42)}, } for i, tt := range pointerAllocTests { @@ -126,13 +126,13 @@ func TestInt2AssignTo(t *testing.T) { src pgtype.Int2 dst interface{} }{ - {src: pgtype.Int2{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &i16}, + {src: pgtype.Int2{Int: 150, Valid: true}, dst: &i8}, + {src: pgtype.Int2{Int: -1, Valid: true}, dst: &ui8}, + {src: pgtype.Int2{Int: -1, Valid: true}, dst: &ui16}, + {src: pgtype.Int2{Int: -1, Valid: true}, dst: &ui32}, + {src: pgtype.Int2{Int: -1, Valid: true}, dst: &ui64}, + {src: pgtype.Int2{Int: -1, Valid: true}, dst: &ui}, + {src: pgtype.Int2{Int: 0}, dst: &i16}, } for i, tt := range errorTests { diff --git a/int4.go b/int4.go index 22b48e5e..6f1e61f3 100644 --- a/int4.go +++ b/int4.go @@ -12,13 +12,13 @@ import ( ) type Int4 struct { - Int int32 - Status Status + Int int32 + Valid bool } func (dst *Int4) Set(src interface{}) error { if src == nil { - *dst = Int4{Status: Null} + *dst = Int4{} return nil } @@ -31,20 +31,20 @@ func (dst *Int4) Set(src interface{}) error { switch value := src.(type) { case int8: - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case uint8: - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case int16: - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case uint16: - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case int32: - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case uint32: if value > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case int64: if value < math.MinInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) @@ -52,12 +52,12 @@ func (dst *Int4) Set(src interface{}) error { if value > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case uint64: if value > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case int: if value < math.MinInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) @@ -65,103 +65,103 @@ func (dst *Int4) Set(src interface{}) error { if value > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case uint: if value > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case string: num, err := strconv.ParseInt(value, 10, 32) if err != nil { return err } - *dst = Int4{Int: int32(num), Status: Present} + *dst = Int4{Int: int32(num), Valid: true} case float32: if value > math.MaxInt32 { return fmt.Errorf("%f is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case float64: if value > math.MaxInt32 { return fmt.Errorf("%f is greater than maximum value for Int4", value) } - *dst = Int4{Int: int32(value), Status: Present} + *dst = Int4{Int: int32(value), Valid: true} case *int8: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } case *float64: if value == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { return dst.Set(*value) } @@ -176,23 +176,19 @@ func (dst *Int4) Set(src interface{}) error { } func (dst Int4) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Int } func (src *Int4) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) + return int64AssignTo(int64(src.Int), src.Valid, dst) } func (dst *Int4) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4{Status: Null} + *dst = Int4{} return nil } @@ -201,13 +197,13 @@ func (dst *Int4) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Int4{Int: int32(n), Status: Present} + *dst = Int4{Int: int32(n), Valid: true} return nil } func (dst *Int4) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4{Status: Null} + *dst = Int4{} return nil } @@ -216,27 +212,21 @@ func (dst *Int4) DecodeBinary(ci *ConnInfo, src []byte) error { } n := int32(binary.BigEndian.Uint32(src)) - *dst = Int4{Int: n, Status: Present} + *dst = Int4{Int: n, Valid: true} return nil } func (src Int4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil } func (src Int4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return pgio.AppendInt32(buf, src.Int), nil @@ -245,7 +235,7 @@ func (src Int4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Int4) Scan(src interface{}) error { if src == nil { - *dst = Int4{Status: Null} + *dst = Int4{} return nil } @@ -257,7 +247,7 @@ func (dst *Int4) Scan(src interface{}) error { if src > math.MaxInt32 { return fmt.Errorf("%d is greater than maximum value for Int4", src) } - *dst = Int4{Int: int32(src), Status: Present} + *dst = Int4{Int: int32(src), Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -272,27 +262,17 @@ func (dst *Int4) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Int4) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return int64(src.Int), nil } func (src Int4) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(int64(src.Int), 10)), nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return []byte(strconv.FormatInt(int64(src.Int), 10)), nil } func (dst *Int4) UnmarshalJSON(b []byte) error { @@ -303,9 +283,9 @@ func (dst *Int4) UnmarshalJSON(b []byte) error { } if n == nil { - *dst = Int4{Status: Null} + *dst = Int4{} } else { - *dst = Int4{Int: *n, Status: Present} + *dst = Int4{Int: *n, Valid: true} } return nil diff --git a/int4_array.go b/int4_array.go index de26236f..e725e7a8 100644 --- a/int4_array.go +++ b/int4_array.go @@ -14,13 +14,13 @@ import ( type Int4Array struct { Elements []Int4 Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *Int4Array) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} return nil } @@ -36,9 +36,9 @@ func (dst *Int4Array) Set(src interface{}) error { case []int16: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int16: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -68,15 +68,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint16: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -87,15 +87,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint16: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -106,15 +106,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int32: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -125,15 +125,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int32: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -144,15 +144,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint32: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -163,15 +163,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint32: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -182,15 +182,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int64: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -201,15 +201,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int64: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -220,15 +220,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint64: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -239,15 +239,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint64: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -258,15 +258,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -277,15 +277,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -296,15 +296,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -315,15 +315,15 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { elements := make([]Int4, len(value)) for i := range value { @@ -334,20 +334,20 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Int4: if value == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} } else if len(value) == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} } else { *dst = Int4Array{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -356,7 +356,7 @@ func (dst *Int4Array) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} return nil } @@ -365,7 +365,7 @@ func (dst *Int4Array) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for Int4Array", src) } if elementsLength == 0 { - *dst = Int4Array{Status: Present} + *dst = Int4Array{Valid: true} return nil } if len(dimensions) == 0 { @@ -378,7 +378,7 @@ func (dst *Int4Array) Set(src interface{}) error { *dst = Int4Array{ Elements: make([]Int4, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -445,210 +445,203 @@ func (dst *Int4Array) setRecursive(value reflect.Value, index, dimension int) (i } func (dst Int4Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Int4Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]int16: - *v = make([]int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int16: - *v = make([]*int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint16: - *v = make([]uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint16: - *v = make([]*uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int32: - *v = make([]int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int32: - *v = make([]*int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint32: - *v = make([]uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint32: - *v = make([]*uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int64: - *v = make([]*int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint64: - *v = make([]*uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int: - *v = make([]int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int: - *v = make([]*int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint: - *v = make([]uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint: - *v = make([]*uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]int16: + *v = make([]int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int16: + *v = make([]*int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint16: + *v = make([]uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint16: + *v = make([]*uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int32: + *v = make([]int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int32: + *v = make([]*int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint32: + *v = make([]uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint32: + *v = make([]*uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int64: + *v = make([]int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int64: + *v = make([]*int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint64: + *v = make([]uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint64: + *v = make([]*uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int: + *v = make([]int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int: + *v = make([]*int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint: + *v = make([]uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint: + *v = make([]*uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *Int4Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -700,7 +693,7 @@ func (src *Int4Array) assignToRecursive(value reflect.Value, index, dimension in func (dst *Int4Array) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} return nil } @@ -729,14 +722,14 @@ func (dst *Int4Array) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Int4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = Int4Array{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *Int4Array) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4Array{Status: Null} + *dst = Int4Array{} return nil } @@ -747,7 +740,7 @@ func (dst *Int4Array) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = Int4Array{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int4Array{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -772,16 +765,13 @@ func (dst *Int4Array) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = Int4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src Int4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -834,11 +824,8 @@ func (src Int4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Int4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -852,7 +839,7 @@ func (src Int4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/int4_array_test.go b/int4_array_test.go index 110512a9..a9c9acd9 100644 --- a/int4_array_test.go +++ b/int4_array_test.go @@ -14,41 +14,41 @@ func TestInt4ArrayTranscode(t *testing.T) { &pgtype.Int4Array{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Int: 1, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Int4Array{Status: pgtype.Null}, + &pgtype.Int4Array{}, &pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {}, + {Int: 6, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -62,30 +62,30 @@ func TestInt4ArraySet(t *testing.T) { { source: []int64{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int32{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int16{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int{1, math.MaxInt32 + 1, 2}, @@ -94,75 +94,75 @@ func TestInt4ArraySet(t *testing.T) { { source: []uint64{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint32{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint16{1}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]int32)(nil)), - result: pgtype.Int4Array{Status: pgtype.Null}, + result: pgtype.Int4Array{}, }, { source: [][]int32{{1}, {2}}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]int32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]int32{{1}, {2}}, result: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]int32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -203,90 +203,90 @@ func TestInt4ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int32Slice, expected: []int32{1}, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint32Slice, expected: []uint32{1}, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedInt32Slice, expected: _int32Slice{1}, }, { - src: pgtype.Int4Array{Status: pgtype.Null}, + src: pgtype.Int4Array{}, dst: &int32Slice, expected: (([]int32)(nil)), }, { - src: pgtype.Int4Array{Status: pgtype.Present}, + src: pgtype.Int4Array{Valid: true}, dst: &int32Slice, expected: []int32{}, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]int32{{1}, {2}}, dst: &int32SliceDim2, }, { src: pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]int32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int32SliceDim4, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]int32{{1}, {2}}, dst: &int32ArrayDim2, }, { src: pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]int32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int32ArrayDim4, }, @@ -309,39 +309,39 @@ func TestInt4ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Status: pgtype.Null}}, + Elements: []pgtype.Int4{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int32Slice, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: -1, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: -1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint32Slice, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int32ArrayDim2, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int32Slice, }, { src: pgtype.Int4Array{ - Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int4{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &int32ArrayDim4, }, } diff --git a/int4_test.go b/int4_test.go index ae01114f..3085babd 100644 --- a/int4_test.go +++ b/int4_test.go @@ -11,12 +11,12 @@ import ( func TestInt4Transcode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "int4", []interface{}{ - &pgtype.Int4{Int: math.MinInt32, Status: pgtype.Present}, - &pgtype.Int4{Int: -1, Status: pgtype.Present}, - &pgtype.Int4{Int: 0, Status: pgtype.Present}, - &pgtype.Int4{Int: 1, Status: pgtype.Present}, - &pgtype.Int4{Int: math.MaxInt32, Status: pgtype.Present}, - &pgtype.Int4{Int: 0, Status: pgtype.Null}, + &pgtype.Int4{Int: math.MinInt32, Valid: true}, + &pgtype.Int4{Int: -1, Valid: true}, + &pgtype.Int4{Int: 0, Valid: true}, + &pgtype.Int4{Int: 1, Valid: true}, + &pgtype.Int4{Int: math.MaxInt32, Valid: true}, + &pgtype.Int4{Int: 0}, }) } @@ -25,22 +25,22 @@ func TestInt4Set(t *testing.T) { source interface{} result pgtype.Int4 }{ - {source: int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: float32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, + {source: int8(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: int16(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: int32(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: int64(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: int8(-1), result: pgtype.Int4{Int: -1, Valid: true}}, + {source: int16(-1), result: pgtype.Int4{Int: -1, Valid: true}}, + {source: int32(-1), result: pgtype.Int4{Int: -1, Valid: true}}, + {source: int64(-1), result: pgtype.Int4{Int: -1, Valid: true}}, + {source: uint8(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: uint16(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: uint32(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: uint64(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: float32(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: float64(1), result: pgtype.Int4{Int: 1, Valid: true}}, + {source: "1", result: pgtype.Int4{Int: 1, Valid: true}}, + {source: _int8(1), result: pgtype.Int4{Int: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -76,19 +76,19 @@ func TestInt4AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &i8, expected: int8(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.Int4{Int: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.Int4{Int: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -107,8 +107,8 @@ func TestInt4AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &pi8, expected: int8(42)}, + {src: pgtype.Int4{Int: 42, Valid: true}, dst: &_pi8, expected: _int8(42)}, } for i, tt := range pointerAllocTests { @@ -126,14 +126,14 @@ func TestInt4AssignTo(t *testing.T) { src pgtype.Int4 dst interface{} }{ - {src: pgtype.Int4{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int4{Int: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &i32}, + {src: pgtype.Int4{Int: 150, Valid: true}, dst: &i8}, + {src: pgtype.Int4{Int: 40000, Valid: true}, dst: &i16}, + {src: pgtype.Int4{Int: -1, Valid: true}, dst: &ui8}, + {src: pgtype.Int4{Int: -1, Valid: true}, dst: &ui16}, + {src: pgtype.Int4{Int: -1, Valid: true}, dst: &ui32}, + {src: pgtype.Int4{Int: -1, Valid: true}, dst: &ui64}, + {src: pgtype.Int4{Int: -1, Valid: true}, dst: &ui}, + {src: pgtype.Int4{Int: 0}, dst: &i32}, } for i, tt := range errorTests { @@ -149,8 +149,8 @@ func TestInt4MarshalJSON(t *testing.T) { source pgtype.Int4 result string }{ - {source: pgtype.Int4{Int: 0, Status: pgtype.Null}, result: "null"}, - {source: pgtype.Int4{Int: 1, Status: pgtype.Present}, result: "1"}, + {source: pgtype.Int4{Int: 0}, result: "null"}, + {source: pgtype.Int4{Int: 1, Valid: true}, result: "1"}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -169,8 +169,8 @@ func TestInt4UnmarshalJSON(t *testing.T) { source string result pgtype.Int4 }{ - {source: "null", result: pgtype.Int4{Int: 0, Status: pgtype.Null}}, - {source: "1", result: pgtype.Int4{Int: 1, Status: pgtype.Present}}, + {source: "null", result: pgtype.Int4{Int: 0}}, + {source: "1", result: pgtype.Int4{Int: 1, Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Int4 diff --git a/int4range.go b/int4range.go index c7f51fa6..49503c0d 100644 --- a/int4range.go +++ b/int4range.go @@ -12,13 +12,13 @@ type Int4range struct { Upper Int4 LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Int4range) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Int4range{Status: Null} + *dst = Int4range{} return nil } @@ -36,15 +36,11 @@ func (dst *Int4range) Set(src interface{}) error { return nil } -func (dst Int4range) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Int4range) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Int4range) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Int4range) AssignTo(dst interface{}) error { func (dst *Int4range) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4range{Status: Null} + *dst = Int4range{} return nil } @@ -62,7 +58,7 @@ func (dst *Int4range) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Int4range{Status: Present} + *dst = Int4range{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Int4range) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Int4range) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int4range{Status: Null} + *dst = Int4range{} return nil } @@ -97,7 +93,7 @@ func (dst *Int4range) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Int4range{Status: Present} + *dst = Int4range{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Int4range) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Int4range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Int4range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Int4range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Int4range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Int4range) Scan(src interface{}) error { if src == nil { - *dst = Int4range{Status: Null} + *dst = Int4range{} return nil } diff --git a/int4range_test.go b/int4range_test.go index 43626189..8b990036 100644 --- a/int4range_test.go +++ b/int4range_test.go @@ -9,12 +9,12 @@ import ( func TestInt4rangeTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "int4range", []interface{}{ - &pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int4{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present}, - &pgtype.Int4range{Upper: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int4range{Status: pgtype.Null}, + &pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, + &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Valid: true}, Upper: pgtype.Int4{Int: 10, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int4range{Lower: pgtype.Int4{Int: -42, Valid: true}, Upper: pgtype.Int4{Int: -5, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Valid: true}, + &pgtype.Int4range{Upper: pgtype.Int4{Int: 1, Valid: true}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int4range{}, }) } @@ -22,7 +22,7 @@ func TestInt4rangeNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select int4range(1, 10, '(]')", - Value: pgtype.Int4range{Lower: pgtype.Int4{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, + Value: pgtype.Int4range{Lower: pgtype.Int4{Int: 2, Valid: true}, Upper: pgtype.Int4{Int: 11, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, }, }) } diff --git a/int8.go b/int8.go index 0e089979..794f92c6 100644 --- a/int8.go +++ b/int8.go @@ -12,13 +12,13 @@ import ( ) type Int8 struct { - Int int64 - Status Status + Int int64 + Valid bool } func (dst *Int8) Set(src interface{}) error { if src == nil { - *dst = Int8{Status: Null} + *dst = Int8{} return nil } @@ -31,24 +31,24 @@ func (dst *Int8) Set(src interface{}) error { switch value := src.(type) { case int8: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case uint8: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case int16: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case uint16: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case int32: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case uint32: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case int64: - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case uint64: if value > math.MaxInt64 { return fmt.Errorf("%d is greater than maximum value for Int8", value) } - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case int: if int64(value) < math.MinInt64 { return fmt.Errorf("%d is greater than maximum value for Int8", value) @@ -56,103 +56,103 @@ func (dst *Int8) Set(src interface{}) error { if int64(value) > math.MaxInt64 { return fmt.Errorf("%d is greater than maximum value for Int8", value) } - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case uint: if uint64(value) > math.MaxInt64 { return fmt.Errorf("%d is greater than maximum value for Int8", value) } - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case string: num, err := strconv.ParseInt(value, 10, 64) if err != nil { return err } - *dst = Int8{Int: num, Status: Present} + *dst = Int8{Int: num, Valid: true} case float32: if value > math.MaxInt64 { return fmt.Errorf("%f is greater than maximum value for Int8", value) } - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case float64: if value > math.MaxInt64 { return fmt.Errorf("%f is greater than maximum value for Int8", value) } - *dst = Int8{Int: int64(value), Status: Present} + *dst = Int8{Int: int64(value), Valid: true} case *int8: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } case *float64: if value == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { return dst.Set(*value) } @@ -167,23 +167,19 @@ func (dst *Int8) Set(src interface{}) error { } func (dst Int8) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Int } func (src *Int8) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) + return int64AssignTo(int64(src.Int), src.Valid, dst) } func (dst *Int8) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8{Status: Null} + *dst = Int8{} return nil } @@ -192,13 +188,13 @@ func (dst *Int8) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Int8{Int: n, Status: Present} + *dst = Int8{Int: n, Valid: true} return nil } func (dst *Int8) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8{Status: Null} + *dst = Int8{} return nil } @@ -208,27 +204,21 @@ func (dst *Int8) DecodeBinary(ci *ConnInfo, src []byte) error { n := int64(binary.BigEndian.Uint64(src)) - *dst = Int8{Int: n, Status: Present} + *dst = Int8{Int: n, Valid: true} return nil } func (src Int8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, strconv.FormatInt(src.Int, 10)...), nil } func (src Int8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return pgio.AppendInt64(buf, src.Int), nil @@ -237,13 +227,13 @@ func (src Int8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Int8) Scan(src interface{}) error { if src == nil { - *dst = Int8{Status: Null} + *dst = Int8{} return nil } switch src := src.(type) { case int64: - *dst = Int8{Int: src, Status: Present} + *dst = Int8{Int: src, Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -258,27 +248,17 @@ func (dst *Int8) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Int8) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Int), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return int64(src.Int), nil } func (src Int8) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return []byte(strconv.FormatInt(src.Int, 10)), nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return []byte(strconv.FormatInt(src.Int, 10)), nil } func (dst *Int8) UnmarshalJSON(b []byte) error { @@ -289,9 +269,9 @@ func (dst *Int8) UnmarshalJSON(b []byte) error { } if n == nil { - *dst = Int8{Status: Null} + *dst = Int8{} } else { - *dst = Int8{Int: *n, Status: Present} + *dst = Int8{Int: *n, Valid: true} } return nil diff --git a/int8_array.go b/int8_array.go index e405b326..d6f38994 100644 --- a/int8_array.go +++ b/int8_array.go @@ -14,13 +14,13 @@ import ( type Int8Array struct { Elements []Int8 Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *Int8Array) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} return nil } @@ -36,9 +36,9 @@ func (dst *Int8Array) Set(src interface{}) error { case []int16: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int16: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -68,15 +68,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint16: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -87,15 +87,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint16: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -106,15 +106,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int32: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -125,15 +125,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int32: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -144,15 +144,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint32: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -163,15 +163,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint32: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -182,15 +182,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int64: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -201,15 +201,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int64: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -220,15 +220,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint64: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -239,15 +239,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint64: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -258,15 +258,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -277,15 +277,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -296,15 +296,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -315,15 +315,15 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { elements := make([]Int8, len(value)) for i := range value { @@ -334,20 +334,20 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Int8: if value == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} } else if len(value) == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} } else { *dst = Int8Array{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -356,7 +356,7 @@ func (dst *Int8Array) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} return nil } @@ -365,7 +365,7 @@ func (dst *Int8Array) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for Int8Array", src) } if elementsLength == 0 { - *dst = Int8Array{Status: Present} + *dst = Int8Array{Valid: true} return nil } if len(dimensions) == 0 { @@ -378,7 +378,7 @@ func (dst *Int8Array) Set(src interface{}) error { *dst = Int8Array{ Elements: make([]Int8, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -445,210 +445,203 @@ func (dst *Int8Array) setRecursive(value reflect.Value, index, dimension int) (i } func (dst Int8Array) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Int8Array) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]int16: - *v = make([]int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int16: - *v = make([]*int16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint16: - *v = make([]uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint16: - *v = make([]*uint16, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int32: - *v = make([]int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int32: - *v = make([]*int32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint32: - *v = make([]uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint32: - *v = make([]*uint32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int64: - *v = make([]*int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint64: - *v = make([]*uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int: - *v = make([]int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int: - *v = make([]*int, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint: - *v = make([]uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint: - *v = make([]*uint, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]int16: + *v = make([]int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int16: + *v = make([]*int16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint16: + *v = make([]uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint16: + *v = make([]*uint16, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int32: + *v = make([]int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int32: + *v = make([]*int32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint32: + *v = make([]uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint32: + *v = make([]*uint32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int64: + *v = make([]int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int64: + *v = make([]*int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint64: + *v = make([]uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint64: + *v = make([]*uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int: + *v = make([]int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int: + *v = make([]*int, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint: + *v = make([]uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint: + *v = make([]*uint, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *Int8Array) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -700,7 +693,7 @@ func (src *Int8Array) assignToRecursive(value reflect.Value, index, dimension in func (dst *Int8Array) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} return nil } @@ -729,14 +722,14 @@ func (dst *Int8Array) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Int8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = Int8Array{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *Int8Array) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8Array{Status: Null} + *dst = Int8Array{} return nil } @@ -747,7 +740,7 @@ func (dst *Int8Array) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = Int8Array{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int8Array{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -772,16 +765,13 @@ func (dst *Int8Array) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = Int8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = Int8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src Int8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -834,11 +824,8 @@ func (src Int8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Int8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -852,7 +839,7 @@ func (src Int8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/int8_array_test.go b/int8_array_test.go index 1d42a278..29eaf8cb 100644 --- a/int8_array_test.go +++ b/int8_array_test.go @@ -13,41 +13,41 @@ func TestInt8ArrayTranscode(t *testing.T) { &pgtype.Int8Array{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Int: 1, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Int8Array{Status: pgtype.Null}, + &pgtype.Int8Array{}, &pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 6, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {}, + {Int: 6, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,110 +60,110 @@ func TestInt8ArraySet(t *testing.T) { { source: []int64{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int32{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int16{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []int{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint64{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint32{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint16{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []uint{1}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]int64)(nil)), - result: pgtype.Int8Array{Status: pgtype.Null}, + result: pgtype.Int8Array{}, }, { source: [][]int64{{1}, {2}}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]int64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]int64{{1}, {2}}, result: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]int64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -196,90 +196,90 @@ func TestInt8ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int64Slice, expected: []int64{1}, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint64Slice, expected: []uint64{1}, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedInt64Slice, expected: _int64Slice{1}, }, { - src: pgtype.Int8Array{Status: pgtype.Null}, + src: pgtype.Int8Array{}, dst: &int64Slice, expected: (([]int64)(nil)), }, { - src: pgtype.Int8Array{Status: pgtype.Present}, + src: pgtype.Int8Array{Valid: true}, dst: &int64Slice, expected: []int64{}, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [][]int64{{1}, {2}}, dst: &int64SliceDim2, }, { src: pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [][][][]int64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int64SliceDim4, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1]int64{{1}, {2}}, dst: &int64ArrayDim2, }, { src: pgtype.Int8Array{ Elements: []pgtype.Int8{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Int: 3, Status: pgtype.Present}, - {Int: 4, Status: pgtype.Present}, - {Int: 5, Status: pgtype.Present}, - {Int: 6, Status: pgtype.Present}}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {Int: 3, Valid: true}, + {Int: 4, Valid: true}, + {Int: 5, Valid: true}, + {Int: 6, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, expected: [2][1][1][3]int64{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, dst: &int64ArrayDim4, }, @@ -302,39 +302,39 @@ func TestInt8ArrayAssignTo(t *testing.T) { }{ { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Status: pgtype.Null}}, + Elements: []pgtype.Int8{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &int64Slice, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: -1, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: -1, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &uint64Slice, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int64ArrayDim2, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &int64Slice, }, { src: pgtype.Int8Array{ - Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}, {Int: 2, Status: pgtype.Present}}, + Elements: []pgtype.Int8{{Int: 1, Valid: true}, {Int: 2, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &int64ArrayDim4, }, } diff --git a/int8_test.go b/int8_test.go index 4e28e374..8aca741d 100644 --- a/int8_test.go +++ b/int8_test.go @@ -11,12 +11,12 @@ import ( func TestInt8Transcode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "int8", []interface{}{ - &pgtype.Int8{Int: math.MinInt64, Status: pgtype.Present}, - &pgtype.Int8{Int: -1, Status: pgtype.Present}, - &pgtype.Int8{Int: 0, Status: pgtype.Present}, - &pgtype.Int8{Int: 1, Status: pgtype.Present}, - &pgtype.Int8{Int: math.MaxInt64, Status: pgtype.Present}, - &pgtype.Int8{Int: 0, Status: pgtype.Null}, + &pgtype.Int8{Int: math.MinInt64, Valid: true}, + &pgtype.Int8{Int: -1, Valid: true}, + &pgtype.Int8{Int: 0, Valid: true}, + &pgtype.Int8{Int: 1, Valid: true}, + &pgtype.Int8{Int: math.MaxInt64, Valid: true}, + &pgtype.Int8{Int: 0}, }) } @@ -25,22 +25,22 @@ func TestInt8Set(t *testing.T) { source interface{} result pgtype.Int8 }{ - {source: int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: float32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: float64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, + {source: int8(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: int16(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: int32(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: int64(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: int8(-1), result: pgtype.Int8{Int: -1, Valid: true}}, + {source: int16(-1), result: pgtype.Int8{Int: -1, Valid: true}}, + {source: int32(-1), result: pgtype.Int8{Int: -1, Valid: true}}, + {source: int64(-1), result: pgtype.Int8{Int: -1, Valid: true}}, + {source: uint8(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: uint16(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: uint32(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: uint64(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: float32(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: float64(1), result: pgtype.Int8{Int: 1, Valid: true}}, + {source: "1", result: pgtype.Int8{Int: 1, Valid: true}}, + {source: _int8(1), result: pgtype.Int8{Int: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -76,19 +76,19 @@ func TestInt8AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &i8, expected: int8(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.Int8{Int: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.Int8{Int: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -107,8 +107,8 @@ func TestInt8AssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &pi8, expected: int8(42)}, + {src: pgtype.Int8{Int: 42, Valid: true}, dst: &_pi8, expected: _int8(42)}, } for i, tt := range pointerAllocTests { @@ -126,15 +126,15 @@ func TestInt8AssignTo(t *testing.T) { src pgtype.Int8 dst interface{} }{ - {src: pgtype.Int8{Int: 150, Status: pgtype.Present}, dst: &i8}, - {src: pgtype.Int8{Int: 40000, Status: pgtype.Present}, dst: &i16}, - {src: pgtype.Int8{Int: 5000000000, Status: pgtype.Present}, dst: &i32}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &i64}, + {src: pgtype.Int8{Int: 150, Valid: true}, dst: &i8}, + {src: pgtype.Int8{Int: 40000, Valid: true}, dst: &i16}, + {src: pgtype.Int8{Int: 5000000000, Valid: true}, dst: &i32}, + {src: pgtype.Int8{Int: -1, Valid: true}, dst: &ui8}, + {src: pgtype.Int8{Int: -1, Valid: true}, dst: &ui16}, + {src: pgtype.Int8{Int: -1, Valid: true}, dst: &ui32}, + {src: pgtype.Int8{Int: -1, Valid: true}, dst: &ui64}, + {src: pgtype.Int8{Int: -1, Valid: true}, dst: &ui}, + {src: pgtype.Int8{Int: 0}, dst: &i64}, } for i, tt := range errorTests { @@ -150,8 +150,8 @@ func TestInt8MarshalJSON(t *testing.T) { source pgtype.Int8 result string }{ - {source: pgtype.Int8{Int: 0, Status: pgtype.Null}, result: "null"}, - {source: pgtype.Int8{Int: 1, Status: pgtype.Present}, result: "1"}, + {source: pgtype.Int8{Int: 0}, result: "null"}, + {source: pgtype.Int8{Int: 1, Valid: true}, result: "1"}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -170,8 +170,8 @@ func TestInt8UnmarshalJSON(t *testing.T) { source string result pgtype.Int8 }{ - {source: "null", result: pgtype.Int8{Int: 0, Status: pgtype.Null}}, - {source: "1", result: pgtype.Int8{Int: 1, Status: pgtype.Present}}, + {source: "null", result: pgtype.Int8{Int: 0}}, + {source: "1", result: pgtype.Int8{Int: 1, Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Int8 diff --git a/int8range.go b/int8range.go index 71369373..a7cbcd12 100644 --- a/int8range.go +++ b/int8range.go @@ -12,13 +12,13 @@ type Int8range struct { Upper Int8 LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Int8range) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Int8range{Status: Null} + *dst = Int8range{} return nil } @@ -36,15 +36,11 @@ func (dst *Int8range) Set(src interface{}) error { return nil } -func (dst Int8range) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Int8range) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Int8range) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Int8range) AssignTo(dst interface{}) error { func (dst *Int8range) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8range{Status: Null} + *dst = Int8range{} return nil } @@ -62,7 +58,7 @@ func (dst *Int8range) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Int8range{Status: Present} + *dst = Int8range{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Int8range) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Int8range) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Int8range{Status: Null} + *dst = Int8range{} return nil } @@ -97,7 +93,7 @@ func (dst *Int8range) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Int8range{Status: Present} + *dst = Int8range{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Int8range) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Int8range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Int8range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Int8range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Int8range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Int8range) Scan(src interface{}) error { if src == nil { - *dst = Int8range{Status: Null} + *dst = Int8range{} return nil } diff --git a/int8range_test.go b/int8range_test.go index 99d4e8a3..f2e4098d 100644 --- a/int8range_test.go +++ b/int8range_test.go @@ -9,12 +9,12 @@ import ( func TestInt8rangeTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "Int8range", []interface{}{ - &pgtype.Int8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int8{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present}, - &pgtype.Int8range{Upper: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present}, - &pgtype.Int8range{Status: pgtype.Null}, + &pgtype.Int8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, + &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Valid: true}, Upper: pgtype.Int8{Int: 10, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int8range{Lower: pgtype.Int8{Int: -42, Valid: true}, Upper: pgtype.Int8{Int: -5, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Valid: true}, + &pgtype.Int8range{Upper: pgtype.Int8{Int: 1, Valid: true}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Valid: true}, + &pgtype.Int8range{}, }) } @@ -22,7 +22,7 @@ func TestInt8rangeNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select Int8range(1, 10, '(]')", - Value: pgtype.Int8range{Lower: pgtype.Int8{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present}, + Value: pgtype.Int8range{Lower: pgtype.Int8{Int: 2, Valid: true}, Upper: pgtype.Int8{Int: 11, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Valid: true}, }, }) } diff --git a/interval.go b/interval.go index b01fbb7c..a92cd41f 100644 --- a/interval.go +++ b/interval.go @@ -23,12 +23,12 @@ type Interval struct { Microseconds int64 Days int32 Months int32 - Status Status + Valid bool } func (dst *Interval) Set(src interface{}) error { if src == nil { - *dst = Interval{Status: Null} + *dst = Interval{} return nil } @@ -41,7 +41,7 @@ func (dst *Interval) Set(src interface{}) error { switch value := src.(type) { case time.Duration: - *dst = Interval{Microseconds: int64(value) / 1000, Status: Present} + *dst = Interval{Microseconds: int64(value) / 1000, Valid: true} default: if originalSrc, ok := underlyingPtrType(src); ok { return dst.Set(originalSrc) @@ -53,40 +53,33 @@ func (dst *Interval) Set(src interface{}) error { } func (dst Interval) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Interval) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Duration: - us := int64(src.Months)*microsecondsPerMonth + int64(src.Days)*microsecondsPerDay + src.Microseconds - *v = time.Duration(us) * time.Microsecond - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *time.Duration: + us := int64(src.Months)*microsecondsPerMonth + int64(src.Days)*microsecondsPerDay + src.Microseconds + *v = time.Duration(us) * time.Microsecond + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Interval) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Interval{Status: Null} + *dst = Interval{} return nil } @@ -163,13 +156,13 @@ func (dst *Interval) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Interval{Months: months, Days: days, Microseconds: microseconds, Status: Present} + *dst = Interval{Months: months, Days: days, Microseconds: microseconds, Valid: true} return nil } func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Interval{Status: Null} + *dst = Interval{} return nil } @@ -181,16 +174,13 @@ func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error { days := int32(binary.BigEndian.Uint32(src[8:])) months := int32(binary.BigEndian.Uint32(src[12:])) - *dst = Interval{Microseconds: microseconds, Days: days, Months: months, Status: Present} + *dst = Interval{Microseconds: microseconds, Days: days, Months: months, Valid: true} return nil } func (src Interval) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.Months != 0 { @@ -220,11 +210,8 @@ func (src Interval) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // EncodeBinary encodes src into w. func (src Interval) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendInt64(buf, src.Microseconds) @@ -235,7 +222,7 @@ func (src Interval) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Interval) Scan(src interface{}) error { if src == nil { - *dst = Interval{Status: Null} + *dst = Interval{} return nil } diff --git a/interval_test.go b/interval_test.go index 1ee094d7..844f3866 100644 --- a/interval_test.go +++ b/interval_test.go @@ -12,23 +12,23 @@ import ( func TestIntervalTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "interval", []interface{}{ - &pgtype.Interval{Microseconds: 1, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present}, - &pgtype.Interval{Days: 1, Status: pgtype.Present}, - &pgtype.Interval{Months: 1, Status: pgtype.Present}, - &pgtype.Interval{Months: 12, Status: pgtype.Present}, - &pgtype.Interval{Months: 13, Days: 15, Microseconds: 1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1000000, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -1000001, Status: pgtype.Present}, - &pgtype.Interval{Microseconds: -123202800000000, Status: pgtype.Present}, - &pgtype.Interval{Days: -1, Status: pgtype.Present}, - &pgtype.Interval{Months: -1, Status: pgtype.Present}, - &pgtype.Interval{Months: -12, Status: pgtype.Present}, - &pgtype.Interval{Months: -13, Days: -15, Microseconds: -1000001, Status: pgtype.Present}, - &pgtype.Interval{Status: pgtype.Null}, + &pgtype.Interval{Microseconds: 1, Valid: true}, + &pgtype.Interval{Microseconds: 1000000, Valid: true}, + &pgtype.Interval{Microseconds: 1000001, Valid: true}, + &pgtype.Interval{Microseconds: 123202800000000, Valid: true}, + &pgtype.Interval{Days: 1, Valid: true}, + &pgtype.Interval{Months: 1, Valid: true}, + &pgtype.Interval{Months: 12, Valid: true}, + &pgtype.Interval{Months: 13, Days: 15, Microseconds: 1000001, Valid: true}, + &pgtype.Interval{Microseconds: -1, Valid: true}, + &pgtype.Interval{Microseconds: -1000000, Valid: true}, + &pgtype.Interval{Microseconds: -1000001, Valid: true}, + &pgtype.Interval{Microseconds: -123202800000000, Valid: true}, + &pgtype.Interval{Days: -1, Valid: true}, + &pgtype.Interval{Months: -1, Valid: true}, + &pgtype.Interval{Months: -12, Valid: true}, + &pgtype.Interval{Months: -13, Days: -15, Microseconds: -1000001, Valid: true}, + &pgtype.Interval{}, }) } @@ -36,37 +36,37 @@ func TestIntervalNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select '1 second'::interval", - Value: &pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present}, + Value: &pgtype.Interval{Microseconds: 1000000, Valid: true}, }, { SQL: "select '1.000001 second'::interval", - Value: &pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present}, + Value: &pgtype.Interval{Microseconds: 1000001, Valid: true}, }, { SQL: "select '34223 hours'::interval", - Value: &pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present}, + Value: &pgtype.Interval{Microseconds: 123202800000000, Valid: true}, }, { SQL: "select '1 day'::interval", - Value: &pgtype.Interval{Days: 1, Status: pgtype.Present}, + Value: &pgtype.Interval{Days: 1, Valid: true}, }, { SQL: "select '1 month'::interval", - Value: &pgtype.Interval{Months: 1, Status: pgtype.Present}, + Value: &pgtype.Interval{Months: 1, Valid: true}, }, { SQL: "select '1 year'::interval", - Value: &pgtype.Interval{Months: 12, Status: pgtype.Present}, + Value: &pgtype.Interval{Months: 12, Valid: true}, }, { SQL: "select '-13 mon'::interval", - Value: &pgtype.Interval{Months: -13, Status: pgtype.Present}, + Value: &pgtype.Interval{Months: -13, Valid: true}, }, }) } func TestIntervalLossyConversionToDuration(t *testing.T) { - interval := &pgtype.Interval{Months: 1, Days: 1, Status: pgtype.Present} + interval := &pgtype.Interval{Months: 1, Days: 1, Valid: true} var d time.Duration err := interval.AssignTo(&d) require.NoError(t, err) diff --git a/json.go b/json.go index 32bef5e7..580e8505 100644 --- a/json.go +++ b/json.go @@ -8,13 +8,13 @@ import ( ) type JSON struct { - Bytes []byte - Status Status + Bytes []byte + Valid bool } func (dst *JSON) Set(src interface{}) error { if src == nil { - *dst = JSON{Status: Null} + *dst = JSON{} return nil } @@ -27,18 +27,18 @@ func (dst *JSON) Set(src interface{}) error { switch value := src.(type) { case string: - *dst = JSON{Bytes: []byte(value), Status: Present} + *dst = JSON{Bytes: []byte(value), Valid: true} case *string: if value == nil { - *dst = JSON{Status: Null} + *dst = JSON{} } else { - *dst = JSON{Bytes: []byte(*value), Status: Present} + *dst = JSON{Bytes: []byte(*value), Valid: true} } case []byte: if value == nil { - *dst = JSON{Status: Null} + *dst = JSON{} } else { - *dst = JSON{Bytes: value, Status: Present} + *dst = JSON{Bytes: value, Valid: true} } // Encode* methods are defined on *JSON. If JSON is passed directly then the // struct itself would be encoded instead of Bytes. This is clearly a footgun @@ -54,38 +54,35 @@ func (dst *JSON) Set(src interface{}) error { if err != nil { return err } - *dst = JSON{Bytes: buf, Status: Present} + *dst = JSON{Bytes: buf, Valid: true} } return nil } func (dst JSON) Get() interface{} { - switch dst.Status { - case Present: - var i interface{} - err := json.Unmarshal(dst.Bytes, &i) - if err != nil { - return dst - } - return i - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + + var i interface{} + err := json.Unmarshal(dst.Bytes, &i) + if err != nil { + return dst + } + return i } func (src *JSON) AssignTo(dst interface{}) error { switch v := dst.(type) { case *string: - if src.Status == Present { + if src.Valid { *v = string(src.Bytes) } else { - return fmt.Errorf("cannot assign non-present status to %T", dst) + return fmt.Errorf("cannot assign non-valid to %T", dst) } case **string: - if src.Status == Present { + if src.Valid { s := string(src.Bytes) *v = &s return nil @@ -94,7 +91,7 @@ func (src *JSON) AssignTo(dst interface{}) error { return nil } case *[]byte: - if src.Status != Present { + if !src.Valid { *v = nil } else { buf := make([]byte, len(src.Bytes)) @@ -103,7 +100,7 @@ func (src *JSON) AssignTo(dst interface{}) error { } default: data := src.Bytes - if data == nil || src.Status != Present { + if data == nil || !src.Valid { data = []byte("null") } @@ -119,11 +116,11 @@ func (JSON) PreferredResultFormat() int16 { func (dst *JSON) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = JSON{Status: Null} + *dst = JSON{} return nil } - *dst = JSON{Bytes: src, Status: Present} + *dst = JSON{Bytes: src, Valid: true} return nil } @@ -136,11 +133,8 @@ func (JSON) PreferredParamFormat() int16 { } func (src JSON) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.Bytes...), nil @@ -153,7 +147,7 @@ func (src JSON) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *JSON) Scan(src interface{}) error { if src == nil { - *dst = JSON{Status: Null} + *dst = JSON{} return nil } @@ -171,34 +165,24 @@ func (dst *JSON) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src JSON) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.Bytes, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return src.Bytes, nil } func (src JSON) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return src.Bytes, nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - - return nil, errBadStatus + return src.Bytes, nil } func (dst *JSON) UnmarshalJSON(b []byte) error { if b == nil || string(b) == "null" { - *dst = JSON{Status: Null} + *dst = JSON{} } else { - *dst = JSON{Bytes: b, Status: Present} + *dst = JSON{Bytes: b, Valid: true} } return nil diff --git a/json_test.go b/json_test.go index bbd3959e..c56f403f 100644 --- a/json_test.go +++ b/json_test.go @@ -11,11 +11,11 @@ import ( func TestJSONTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "json", []interface{}{ - &pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte("null"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte("42"), Status: pgtype.Present}, - &pgtype.JSON{Bytes: []byte(`"hello"`), Status: pgtype.Present}, - &pgtype.JSON{Status: pgtype.Null}, + &pgtype.JSON{Bytes: []byte("{}"), Valid: true}, + &pgtype.JSON{Bytes: []byte("null"), Valid: true}, + &pgtype.JSON{Bytes: []byte("42"), Valid: true}, + &pgtype.JSON{Bytes: []byte(`"hello"`), Valid: true}, + &pgtype.JSON{}, }) } @@ -24,12 +24,12 @@ func TestJSONSet(t *testing.T) { source interface{} result pgtype.JSON }{ - {source: "{}", result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: []byte("{}"), result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: ([]byte)(nil), result: pgtype.JSON{Status: pgtype.Null}}, - {source: (*string)(nil), result: pgtype.JSON{Status: pgtype.Null}}, - {source: []int{1, 2, 3}, result: pgtype.JSON{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}}, - {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}}, + {source: "{}", result: pgtype.JSON{Bytes: []byte("{}"), Valid: true}}, + {source: []byte("{}"), result: pgtype.JSON{Bytes: []byte("{}"), Valid: true}}, + {source: ([]byte)(nil), result: pgtype.JSON{}}, + {source: (*string)(nil), result: pgtype.JSON{}}, + {source: []int{1, 2, 3}, result: pgtype.JSON{Bytes: []byte("[1,2,3]"), Valid: true}}, + {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Valid: true}}, } for i, tt := range successfulTests { @@ -55,7 +55,7 @@ func TestJSONAssignTo(t *testing.T) { dst *string expected string }{ - {src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"}, + {src: pgtype.JSON{Bytes: []byte("{}"), Valid: true}, dst: &s, expected: "{}"}, } for i, tt := range rawStringTests { @@ -74,8 +74,8 @@ func TestJSONAssignTo(t *testing.T) { dst *[]byte expected []byte }{ - {src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")}, - {src: pgtype.JSON{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))}, + {src: pgtype.JSON{Bytes: []byte("{}"), Valid: true}, dst: &b, expected: []byte("{}")}, + {src: pgtype.JSON{}, dst: &b, expected: (([]byte)(nil))}, } for i, tt := range rawBytesTests { @@ -101,8 +101,8 @@ func TestJSONAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, - {src: pgtype.JSON{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, + {src: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Valid: true}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, + {src: pgtype.JSON{Bytes: []byte(`{"name":"John","age":42}`), Valid: true}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, } for i, tt := range unmarshalTests { err := tt.src.AssignTo(tt.dst) @@ -120,7 +120,7 @@ func TestJSONAssignTo(t *testing.T) { dst **string expected *string }{ - {src: pgtype.JSON{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, + {src: pgtype.JSON{}, dst: &ps, expected: ((*string)(nil))}, } for i, tt := range pointerAllocTests { @@ -140,8 +140,8 @@ func TestJSONMarshalJSON(t *testing.T) { source pgtype.JSON result string }{ - {source: pgtype.JSON{Status: pgtype.Null}, result: "null"}, - {source: pgtype.JSON{Bytes: []byte("{\"a\": 1}"), Status: pgtype.Present}, result: "{\"a\": 1}"}, + {source: pgtype.JSON{}, result: "null"}, + {source: pgtype.JSON{Bytes: []byte("{\"a\": 1}"), Valid: true}, result: "{\"a\": 1}"}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -160,8 +160,8 @@ func TestJSONUnmarshalJSON(t *testing.T) { source string result pgtype.JSON }{ - {source: "null", result: pgtype.JSON{Status: pgtype.Null}}, - {source: "{\"a\": 1}", result: pgtype.JSON{Bytes: []byte("{\"a\": 1}"), Status: pgtype.Present}}, + {source: "null", result: pgtype.JSON{}}, + {source: "{\"a\": 1}", result: pgtype.JSON{Bytes: []byte("{\"a\": 1}"), Valid: true}}, } for i, tt := range successfulTests { var r pgtype.JSON @@ -170,7 +170,7 @@ func TestJSONUnmarshalJSON(t *testing.T) { t.Errorf("%d: %v", i, err) } - if string(r.Bytes) != string(tt.result.Bytes) || r.Status != tt.result.Status { + if string(r.Bytes) != string(tt.result.Bytes) || r.Valid != tt.result.Valid { t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) } } diff --git a/jsonb.go b/jsonb.go index c9dafc93..38d56499 100644 --- a/jsonb.go +++ b/jsonb.go @@ -29,7 +29,7 @@ func (dst *JSONB) DecodeText(ci *ConnInfo, src []byte) error { func (dst *JSONB) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = JSONB{Status: Null} + *dst = JSONB{} return nil } @@ -41,7 +41,7 @@ func (dst *JSONB) DecodeBinary(ci *ConnInfo, src []byte) error { return fmt.Errorf("unknown jsonb version number %d", src[0]) } - *dst = JSONB{Bytes: src[1:], Status: Present} + *dst = JSONB{Bytes: src[1:], Valid: true} return nil } @@ -55,11 +55,8 @@ func (src JSONB) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src JSONB) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, 1) diff --git a/jsonb_array.go b/jsonb_array.go index c4b7cd3d..81ed9f29 100644 --- a/jsonb_array.go +++ b/jsonb_array.go @@ -14,13 +14,13 @@ import ( type JSONBArray struct { Elements []JSONB Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *JSONBArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} return nil } @@ -36,9 +36,9 @@ func (dst *JSONBArray) Set(src interface{}) error { case []string: if value == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} } else if len(value) == 0 { - *dst = JSONBArray{Status: Present} + *dst = JSONBArray{Valid: true} } else { elements := make([]JSONB, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *JSONBArray) Set(src interface{}) error { *dst = JSONBArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case [][]byte: if value == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} } else if len(value) == 0 { - *dst = JSONBArray{Status: Present} + *dst = JSONBArray{Valid: true} } else { elements := make([]JSONB, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *JSONBArray) Set(src interface{}) error { *dst = JSONBArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []JSONB: if value == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} } else if len(value) == 0 { - *dst = JSONBArray{Status: Present} + *dst = JSONBArray{Valid: true} } else { *dst = JSONBArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *JSONBArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} return nil } @@ -99,7 +99,7 @@ func (dst *JSONBArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for JSONBArray", src) } if elementsLength == 0 { - *dst = JSONBArray{Status: Present} + *dst = JSONBArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *JSONBArray) Set(src interface{}) error { *dst = JSONBArray{ Elements: make([]JSONB, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *JSONBArray) setRecursive(value reflect.Value, index, dimension int) ( } func (dst JSONBArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *JSONBArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[][]byte: - *v = make([][]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[][]byte: + *v = make([][]byte, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *JSONBArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *JSONBArray) assignToRecursive(value reflect.Value, index, dimension i func (dst *JSONBArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} return nil } @@ -337,14 +330,14 @@ func (dst *JSONBArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = JSONBArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = JSONBArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *JSONBArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = JSONBArray{Status: Null} + *dst = JSONBArray{} return nil } @@ -355,7 +348,7 @@ func (dst *JSONBArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = JSONBArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = JSONBArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *JSONBArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = JSONBArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = JSONBArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src JSONBArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src JSONBArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src JSONBArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src JSONBArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/jsonb_array_test.go b/jsonb_array_test.go index 65f1777a..4f293e9e 100644 --- a/jsonb_array_test.go +++ b/jsonb_array_test.go @@ -12,25 +12,25 @@ func TestJSONBArrayTranscode(t *testing.T) { &pgtype.JSONBArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.JSONBArray{ Elements: []pgtype.JSONB{ - {Bytes: []byte(`"foo"`), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Bytes: []byte(`"foo"`), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.JSONBArray{Status: pgtype.Null}, + &pgtype.JSONBArray{}, &pgtype.JSONBArray{ Elements: []pgtype.JSONB{ - {Bytes: []byte(`"foo"`), Status: pgtype.Present}, - {Bytes: []byte("null"), Status: pgtype.Present}, - {Bytes: []byte("42"), Status: pgtype.Present}, + {Bytes: []byte(`"foo"`), Valid: true}, + {Bytes: []byte("null"), Valid: true}, + {Bytes: []byte("42"), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, }) } diff --git a/jsonb_test.go b/jsonb_test.go index 9ce80d42..41df18fa 100644 --- a/jsonb_test.go +++ b/jsonb_test.go @@ -17,11 +17,11 @@ func TestJSONBTranscode(t *testing.T) { } testutil.TestSuccessfulTranscode(t, "jsonb", []interface{}{ - &pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte("null"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte("42"), Status: pgtype.Present}, - &pgtype.JSONB{Bytes: []byte(`"hello"`), Status: pgtype.Present}, - &pgtype.JSONB{Status: pgtype.Null}, + &pgtype.JSONB{Bytes: []byte("{}"), Valid: true}, + &pgtype.JSONB{Bytes: []byte("null"), Valid: true}, + &pgtype.JSONB{Bytes: []byte("42"), Valid: true}, + &pgtype.JSONB{Bytes: []byte(`"hello"`), Valid: true}, + &pgtype.JSONB{}, }) } @@ -30,12 +30,12 @@ func TestJSONBSet(t *testing.T) { source interface{} result pgtype.JSONB }{ - {source: "{}", result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: []byte("{}"), result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}}, - {source: ([]byte)(nil), result: pgtype.JSONB{Status: pgtype.Null}}, - {source: (*string)(nil), result: pgtype.JSONB{Status: pgtype.Null}}, - {source: []int{1, 2, 3}, result: pgtype.JSONB{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}}, - {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}}, + {source: "{}", result: pgtype.JSONB{Bytes: []byte("{}"), Valid: true}}, + {source: []byte("{}"), result: pgtype.JSONB{Bytes: []byte("{}"), Valid: true}}, + {source: ([]byte)(nil), result: pgtype.JSONB{}}, + {source: (*string)(nil), result: pgtype.JSONB{}}, + {source: []int{1, 2, 3}, result: pgtype.JSONB{Bytes: []byte("[1,2,3]"), Valid: true}}, + {source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Valid: true}}, } for i, tt := range successfulTests { @@ -61,7 +61,7 @@ func TestJSONBAssignTo(t *testing.T) { dst *string expected string }{ - {src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"}, + {src: pgtype.JSONB{Bytes: []byte("{}"), Valid: true}, dst: &s, expected: "{}"}, } for i, tt := range rawStringTests { @@ -80,8 +80,8 @@ func TestJSONBAssignTo(t *testing.T) { dst *[]byte expected []byte }{ - {src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")}, - {src: pgtype.JSONB{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))}, + {src: pgtype.JSONB{Bytes: []byte("{}"), Valid: true}, dst: &b, expected: []byte("{}")}, + {src: pgtype.JSONB{}, dst: &b, expected: (([]byte)(nil))}, } for i, tt := range rawBytesTests { @@ -107,8 +107,8 @@ func TestJSONBAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, - {src: pgtype.JSONB{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, + {src: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Valid: true}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}}, + {src: pgtype.JSONB{Bytes: []byte(`{"name":"John","age":42}`), Valid: true}, dst: &strDst, expected: structDst{Name: "John", Age: 42}}, } for i, tt := range unmarshalTests { err := tt.src.AssignTo(tt.dst) @@ -126,7 +126,7 @@ func TestJSONBAssignTo(t *testing.T) { dst **string expected *string }{ - {src: pgtype.JSONB{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, + {src: pgtype.JSONB{}, dst: &ps, expected: ((*string)(nil))}, } for i, tt := range pointerAllocTests { diff --git a/line.go b/line.go index 3564b174..c3192b2a 100644 --- a/line.go +++ b/line.go @@ -13,7 +13,7 @@ import ( type Line struct { A, B, C float64 - Status Status + Valid bool } func (dst *Line) Set(src interface{}) error { @@ -21,14 +21,10 @@ func (dst *Line) Set(src interface{}) error { } func (dst Line) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Line) AssignTo(dst interface{}) error { @@ -37,7 +33,7 @@ func (src *Line) AssignTo(dst interface{}) error { func (dst *Line) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Line{Status: Null} + *dst = Line{} return nil } @@ -65,13 +61,13 @@ func (dst *Line) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Line{A: a, B: b, C: c, Status: Present} + *dst = Line{A: a, B: b, C: c, Valid: true} return nil } func (dst *Line) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Line{Status: Null} + *dst = Line{} return nil } @@ -84,20 +80,17 @@ func (dst *Line) DecodeBinary(ci *ConnInfo, src []byte) error { c := binary.BigEndian.Uint64(src[16:]) *dst = Line{ - A: math.Float64frombits(a), - B: math.Float64frombits(b), - C: math.Float64frombits(c), - Status: Present, + A: math.Float64frombits(a), + B: math.Float64frombits(b), + C: math.Float64frombits(c), + Valid: true, } return nil } func (src Line) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, fmt.Sprintf(`{%s,%s,%s}`, @@ -110,11 +103,8 @@ func (src Line) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Line) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.A)) @@ -126,7 +116,7 @@ func (src Line) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Line) Scan(src interface{}) error { if src == nil { - *dst = Line{Status: Null} + *dst = Line{} return nil } diff --git a/line_test.go b/line_test.go index f697ac43..c47f6512 100644 --- a/line_test.go +++ b/line_test.go @@ -27,12 +27,12 @@ func TestLineTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "line", []interface{}{ &pgtype.Line{ A: 1.23, B: 4.56, C: 7.89012345, - Status: pgtype.Present, + Valid: true, }, &pgtype.Line{ A: -1.23, B: -4.56, C: -7.89, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Line{Status: pgtype.Null}, + &pgtype.Line{}, }) } diff --git a/lseg.go b/lseg.go index 5c4babb6..649863ca 100644 --- a/lseg.go +++ b/lseg.go @@ -12,8 +12,8 @@ import ( ) type Lseg struct { - P [2]Vec2 - Status Status + P [2]Vec2 + Valid bool } func (dst *Lseg) Set(src interface{}) error { @@ -21,14 +21,10 @@ func (dst *Lseg) Set(src interface{}) error { } func (dst Lseg) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Lseg) AssignTo(dst interface{}) error { @@ -37,7 +33,7 @@ func (src *Lseg) AssignTo(dst interface{}) error { func (dst *Lseg) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Lseg{Status: Null} + *dst = Lseg{} return nil } @@ -78,13 +74,13 @@ func (dst *Lseg) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Lseg{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present} + *dst = Lseg{P: [2]Vec2{{x1, y1}, {x2, y2}}, Valid: true} return nil } func (dst *Lseg) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Lseg{Status: Null} + *dst = Lseg{} return nil } @@ -102,17 +98,14 @@ func (dst *Lseg) DecodeBinary(ci *ConnInfo, src []byte) error { {math.Float64frombits(x1), math.Float64frombits(y1)}, {math.Float64frombits(x2), math.Float64frombits(y2)}, }, - Status: Present, + Valid: true, } return nil } func (src Lseg) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`, @@ -126,11 +119,8 @@ func (src Lseg) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Lseg) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X)) @@ -143,7 +133,7 @@ func (src Lseg) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Lseg) Scan(src interface{}) error { if src == nil { - *dst = Lseg{Status: Null} + *dst = Lseg{} return nil } diff --git a/lseg_test.go b/lseg_test.go index b75297cc..af2faf3f 100644 --- a/lseg_test.go +++ b/lseg_test.go @@ -10,13 +10,13 @@ import ( func TestLsegTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "lseg", []interface{}{ &pgtype.Lseg{ - P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}}, - Status: pgtype.Present, + P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}}, + Valid: true, }, &pgtype.Lseg{ - P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, - Status: pgtype.Present, + P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, + Valid: true, }, - &pgtype.Lseg{Status: pgtype.Null}, + &pgtype.Lseg{}, }) } diff --git a/macaddr.go b/macaddr.go index 1d3cfe7b..8d6ab720 100644 --- a/macaddr.go +++ b/macaddr.go @@ -7,13 +7,13 @@ import ( ) type Macaddr struct { - Addr net.HardwareAddr - Status Status + Addr net.HardwareAddr + Valid bool } func (dst *Macaddr) Set(src interface{}) error { if src == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} return nil } @@ -28,22 +28,22 @@ func (dst *Macaddr) Set(src interface{}) error { case net.HardwareAddr: addr := make(net.HardwareAddr, len(value)) copy(addr, value) - *dst = Macaddr{Addr: addr, Status: Present} + *dst = Macaddr{Addr: addr, Valid: true} case string: addr, err := net.ParseMAC(value) if err != nil { return err } - *dst = Macaddr{Addr: addr, Status: Present} + *dst = Macaddr{Addr: addr, Valid: true} case *net.HardwareAddr: if value == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} } else { return dst.Set(*value) } @@ -58,43 +58,36 @@ func (dst *Macaddr) Set(src interface{}) error { } func (dst Macaddr) Get() interface{} { - switch dst.Status { - case Present: - return dst.Addr - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Addr } func (src *Macaddr) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *net.HardwareAddr: - *v = make(net.HardwareAddr, len(src.Addr)) - copy(*v, src.Addr) - return nil - case *string: - *v = src.Addr.String() - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *net.HardwareAddr: + *v = make(net.HardwareAddr, len(src.Addr)) + copy(*v, src.Addr) + return nil + case *string: + *v = src.Addr.String() + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Macaddr) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} return nil } @@ -103,13 +96,13 @@ func (dst *Macaddr) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Macaddr{Addr: addr, Status: Present} + *dst = Macaddr{Addr: addr, Valid: true} return nil } func (dst *Macaddr) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} return nil } @@ -120,17 +113,14 @@ func (dst *Macaddr) DecodeBinary(ci *ConnInfo, src []byte) error { addr := make(net.HardwareAddr, 6) copy(addr, src) - *dst = Macaddr{Addr: addr, Status: Present} + *dst = Macaddr{Addr: addr, Valid: true} return nil } func (src Macaddr) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.Addr.String()...), nil @@ -138,11 +128,8 @@ func (src Macaddr) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // EncodeBinary encodes src into w. func (src Macaddr) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.Addr...), nil @@ -151,7 +138,7 @@ func (src Macaddr) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Macaddr) Scan(src interface{}) error { if src == nil { - *dst = Macaddr{Status: Null} + *dst = Macaddr{} return nil } diff --git a/macaddr_array.go b/macaddr_array.go index bdb1f203..78a93a2d 100644 --- a/macaddr_array.go +++ b/macaddr_array.go @@ -15,13 +15,13 @@ import ( type MacaddrArray struct { Elements []Macaddr Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *MacaddrArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} return nil } @@ -37,9 +37,9 @@ func (dst *MacaddrArray) Set(src interface{}) error { case []net.HardwareAddr: if value == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} } else if len(value) == 0 { - *dst = MacaddrArray{Status: Present} + *dst = MacaddrArray{Valid: true} } else { elements := make([]Macaddr, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *MacaddrArray) Set(src interface{}) error { *dst = MacaddrArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*net.HardwareAddr: if value == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} } else if len(value) == 0 { - *dst = MacaddrArray{Status: Present} + *dst = MacaddrArray{Valid: true} } else { elements := make([]Macaddr, len(value)) for i := range value { @@ -69,20 +69,20 @@ func (dst *MacaddrArray) Set(src interface{}) error { *dst = MacaddrArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Macaddr: if value == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} } else if len(value) == 0 { - *dst = MacaddrArray{Status: Present} + *dst = MacaddrArray{Valid: true} } else { *dst = MacaddrArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -91,7 +91,7 @@ func (dst *MacaddrArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} return nil } @@ -100,7 +100,7 @@ func (dst *MacaddrArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for MacaddrArray", src) } if elementsLength == 0 { - *dst = MacaddrArray{Status: Present} + *dst = MacaddrArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -113,7 +113,7 @@ func (dst *MacaddrArray) Set(src interface{}) error { *dst = MacaddrArray{ Elements: make([]Macaddr, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -180,84 +180,77 @@ func (dst *MacaddrArray) setRecursive(value reflect.Value, index, dimension int) } func (dst MacaddrArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *MacaddrArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]net.HardwareAddr: - *v = make([]net.HardwareAddr, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*net.HardwareAddr: - *v = make([]*net.HardwareAddr, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]net.HardwareAddr: + *v = make([]net.HardwareAddr, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*net.HardwareAddr: + *v = make([]*net.HardwareAddr, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *MacaddrArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -309,7 +302,7 @@ func (src *MacaddrArray) assignToRecursive(value reflect.Value, index, dimension func (dst *MacaddrArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} return nil } @@ -338,14 +331,14 @@ func (dst *MacaddrArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = MacaddrArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = MacaddrArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *MacaddrArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = MacaddrArray{Status: Null} + *dst = MacaddrArray{} return nil } @@ -356,7 +349,7 @@ func (dst *MacaddrArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = MacaddrArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = MacaddrArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -381,16 +374,13 @@ func (dst *MacaddrArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = MacaddrArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = MacaddrArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src MacaddrArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -443,11 +433,8 @@ func (src MacaddrArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src MacaddrArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -461,7 +448,7 @@ func (src MacaddrArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/macaddr_array_test.go b/macaddr_array_test.go index c1a8b72d..a4a55cb0 100644 --- a/macaddr_array_test.go +++ b/macaddr_array_test.go @@ -14,17 +14,17 @@ func TestMacaddrArrayTranscode(t *testing.T) { &pgtype.MacaddrArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.MacaddrArray{Status: pgtype.Null}, + &pgtype.MacaddrArray{}, }) } @@ -36,13 +36,13 @@ func TestMacaddrArraySet(t *testing.T) { { source: []net.HardwareAddr{mustParseMacaddr(t, "01:23:45:67:89:ab")}, result: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}}, + Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]net.HardwareAddr)(nil)), - result: pgtype.MacaddrArray{Status: pgtype.Null}, + result: pgtype.MacaddrArray{}, }, { source: [][]net.HardwareAddr{ @@ -50,10 +50,10 @@ func TestMacaddrArraySet(t *testing.T) { {mustParseMacaddr(t, "cd:ef:01:23:45:67")}}, result: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]net.HardwareAddr{ @@ -67,18 +67,18 @@ func TestMacaddrArraySet(t *testing.T) { mustParseMacaddr(t, "32:10:fe:dc:ba:98")}}}}, result: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}, + {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Valid: true}, + {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Valid: true}, + {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Valid: true}, + {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]net.HardwareAddr{ @@ -86,10 +86,10 @@ func TestMacaddrArraySet(t *testing.T) { {mustParseMacaddr(t, "cd:ef:01:23:45:67")}}, result: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]net.HardwareAddr{ @@ -103,18 +103,18 @@ func TestMacaddrArraySet(t *testing.T) { mustParseMacaddr(t, "32:10:fe:dc:ba:98")}}}}, result: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}, + {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Valid: true}, + {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Valid: true}, + {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Valid: true}, + {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -145,39 +145,39 @@ func TestMacaddrArrayAssignTo(t *testing.T) { }{ { src: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}}, + Elements: []pgtype.Macaddr{{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &macaddrSlice, expected: []net.HardwareAddr{mustParseMacaddr(t, "01:23:45:67:89:ab")}, }, { src: pgtype.MacaddrArray{ - Elements: []pgtype.Macaddr{{Status: pgtype.Null}}, + Elements: []pgtype.Macaddr{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &macaddrSlice, expected: []net.HardwareAddr{nil}, }, { - src: pgtype.MacaddrArray{Status: pgtype.Null}, + src: pgtype.MacaddrArray{}, dst: &macaddrSlice, expected: (([]net.HardwareAddr)(nil)), }, { - src: pgtype.MacaddrArray{Status: pgtype.Present}, + src: pgtype.MacaddrArray{Valid: true}, dst: &macaddrSlice, expected: []net.HardwareAddr{}, }, { src: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &macaddrSliceDim2, expected: [][]net.HardwareAddr{ {mustParseMacaddr(t, "01:23:45:67:89:ab")}, @@ -186,18 +186,18 @@ func TestMacaddrArrayAssignTo(t *testing.T) { { src: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}, + {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Valid: true}, + {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Valid: true}, + {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Valid: true}, + {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &macaddrSliceDim4, expected: [][][][]net.HardwareAddr{ {{{ @@ -212,10 +212,10 @@ func TestMacaddrArrayAssignTo(t *testing.T) { { src: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &macaddrArrayDim2, expected: [2][1]net.HardwareAddr{ {mustParseMacaddr(t, "01:23:45:67:89:ab")}, @@ -224,18 +224,18 @@ func TestMacaddrArrayAssignTo(t *testing.T) { { src: pgtype.MacaddrArray{ Elements: []pgtype.Macaddr{ - {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Status: pgtype.Present}, - {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Status: pgtype.Present}}, + {Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + {Addr: mustParseMacaddr(t, "cd:ef:01:23:45:67"), Valid: true}, + {Addr: mustParseMacaddr(t, "89:ab:cd:ef:01:23"), Valid: true}, + {Addr: mustParseMacaddr(t, "45:67:89:ab:cd:ef"), Valid: true}, + {Addr: mustParseMacaddr(t, "fe:dc:ba:98:76:54"), Valid: true}, + {Addr: mustParseMacaddr(t, "32:10:fe:dc:ba:98"), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &macaddrArrayDim4, expected: [2][1][1][3]net.HardwareAddr{ {{{ diff --git a/macaddr_test.go b/macaddr_test.go index 364a8914..dc475c41 100644 --- a/macaddr_test.go +++ b/macaddr_test.go @@ -12,8 +12,8 @@ import ( func TestMacaddrTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "macaddr", []interface{}{ - &pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, - &pgtype.Macaddr{Status: pgtype.Null}, + &pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, + &pgtype.Macaddr{}, }) } @@ -24,11 +24,11 @@ func TestMacaddrSet(t *testing.T) { }{ { source: mustParseMacaddr(t, "01:23:45:67:89:ab"), - result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, + result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, }, { source: "01:23:45:67:89:ab", - result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present}, + result: pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true}, }, } @@ -47,7 +47,7 @@ func TestMacaddrSet(t *testing.T) { func TestMacaddrAssignTo(t *testing.T) { { - src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present} + src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true} var dst net.HardwareAddr expected := mustParseMacaddr(t, "01:23:45:67:89:ab") @@ -62,7 +62,7 @@ func TestMacaddrAssignTo(t *testing.T) { } { - src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Status: pgtype.Present} + src := pgtype.Macaddr{Addr: mustParseMacaddr(t, "01:23:45:67:89:ab"), Valid: true} var dst string expected := "01:23:45:67:89:ab" diff --git a/name_test.go b/name_test.go index 75329b01..5f429d83 100644 --- a/name_test.go +++ b/name_test.go @@ -10,9 +10,9 @@ import ( func TestNameTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "name", []interface{}{ - &pgtype.Name{String: "", Status: pgtype.Present}, - &pgtype.Name{String: "foo", Status: pgtype.Present}, - &pgtype.Name{Status: pgtype.Null}, + &pgtype.Name{String: "", Valid: true}, + &pgtype.Name{String: "foo", Valid: true}, + &pgtype.Name{}, }) } @@ -21,9 +21,9 @@ func TestNameSet(t *testing.T) { source interface{} result pgtype.Name }{ - {source: "foo", result: pgtype.Name{String: "foo", Status: pgtype.Present}}, - {source: _string("bar"), result: pgtype.Name{String: "bar", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.Name{Status: pgtype.Null}}, + {source: "foo", result: pgtype.Name{String: "foo", Valid: true}}, + {source: _string("bar"), result: pgtype.Name{String: "bar", Valid: true}}, + {source: (*string)(nil), result: pgtype.Name{}}, } for i, tt := range successfulTests { @@ -48,8 +48,8 @@ func TestNameAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Name{String: "foo", Status: pgtype.Present}, dst: &s, expected: "foo"}, - {src: pgtype.Name{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, + {src: pgtype.Name{String: "foo", Valid: true}, dst: &s, expected: "foo"}, + {src: pgtype.Name{}, dst: &ps, expected: ((*string)(nil))}, } for i, tt := range simpleTests { @@ -68,7 +68,7 @@ func TestNameAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Name{String: "foo", Status: pgtype.Present}, dst: &ps, expected: "foo"}, + {src: pgtype.Name{String: "foo", Valid: true}, dst: &ps, expected: "foo"}, } for i, tt := range pointerAllocTests { @@ -86,7 +86,7 @@ func TestNameAssignTo(t *testing.T) { src pgtype.Name dst interface{} }{ - {src: pgtype.Name{Status: pgtype.Null}, dst: &s}, + {src: pgtype.Name{}, dst: &s}, } for i, tt := range errorTests { diff --git a/numeric.go b/numeric.go index a939625b..3d209ff2 100644 --- a/numeric.go +++ b/numeric.go @@ -57,14 +57,14 @@ var bigNBaseX4 *big.Int = big.NewInt(nbase * nbase * nbase * nbase) type Numeric struct { Int *big.Int Exp int32 - Status Status + Valid bool NaN bool InfinityModifier InfinityModifier } func (dst *Numeric) Set(src interface{}) error { if src == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} return nil } @@ -78,142 +78,142 @@ func (dst *Numeric) Set(src interface{}) error { switch value := src.(type) { case float32: if math.IsNaN(float64(value)) { - *dst = Numeric{Status: Present, NaN: true} + *dst = Numeric{Valid: true, NaN: true} return nil } else if math.IsInf(float64(value), 1) { - *dst = Numeric{Status: Present, InfinityModifier: Infinity} + *dst = Numeric{Valid: true, InfinityModifier: Infinity} return nil } else if math.IsInf(float64(value), -1) { - *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + *dst = Numeric{Valid: true, InfinityModifier: NegativeInfinity} return nil } num, exp, err := parseNumericString(strconv.FormatFloat(float64(value), 'f', -1, 64)) if err != nil { return err } - *dst = Numeric{Int: num, Exp: exp, Status: Present} + *dst = Numeric{Int: num, Exp: exp, Valid: true} case float64: if math.IsNaN(value) { - *dst = Numeric{Status: Present, NaN: true} + *dst = Numeric{Valid: true, NaN: true} return nil } else if math.IsInf(value, 1) { - *dst = Numeric{Status: Present, InfinityModifier: Infinity} + *dst = Numeric{Valid: true, InfinityModifier: Infinity} return nil } else if math.IsInf(value, -1) { - *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + *dst = Numeric{Valid: true, InfinityModifier: NegativeInfinity} return nil } num, exp, err := parseNumericString(strconv.FormatFloat(value, 'f', -1, 64)) if err != nil { return err } - *dst = Numeric{Int: num, Exp: exp, Status: Present} + *dst = Numeric{Int: num, Exp: exp, Valid: true} case int8: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case uint8: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case int16: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case uint16: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case int32: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case uint32: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case int64: - *dst = Numeric{Int: big.NewInt(value), Status: Present} + *dst = Numeric{Int: big.NewInt(value), Valid: true} case uint64: - *dst = Numeric{Int: (&big.Int{}).SetUint64(value), Status: Present} + *dst = Numeric{Int: (&big.Int{}).SetUint64(value), Valid: true} case int: - *dst = Numeric{Int: big.NewInt(int64(value)), Status: Present} + *dst = Numeric{Int: big.NewInt(int64(value)), Valid: true} case uint: - *dst = Numeric{Int: (&big.Int{}).SetUint64(uint64(value)), Status: Present} + *dst = Numeric{Int: (&big.Int{}).SetUint64(uint64(value)), Valid: true} case string: num, exp, err := parseNumericString(value) if err != nil { return err } - *dst = Numeric{Int: num, Exp: exp, Status: Present} + *dst = Numeric{Int: num, Exp: exp, Valid: true} case *float64: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *float32: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *int8: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *uint8: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *int16: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *uint16: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *int32: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *uint32: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *int64: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *uint64: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *int: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *uint: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case *string: if value == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} } else { return dst.Set(*value) } case InfinityModifier: - *dst = Numeric{InfinityModifier: value, Status: Present} + *dst = Numeric{InfinityModifier: value, Valid: true} default: if originalSrc, ok := underlyingNumberType(src); ok { return dst.Set(originalSrc) @@ -225,160 +225,156 @@ func (dst *Numeric) Set(src interface{}) error { } func (dst Numeric) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + + if dst.InfinityModifier != None { + return dst.InfinityModifier + } + return dst } func (src *Numeric) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *float32: - f, err := src.toFloat64() - if err != nil { - return err - } - return float64AssignTo(f, src.Status, dst) - case *float64: - f, err := src.toFloat64() - if err != nil { - return err - } - return float64AssignTo(f, src.Status, dst) - case *int: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt) > 0 { - return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt) < 0 { - return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int(normalizedInt.Int64()) - case *int8: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt8) > 0 { - return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt8) < 0 { - return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int8(normalizedInt.Int64()) - case *int16: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt16) > 0 { - return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt16) < 0 { - return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int16(normalizedInt.Int64()) - case *int32: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt32) > 0 { - return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt32) < 0 { - return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = int32(normalizedInt.Int64()) - case *int64: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(bigMaxInt64) > 0 { - return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) - } - if normalizedInt.Cmp(bigMinInt64) < 0 { - return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) - } - *v = normalizedInt.Int64() - case *uint: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint) > 0 { - return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint(normalizedInt.Uint64()) - case *uint8: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint8) > 0 { - return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint8(normalizedInt.Uint64()) - case *uint16: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint16) > 0 { - return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint16(normalizedInt.Uint64()) - case *uint32: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint32) > 0 { - return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = uint32(normalizedInt.Uint64()) - case *uint64: - normalizedInt, err := src.toBigInt() - if err != nil { - return err - } - if normalizedInt.Cmp(big0) < 0 { - return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) - } else if normalizedInt.Cmp(bigMaxUint64) > 0 { - return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) - } - *v = normalizedInt.Uint64() - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } + switch v := dst.(type) { + case *float32: + f, err := src.toFloat64() + if err != nil { + return err + } + return float64AssignTo(f, src.Valid, dst) + case *float64: + f, err := src.toFloat64() + if err != nil { + return err + } + return float64AssignTo(f, src.Valid, dst) + case *int: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(bigMaxInt) > 0 { + return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) + } + if normalizedInt.Cmp(bigMinInt) < 0 { + return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) + } + *v = int(normalizedInt.Int64()) + case *int8: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(bigMaxInt8) > 0 { + return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) + } + if normalizedInt.Cmp(bigMinInt8) < 0 { + return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) + } + *v = int8(normalizedInt.Int64()) + case *int16: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(bigMaxInt16) > 0 { + return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) + } + if normalizedInt.Cmp(bigMinInt16) < 0 { + return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) + } + *v = int16(normalizedInt.Int64()) + case *int32: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(bigMaxInt32) > 0 { + return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) + } + if normalizedInt.Cmp(bigMinInt32) < 0 { + return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) + } + *v = int32(normalizedInt.Int64()) + case *int64: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(bigMaxInt64) > 0 { + return fmt.Errorf("%v is greater than maximum value for %T", normalizedInt, *v) + } + if normalizedInt.Cmp(bigMinInt64) < 0 { + return fmt.Errorf("%v is less than minimum value for %T", normalizedInt, *v) + } + *v = normalizedInt.Int64() + case *uint: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(big0) < 0 { + return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) + } else if normalizedInt.Cmp(bigMaxUint) > 0 { + return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) + } + *v = uint(normalizedInt.Uint64()) + case *uint8: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(big0) < 0 { + return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) + } else if normalizedInt.Cmp(bigMaxUint8) > 0 { + return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) + } + *v = uint8(normalizedInt.Uint64()) + case *uint16: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(big0) < 0 { + return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) + } else if normalizedInt.Cmp(bigMaxUint16) > 0 { + return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) + } + *v = uint16(normalizedInt.Uint64()) + case *uint32: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(big0) < 0 { + return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) + } else if normalizedInt.Cmp(bigMaxUint32) > 0 { + return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) + } + *v = uint32(normalizedInt.Uint64()) + case *uint64: + normalizedInt, err := src.toBigInt() + if err != nil { + return err + } + if normalizedInt.Cmp(big0) < 0 { + return fmt.Errorf("%d is less than zero for %T", normalizedInt, *v) + } else if normalizedInt.Cmp(bigMaxUint64) > 0 { + return fmt.Errorf("%d is greater than maximum value for %T", normalizedInt, *v) + } + *v = normalizedInt.Uint64() + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } + return nil } @@ -430,18 +426,18 @@ func (src *Numeric) toFloat64() (float64, error) { func (dst *Numeric) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} return nil } if string(src) == "NaN" { - *dst = Numeric{Status: Present, NaN: true} + *dst = Numeric{Valid: true, NaN: true} return nil } else if string(src) == "Infinity" { - *dst = Numeric{Status: Present, InfinityModifier: Infinity} + *dst = Numeric{Valid: true, InfinityModifier: Infinity} return nil } else if string(src) == "-Infinity" { - *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + *dst = Numeric{Valid: true, InfinityModifier: NegativeInfinity} return nil } @@ -450,7 +446,7 @@ func (dst *Numeric) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Numeric{Int: num, Exp: exp, Status: Present} + *dst = Numeric{Int: num, Exp: exp, Valid: true} return nil } @@ -477,7 +473,7 @@ func parseNumericString(str string) (n *big.Int, exp int32, err error) { func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} return nil } @@ -496,18 +492,18 @@ func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { rp += 2 if sign == pgNumericNaNSign { - *dst = Numeric{Status: Present, NaN: true} + *dst = Numeric{Valid: true, NaN: true} return nil } else if sign == pgNumericPosInfSign { - *dst = Numeric{Status: Present, InfinityModifier: Infinity} + *dst = Numeric{Valid: true, InfinityModifier: Infinity} return nil } else if sign == pgNumericNegInfSign { - *dst = Numeric{Status: Present, InfinityModifier: NegativeInfinity} + *dst = Numeric{Valid: true, InfinityModifier: NegativeInfinity} return nil } if ndigits == 0 { - *dst = Numeric{Int: big.NewInt(0), Status: Present} + *dst = Numeric{Int: big.NewInt(0), Valid: true} return nil } @@ -579,7 +575,7 @@ func (dst *Numeric) DecodeBinary(ci *ConnInfo, src []byte) error { accum.Neg(accum) } - *dst = Numeric{Int: accum, Exp: exp, Status: Present} + *dst = Numeric{Int: accum, Exp: exp, Valid: true} return nil @@ -605,11 +601,8 @@ func nbaseDigitsToInt64(src []byte) (accum int64, bytesRead, digitsRead int) { } func (src Numeric) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.NaN { @@ -630,11 +623,8 @@ func (src Numeric) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Numeric) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.NaN { @@ -734,7 +724,7 @@ func (src Numeric) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Numeric) Scan(src interface{}) error { if src == nil { - *dst = Numeric{Status: Null} + *dst = Numeric{} return nil } @@ -752,17 +742,14 @@ func (dst *Numeric) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Numeric) Value() (driver.Value, error) { - switch src.Status { - case Present: - buf, err := src.EncodeText(nil, nil) - if err != nil { - return nil, err - } - - return string(buf), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + buf, err := src.EncodeText(nil, nil) + if err != nil { + return nil, err + } + + return string(buf), nil } diff --git a/numeric_array.go b/numeric_array.go index 31899dec..3e9298b6 100644 --- a/numeric_array.go +++ b/numeric_array.go @@ -14,13 +14,13 @@ import ( type NumericArray struct { Elements []Numeric Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *NumericArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} return nil } @@ -36,9 +36,9 @@ func (dst *NumericArray) Set(src interface{}) error { case []float32: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*float32: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -68,15 +68,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []float64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -87,15 +87,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*float64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -106,15 +106,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []int64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -125,15 +125,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*int64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -144,15 +144,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []uint64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -163,15 +163,15 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*uint64: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { elements := make([]Numeric, len(value)) for i := range value { @@ -182,20 +182,20 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Numeric: if value == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} } else if len(value) == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} } else { *dst = NumericArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -204,7 +204,7 @@ func (dst *NumericArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} return nil } @@ -213,7 +213,7 @@ func (dst *NumericArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for NumericArray", src) } if elementsLength == 0 { - *dst = NumericArray{Status: Present} + *dst = NumericArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -226,7 +226,7 @@ func (dst *NumericArray) Set(src interface{}) error { *dst = NumericArray{ Elements: make([]Numeric, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -293,138 +293,131 @@ func (dst *NumericArray) setRecursive(value reflect.Value, index, dimension int) } func (dst NumericArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *NumericArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]float32: - *v = make([]float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*float32: - *v = make([]*float32, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]float64: - *v = make([]float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*float64: - *v = make([]*float64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]int64: - *v = make([]int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*int64: - *v = make([]*int64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]uint64: - *v = make([]uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*uint64: - *v = make([]*uint64, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]float32: + *v = make([]float32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*float32: + *v = make([]*float32, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]float64: + *v = make([]float64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*float64: + *v = make([]*float64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]int64: + *v = make([]int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*int64: + *v = make([]*int64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]uint64: + *v = make([]uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*uint64: + *v = make([]*uint64, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *NumericArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -476,7 +469,7 @@ func (src *NumericArray) assignToRecursive(value reflect.Value, index, dimension func (dst *NumericArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} return nil } @@ -505,14 +498,14 @@ func (dst *NumericArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = NumericArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = NumericArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *NumericArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = NumericArray{Status: Null} + *dst = NumericArray{} return nil } @@ -523,7 +516,7 @@ func (dst *NumericArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = NumericArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = NumericArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -548,16 +541,13 @@ func (dst *NumericArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = NumericArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = NumericArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src NumericArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -610,11 +600,8 @@ func (src NumericArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src NumericArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -628,7 +615,7 @@ func (src NumericArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/numeric_array_test.go b/numeric_array_test.go index 7c1e8c3b..ee36d1a7 100644 --- a/numeric_array_test.go +++ b/numeric_array_test.go @@ -15,41 +15,41 @@ func TestNumericArrayTranscode(t *testing.T) { &pgtype.NumericArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Int: big.NewInt(1), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.NumericArray{Status: pgtype.Null}, + &pgtype.NumericArray{}, &pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: big.NewInt(6), Status: pgtype.Present}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, + {}, + {Int: big.NewInt(6), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -62,82 +62,82 @@ func TestNumericArraySet(t *testing.T) { { source: []float32{1}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []float32{float32(math.Copysign(0, -1))}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(0), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(0), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []float64{1}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []float64{math.Copysign(0, -1)}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(0), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(0), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]float32)(nil)), - result: pgtype.NumericArray{Status: pgtype.Null}, + result: pgtype.NumericArray{}, }, { source: [][]float32{{1}, {2}}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Int: big.NewInt(5), Status: pgtype.Present}, - {Int: big.NewInt(6), Status: pgtype.Present}}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, + {Int: big.NewInt(5), Valid: true}, + {Int: big.NewInt(6), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]float32{{1}, {2}}, result: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, result: pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Int: big.NewInt(5), Status: pgtype.Present}, - {Int: big.NewInt(6), Status: pgtype.Present}}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, + {Int: big.NewInt(5), Valid: true}, + {Int: big.NewInt(6), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -169,81 +169,81 @@ func TestNumericArrayAssignTo(t *testing.T) { }{ { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float32Slice, expected: []float32{1}, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float64Slice, expected: []float64{1}, }, { - src: pgtype.NumericArray{Status: pgtype.Null}, + src: pgtype.NumericArray{}, dst: &float32Slice, expected: (([]float32)(nil)), }, { - src: pgtype.NumericArray{Status: pgtype.Present}, + src: pgtype.NumericArray{Valid: true}, dst: &float32Slice, expected: []float32{}, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32SliceDim2, expected: [][]float32{{1}, {2}}, }, { src: pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Int: big.NewInt(5), Status: pgtype.Present}, - {Int: big.NewInt(6), Status: pgtype.Present}}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, + {Int: big.NewInt(5), Valid: true}, + {Int: big.NewInt(6), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32SliceDim4, expected: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim2, expected: [2][1]float32{{1}, {2}}, }, { src: pgtype.NumericArray{ Elements: []pgtype.Numeric{ - {Int: big.NewInt(1), Status: pgtype.Present}, - {Int: big.NewInt(2), Status: pgtype.Present}, - {Int: big.NewInt(3), Status: pgtype.Present}, - {Int: big.NewInt(4), Status: pgtype.Present}, - {Int: big.NewInt(5), Status: pgtype.Present}, - {Int: big.NewInt(6), Status: pgtype.Present}}, + {Int: big.NewInt(1), Valid: true}, + {Int: big.NewInt(2), Valid: true}, + {Int: big.NewInt(3), Valid: true}, + {Int: big.NewInt(4), Valid: true}, + {Int: big.NewInt(5), Valid: true}, + {Int: big.NewInt(6), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim4, expected: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}}, }, @@ -266,31 +266,31 @@ func TestNumericArrayAssignTo(t *testing.T) { }{ { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Status: pgtype.Null}}, + Elements: []pgtype.Numeric{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &float32Slice, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim2, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32Slice, }, { src: pgtype.NumericArray{ - Elements: []pgtype.Numeric{{Int: big.NewInt(1), Status: pgtype.Present}, {Int: big.NewInt(2), Status: pgtype.Present}}, + Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &float32ArrayDim4, }, } diff --git a/numeric_test.go b/numeric_test.go index 455c3ac3..58ce5c0f 100644 --- a/numeric_test.go +++ b/numeric_test.go @@ -13,7 +13,7 @@ import ( // For test purposes only. Note that it does not normalize values. e.g. (Int: 1, Exp: 3) will not equal (Int: 1000, Exp: 0) func numericEqual(left, right *pgtype.Numeric) bool { - return left.Status == right.Status && + return left.Valid == right.Valid && left.Exp == right.Exp && ((left.Int == nil && right.Int == nil) || (left.Int != nil && right.Int != nil && left.Int.Cmp(right.Int) == 0)) && left.NaN == right.NaN @@ -21,12 +21,12 @@ func numericEqual(left, right *pgtype.Numeric) bool { // For test purposes only. func numericNormalizedEqual(left, right *pgtype.Numeric) bool { - if left.Status != right.Status { + if left.Valid != right.Valid { return false } - normLeft := &pgtype.Numeric{Int: (&big.Int{}).Set(left.Int), Status: left.Status} - normRight := &pgtype.Numeric{Int: (&big.Int{}).Set(right.Int), Status: right.Status} + normLeft := &pgtype.Numeric{Int: (&big.Int{}).Set(left.Int), Valid: left.Valid} + normRight := &pgtype.Numeric{Int: (&big.Int{}).Set(right.Int), Valid: right.Valid} if left.Exp < right.Exp { mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(right.Exp-left.Exp)), nil) @@ -51,66 +51,66 @@ func TestNumericNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select '0'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Valid: true}, }, { SQL: "select '1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Valid: true}, }, { SQL: "select '10.00'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1000), Exp: -2, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(1000), Exp: -2, Valid: true}, }, { SQL: "select '1e-3'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: -3, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: -3, Valid: true}, }, { SQL: "select '-1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Valid: true}, }, { SQL: "select '10000'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 4, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 4, Valid: true}, }, { SQL: "select '3.14'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Valid: true}, }, { SQL: "select '1.1'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(11), Exp: -1, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(11), Exp: -1, Valid: true}, }, { SQL: "select '100010001'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(100010001), Exp: 0, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(100010001), Exp: 0, Valid: true}, }, { SQL: "select '100010001.0001'::numeric", - Value: &pgtype.Numeric{Int: big.NewInt(1000100010001), Exp: -4, Status: pgtype.Present}, + Value: &pgtype.Numeric{Int: big.NewInt(1000100010001), Exp: -4, Valid: true}, }, { SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric", Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "423723478923478928934789237432487213832189417894318904389012483210893443219085471578891547854892438945012347981"), - Exp: -41, - Status: pgtype.Present, + Int: mustParseBigInt(t, "423723478923478928934789237432487213832189417894318904389012483210893443219085471578891547854892438945012347981"), + Exp: -41, + Valid: true, }, }, { SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric", Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), - Exp: -196, - Status: pgtype.Present, + Int: mustParseBigInt(t, "8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), + Exp: -196, + Valid: true, }, }, { SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric", Value: &pgtype.Numeric{ - Int: mustParseBigInt(t, "123"), - Exp: -186, - Status: pgtype.Present, + Int: mustParseBigInt(t, "123"), + Exp: -186, + Valid: true, }, }, }) @@ -119,48 +119,48 @@ func TestNumericNormalize(t *testing.T) { func TestNumericTranscode(t *testing.T) { max := new(big.Int).Exp(big.NewInt(10), big.NewInt(147454), nil) max.Add(max, big.NewInt(1)) - longestNumeric := &pgtype.Numeric{Int: max, Exp: -16383, Status: pgtype.Present} + longestNumeric := &pgtype.Numeric{Int: max, Exp: -16383, Valid: true} testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{ - &pgtype.Numeric{NaN: true, Status: pgtype.Present}, - &pgtype.Numeric{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, - &pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, + &pgtype.Numeric{NaN: true, Valid: true}, + &pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}, + &pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, - &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(1), Exp: 6, Status: pgtype.Present}, + &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(1), Exp: 6, Valid: true}, // preserves significant zeroes - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -1, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -2, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -3, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -4, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -5, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -6, Status: pgtype.Present}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -1, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -2, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -3, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -4, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -5, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(10000000), Exp: -6, Valid: true}, - &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -7, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -8, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -9, Status: pgtype.Present}, - &pgtype.Numeric{Int: big.NewInt(123), Exp: -1500, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3723409723490243842378942378901237502734019231380123"), Exp: 81, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "723409723490243842378942378901237502734019231380123"), Exp: 82, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "23409723490243842378942378901237502734019231380123"), Exp: 83, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3409723490243842378942378901237502734019231380123"), Exp: 84, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3423409823409243892349028349023482934092340892390101"), Exp: -91, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "423409823409243892349028349023482934092340892390101"), Exp: -92, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "23409823409243892349028349023482934092340892390101"), Exp: -93, Status: pgtype.Present}, - &pgtype.Numeric{Int: mustParseBigInt(t, "3409823409243892349028349023482934092340892390101"), Exp: -94, Status: pgtype.Present}, + &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(123), Exp: -7, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(123), Exp: -8, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(123), Exp: -9, Valid: true}, + &pgtype.Numeric{Int: big.NewInt(123), Exp: -1500, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "3723409723490243842378942378901237502734019231380123"), Exp: 81, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "723409723490243842378942378901237502734019231380123"), Exp: 82, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "23409723490243842378942378901237502734019231380123"), Exp: 83, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "3409723490243842378942378901237502734019231380123"), Exp: 84, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "3423409823409243892349028349023482934092340892390101"), Exp: -91, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "423409823409243892349028349023482934092340892390101"), Exp: -92, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "23409823409243892349028349023482934092340892390101"), Exp: -93, Valid: true}, + &pgtype.Numeric{Int: mustParseBigInt(t, "3409823409243892349028349023482934092340892390101"), Exp: -94, Valid: true}, longestNumeric, - &pgtype.Numeric{Status: pgtype.Null}, + &pgtype.Numeric{}, }, func(aa, bb interface{}) bool { a := aa.(pgtype.Numeric) b := bb.(pgtype.Numeric) @@ -181,8 +181,8 @@ func TestNumericTranscodeFuzz(t *testing.T) { num := (&big.Int{}).Rand(r, max) negNum := &big.Int{} negNum.Neg(num) - values = append(values, &pgtype.Numeric{Int: num, Exp: int32(j), Status: pgtype.Present}) - values = append(values, &pgtype.Numeric{Int: negNum, Exp: int32(j), Status: pgtype.Present}) + values = append(values, &pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}) + values = append(values, &pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}) } } @@ -200,36 +200,36 @@ func TestNumericSet(t *testing.T) { source interface{} result *pgtype.Numeric }{ - {source: float32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: float32(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present}}, - {source: float64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: float64(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Present}}, - {source: int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: int16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: int32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: int64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: int8(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}}, - {source: int16(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}}, - {source: int32(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}}, - {source: int64(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}}, - {source: uint8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: uint64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: "1", result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: _int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Status: pgtype.Present}}, - {source: float64(1000), result: &pgtype.Numeric{Int: big.NewInt(1), Exp: 3, Status: pgtype.Present}}, - {source: float64(1234), result: &pgtype.Numeric{Int: big.NewInt(1234), Exp: 0, Status: pgtype.Present}}, - {source: float64(12345678900), result: &pgtype.Numeric{Int: big.NewInt(123456789), Exp: 2, Status: pgtype.Present}}, - {source: float64(12345.678901), result: &pgtype.Numeric{Int: big.NewInt(12345678901), Exp: -6, Status: pgtype.Present}}, - {source: math.NaN(), result: &pgtype.Numeric{Int: nil, Exp: 0, Status: pgtype.Present, NaN: true}}, - {source: float32(math.NaN()), result: &pgtype.Numeric{Int: nil, Exp: 0, Status: pgtype.Present, NaN: true}}, - {source: pgtype.Infinity, result: &pgtype.Numeric{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}}, - {source: math.Inf(1), result: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}}, - {source: float32(math.Inf(1)), result: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}}, - {source: pgtype.NegativeInfinity, result: &pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}}, - {source: math.Inf(-1), result: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}}, - {source: float32(math.Inf(1)), result: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}}, + {source: float32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: float32(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Valid: true}}, + {source: float64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: float64(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Valid: true}}, + {source: int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: int16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: int32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: int64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: int8(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}}, + {source: int16(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}}, + {source: int32(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}}, + {source: int64(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}}, + {source: uint8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: uint16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: uint32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: uint64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: "1", result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: _int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}}, + {source: float64(1000), result: &pgtype.Numeric{Int: big.NewInt(1), Exp: 3, Valid: true}}, + {source: float64(1234), result: &pgtype.Numeric{Int: big.NewInt(1234), Exp: 0, Valid: true}}, + {source: float64(12345678900), result: &pgtype.Numeric{Int: big.NewInt(123456789), Exp: 2, Valid: true}}, + {source: float64(12345.678901), result: &pgtype.Numeric{Int: big.NewInt(12345678901), Exp: -6, Valid: true}}, + {source: math.NaN(), result: &pgtype.Numeric{Int: nil, Exp: 0, Valid: true, NaN: true}}, + {source: float32(math.NaN()), result: &pgtype.Numeric{Int: nil, Exp: 0, Valid: true, NaN: true}}, + {source: pgtype.Infinity, result: &pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}}, + {source: math.Inf(1), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}}, + {source: float32(math.Inf(1)), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}}, + {source: pgtype.NegativeInfinity, result: &pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}}, + {source: math.Inf(-1), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}}, + {source: float32(math.Inf(1)), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}}, } for i, tt := range successfulTests { @@ -269,30 +269,30 @@ func TestNumericAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &f32, expected: float32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &f64, expected: float64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Status: pgtype.Present}, dst: &f32, expected: float32(4.2)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Status: pgtype.Present}, dst: &f64, expected: float64(4.2)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: 3, Status: pgtype.Present}, dst: &i64, expected: int64(42000)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, - {src: &pgtype.Numeric{Int: big.NewInt(1006), Exp: -2, Status: pgtype.Present}, dst: &f64, expected: float64(10.06)}, // https://github.com/jackc/pgtype/issues/27 - {src: &pgtype.Numeric{Status: pgtype.Present, NaN: true}, dst: &f64, expected: math.NaN()}, - {src: &pgtype.Numeric{Status: pgtype.Present, NaN: true}, dst: &f32, expected: float32(math.NaN())}, - {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, dst: &f64, expected: math.Inf(1)}, - {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, dst: &f32, expected: float32(math.Inf(1))}, - {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}, dst: &f64, expected: math.Inf(-1)}, - {src: &pgtype.Numeric{Status: pgtype.Present, InfinityModifier: pgtype.NegativeInfinity}, dst: &f32, expected: float32(math.Inf(-1))}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &f32, expected: float32(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &f64, expected: float64(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Valid: true}, dst: &f32, expected: float32(4.2)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Valid: true}, dst: &f64, expected: float64(4.2)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i16, expected: int16(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i32, expected: int32(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i64, expected: int64(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Exp: 3, Valid: true}, dst: &i64, expected: int64(42000)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i, expected: int(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui, expected: uint(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &pi8, expected: ((*int8)(nil))}, + {src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: &pgtype.Numeric{Int: big.NewInt(1006), Exp: -2, Valid: true}, dst: &f64, expected: float64(10.06)}, // https://github.com/jackc/pgtype/issues/27 + {src: &pgtype.Numeric{Valid: true, NaN: true}, dst: &f64, expected: math.NaN()}, + {src: &pgtype.Numeric{Valid: true, NaN: true}, dst: &f32, expected: float32(math.NaN())}, + {src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}, dst: &f64, expected: math.Inf(1)}, + {src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}, dst: &f32, expected: float32(math.Inf(1))}, + {src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}, dst: &f64, expected: math.Inf(-1)}, + {src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}, dst: &f32, expected: float32(math.Inf(-1))}, } for i, tt := range simpleTests { @@ -329,8 +329,8 @@ func TestNumericAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &pf32, expected: float32(42)}, - {src: &pgtype.Numeric{Int: big.NewInt(42), Status: pgtype.Present}, dst: &pf64, expected: float64(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &pf32, expected: float32(42)}, + {src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &pf64, expected: float64(42)}, } for i, tt := range pointerAllocTests { @@ -348,14 +348,14 @@ func TestNumericAssignTo(t *testing.T) { src *pgtype.Numeric dst interface{} }{ - {src: &pgtype.Numeric{Int: big.NewInt(150), Status: pgtype.Present}, dst: &i8}, - {src: &pgtype.Numeric{Int: big.NewInt(40000), Status: pgtype.Present}, dst: &i16}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui8}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui16}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui32}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui64}, - {src: &pgtype.Numeric{Int: big.NewInt(-1), Status: pgtype.Present}, dst: &ui}, - {src: &pgtype.Numeric{Int: big.NewInt(0), Status: pgtype.Null}, dst: &i32}, + {src: &pgtype.Numeric{Int: big.NewInt(150), Valid: true}, dst: &i8}, + {src: &pgtype.Numeric{Int: big.NewInt(40000), Valid: true}, dst: &i16}, + {src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui8}, + {src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui16}, + {src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui32}, + {src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui64}, + {src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui}, + {src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &i32}, } for i, tt := range errorTests { diff --git a/numrange.go b/numrange.go index 3d5951a2..f1118d83 100644 --- a/numrange.go +++ b/numrange.go @@ -12,13 +12,13 @@ type Numrange struct { Upper Numeric LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Numrange) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Numrange{Status: Null} + *dst = Numrange{} return nil } @@ -36,15 +36,11 @@ func (dst *Numrange) Set(src interface{}) error { return nil } -func (dst Numrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Numrange) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Numrange) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Numrange) AssignTo(dst interface{}) error { func (dst *Numrange) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Numrange{Status: Null} + *dst = Numrange{} return nil } @@ -62,7 +58,7 @@ func (dst *Numrange) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Numrange{Status: Present} + *dst = Numrange{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Numrange) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Numrange) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Numrange{Status: Null} + *dst = Numrange{} return nil } @@ -97,7 +93,7 @@ func (dst *Numrange) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Numrange{Status: Present} + *dst = Numrange{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Numrange) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Numrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Numrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Numrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Numrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Numrange) Scan(src interface{}) error { if src == nil { - *dst = Numrange{Status: Null} + *dst = Numrange{} return nil } diff --git a/numrange_test.go b/numrange_test.go index 0bbb26f0..b9ea7658 100644 --- a/numrange_test.go +++ b/numrange_test.go @@ -13,34 +13,34 @@ func TestNumrangeTranscode(t *testing.T) { &pgtype.Numrange{ LowerType: pgtype.Empty, UpperType: pgtype.Empty, - Status: pgtype.Present, + Valid: true, }, &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-543), Exp: 3, Status: pgtype.Present}, - Upper: pgtype.Numeric{Int: big.NewInt(342), Exp: 1, Status: pgtype.Present}, + Lower: pgtype.Numeric{Int: big.NewInt(-543), Exp: 3, Valid: true}, + Upper: pgtype.Numeric{Int: big.NewInt(342), Exp: 1, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, - Upper: pgtype.Numeric{Int: big.NewInt(-5), Exp: 0, Status: pgtype.Present}, + Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Valid: true}, + Upper: pgtype.Numeric{Int: big.NewInt(-5), Exp: 0, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, &pgtype.Numrange{ - Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, + Lower: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, - Status: pgtype.Present, + Valid: true, }, &pgtype.Numrange{ - Upper: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Status: pgtype.Present}, + Upper: pgtype.Numeric{Int: big.NewInt(-42), Exp: 1, Valid: true}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Numrange{Status: pgtype.Null}, + &pgtype.Numrange{}, }) } diff --git a/oid_value_test.go b/oid_value_test.go index 69742dd7..021f81d3 100644 --- a/oid_value_test.go +++ b/oid_value_test.go @@ -10,8 +10,8 @@ import ( func TestOIDValueTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "oid", []interface{}{ - &pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, - &pgtype.OIDValue{Status: pgtype.Null}, + &pgtype.OIDValue{Uint: 42, Valid: true}, + &pgtype.OIDValue{}, }) } @@ -20,7 +20,7 @@ func TestOIDValueSet(t *testing.T) { source interface{} result pgtype.OIDValue }{ - {source: uint32(1), result: pgtype.OIDValue{Uint: 1, Status: pgtype.Present}}, + {source: uint32(1), result: pgtype.OIDValue{Uint: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -45,8 +45,8 @@ func TestOIDValueAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.OIDValue{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, + {src: pgtype.OIDValue{Uint: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.OIDValue{}, dst: &pui32, expected: ((*uint32)(nil))}, } for i, tt := range simpleTests { @@ -65,7 +65,7 @@ func TestOIDValueAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.OIDValue{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, + {src: pgtype.OIDValue{Uint: 42, Valid: true}, dst: &pui32, expected: uint32(42)}, } for i, tt := range pointerAllocTests { @@ -83,7 +83,7 @@ func TestOIDValueAssignTo(t *testing.T) { src pgtype.OIDValue dst interface{} }{ - {src: pgtype.OIDValue{Status: pgtype.Null}, dst: &ui32}, + {src: pgtype.OIDValue{}, dst: &ui32}, } for i, tt := range errorTests { diff --git a/path.go b/path.go index 9f89969e..7ac38c68 100644 --- a/path.go +++ b/path.go @@ -14,7 +14,7 @@ import ( type Path struct { P []Vec2 Closed bool - Status Status + Valid bool } func (dst *Path) Set(src interface{}) error { @@ -22,14 +22,10 @@ func (dst *Path) Set(src interface{}) error { } func (dst Path) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Path) AssignTo(dst interface{}) error { @@ -38,7 +34,7 @@ func (src *Path) AssignTo(dst interface{}) error { func (dst *Path) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Path{Status: Null} + *dst = Path{} return nil } @@ -75,13 +71,13 @@ func (dst *Path) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Path{P: points, Closed: closed, Status: Present} + *dst = Path{P: points, Closed: closed, Valid: true} return nil } func (dst *Path) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Path{Status: Null} + *dst = Path{} return nil } @@ -110,17 +106,14 @@ func (dst *Path) DecodeBinary(ci *ConnInfo, src []byte) error { *dst = Path{ P: points, Closed: closed, - Status: Present, + Valid: true, } return nil } func (src Path) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var startByte, endByte byte @@ -147,11 +140,8 @@ func (src Path) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Path) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var closeByte byte @@ -173,7 +163,7 @@ func (src Path) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Path) Scan(src interface{}) error { if src == nil { - *dst = Path{Status: Null} + *dst = Path{} return nil } diff --git a/path_test.go b/path_test.go index 969a89ec..9a66996e 100644 --- a/path_test.go +++ b/path_test.go @@ -12,18 +12,18 @@ func TestPathTranscode(t *testing.T) { &pgtype.Path{ P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}}, Closed: false, - Status: pgtype.Present, + Valid: true, }, &pgtype.Path{ P: []pgtype.Vec2{{3.14, 1.678}, {7.1, 5.234}, {23.1, 9.34}}, Closed: true, - Status: pgtype.Present, + Valid: true, }, &pgtype.Path{ P: []pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}}, Closed: true, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Path{Status: pgtype.Null}, + &pgtype.Path{}, }) } diff --git a/pgtype.go b/pgtype.go index 200fb562..c4fe870d 100644 --- a/pgtype.go +++ b/pgtype.go @@ -3,7 +3,6 @@ package pgtype import ( "database/sql" "encoding/binary" - "errors" "fmt" "math" "net" @@ -82,14 +81,6 @@ const ( Int8rangeOID = 3926 ) -type Status byte - -const ( - Undefined Status = iota - Null - Present -) - type InfinityModifier int8 const ( @@ -208,9 +199,6 @@ type TextEncoder interface { EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error) } -var errUndefined = errors.New("cannot encode status undefined") -var errBadStatus = errors.New("invalid status") - type nullAssignmentError struct { dst interface{} } diff --git a/pgtype_test.go b/pgtype_test.go index 5fd89dcb..7ae756e5 100644 --- a/pgtype_test.go +++ b/pgtype_test.go @@ -232,7 +232,7 @@ func BenchmarkConnInfoScanInt4IntoBinaryDecoder(b *testing.B) { if err != nil { b.Fatal(err) } - if v != (pgtype.Int4{Int: 42, Status: pgtype.Present}) { + if v != (pgtype.Int4{Int: 42, Valid: true}) { b.Fatal("scan failed due to bad value") } } @@ -252,7 +252,7 @@ func TestScanPlanBinaryInt32ScanChangedType(t *testing.T) { err = plan.Scan(ci, pgtype.Int4OID, pgtype.BinaryFormatCode, src, &d) require.NoError(t, err) require.EqualValues(t, 42, d.Int) - require.EqualValues(t, pgtype.Present, d.Status) + require.True(t, d.Valid) } func BenchmarkConnInfoScanInt4IntoGoInt32(b *testing.B) { @@ -285,7 +285,7 @@ func BenchmarkScanPlanScanInt4IntoBinaryDecoder(b *testing.B) { if err != nil { b.Fatal(err) } - if v != (pgtype.Int4{Int: 42, Status: pgtype.Present}) { + if v != (pgtype.Int4{Int: 42, Valid: true}) { b.Fatal("scan failed due to bad value") } } diff --git a/pguint32.go b/pguint32.go index a0e88ca2..e36ebb1f 100644 --- a/pguint32.go +++ b/pguint32.go @@ -13,8 +13,8 @@ import ( // pguint32 is the core type that is used to implement PostgreSQL types such as // CID and XID. type pguint32 struct { - Uint uint32 - Status Status + Uint uint32 + Valid bool } // Set converts from src to dst. Note that as pguint32 is not a general @@ -29,9 +29,9 @@ func (dst *pguint32) Set(src interface{}) error { if value > math.MaxUint32 { return fmt.Errorf("%d is greater than maximum value for pguint32", value) } - *dst = pguint32{Uint: uint32(value), Status: Present} + *dst = pguint32{Uint: uint32(value), Valid: true} case uint32: - *dst = pguint32{Uint: value, Status: Present} + *dst = pguint32{Uint: value, Valid: true} default: return fmt.Errorf("cannot convert %v to pguint32", value) } @@ -40,14 +40,10 @@ func (dst *pguint32) Set(src interface{}) error { } func (dst pguint32) Get() interface{} { - switch dst.Status { - case Present: - return dst.Uint - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Uint } // AssignTo assigns from src to dst. Note that as pguint32 is not a general number @@ -55,13 +51,13 @@ func (dst pguint32) Get() interface{} { func (src *pguint32) AssignTo(dst interface{}) error { switch v := dst.(type) { case *uint32: - if src.Status == Present { + if src.Valid { *v = src.Uint } else { return fmt.Errorf("cannot assign %v into %T", src, dst) } case **uint32: - if src.Status == Present { + if src.Valid { n := src.Uint *v = &n } else { @@ -74,7 +70,7 @@ func (src *pguint32) AssignTo(dst interface{}) error { func (dst *pguint32) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = pguint32{Status: Null} + *dst = pguint32{} return nil } @@ -83,13 +79,13 @@ func (dst *pguint32) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = pguint32{Uint: uint32(n), Status: Present} + *dst = pguint32{Uint: uint32(n), Valid: true} return nil } func (dst *pguint32) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = pguint32{Status: Null} + *dst = pguint32{} return nil } @@ -98,27 +94,21 @@ func (dst *pguint32) DecodeBinary(ci *ConnInfo, src []byte) error { } n := binary.BigEndian.Uint32(src) - *dst = pguint32{Uint: n, Status: Present} + *dst = pguint32{Uint: n, Valid: true} return nil } func (src pguint32) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, strconv.FormatUint(uint64(src.Uint), 10)...), nil } func (src pguint32) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return pgio.AppendUint32(buf, src.Uint), nil @@ -127,16 +117,16 @@ func (src pguint32) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *pguint32) Scan(src interface{}) error { if src == nil { - *dst = pguint32{Status: Null} + *dst = pguint32{} return nil } switch src := src.(type) { case uint32: - *dst = pguint32{Uint: src, Status: Present} + *dst = pguint32{Uint: src, Valid: true} return nil case int64: - *dst = pguint32{Uint: uint32(src), Status: Present} + *dst = pguint32{Uint: uint32(src), Valid: true} return nil case string: return dst.DecodeText(nil, []byte(src)) @@ -151,12 +141,8 @@ func (dst *pguint32) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src pguint32) Value() (driver.Value, error) { - switch src.Status { - case Present: - return int64(src.Uint), nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return int64(src.Uint), nil } diff --git a/point.go b/point.go index 0c799106..d35dbf03 100644 --- a/point.go +++ b/point.go @@ -18,13 +18,13 @@ type Vec2 struct { } type Point struct { - P Vec2 - Status Status + P Vec2 + Valid bool } func (dst *Point) Set(src interface{}) error { if src == nil { - dst.Status = Null + dst.Valid = false return nil } err := fmt.Errorf("cannot convert %v to Point", src) @@ -46,7 +46,7 @@ func (dst *Point) Set(src interface{}) error { func parsePoint(src []byte) (*Point, error) { if src == nil || bytes.Compare(src, []byte("null")) == 0 { - return &Point{Status: Null}, nil + return &Point{}, nil } if len(src) < 5 { @@ -70,18 +70,14 @@ func parsePoint(src []byte) (*Point, error) { return nil, err } - return &Point{P: Vec2{x, y}, Status: Present}, nil + return &Point{P: Vec2{x, y}, Valid: true}, nil } func (dst Point) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Point) AssignTo(dst interface{}) error { @@ -90,7 +86,7 @@ func (src *Point) AssignTo(dst interface{}) error { func (dst *Point) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Point{Status: Null} + *dst = Point{} return nil } @@ -113,13 +109,13 @@ func (dst *Point) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Point{P: Vec2{x, y}, Status: Present} + *dst = Point{P: Vec2{x, y}, Valid: true} return nil } func (dst *Point) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Point{Status: Null} + *dst = Point{} return nil } @@ -131,18 +127,15 @@ func (dst *Point) DecodeBinary(ci *ConnInfo, src []byte) error { y := binary.BigEndian.Uint64(src[8:]) *dst = Point{ - P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, - Status: Present, + P: Vec2{math.Float64frombits(x), math.Float64frombits(y)}, + Valid: true, } return nil } func (src Point) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, fmt.Sprintf(`(%s,%s)`, @@ -152,11 +145,8 @@ func (src Point) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Point) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X)) @@ -167,7 +157,7 @@ func (src Point) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Point) Scan(src interface{}) error { if src == nil { - *dst = Point{Status: Null} + *dst = Point{} return nil } @@ -189,19 +179,15 @@ func (src Point) Value() (driver.Value, error) { } func (src Point) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - var buff bytes.Buffer - buff.WriteByte('"') - buff.WriteString(fmt.Sprintf("(%g,%g)", src.P.X, src.P.Y)) - buff.WriteByte('"') - return buff.Bytes(), nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - return nil, errBadStatus + + var buff bytes.Buffer + buff.WriteByte('"') + buff.WriteString(fmt.Sprintf("(%g,%g)", src.P.X, src.P.Y)) + buff.WriteByte('"') + return buff.Bytes(), nil } func (dst *Point) UnmarshalJSON(point []byte) error { diff --git a/point_test.go b/point_test.go index 63f8df07..82f58e17 100644 --- a/point_test.go +++ b/point_test.go @@ -6,13 +6,14 @@ import ( "github.com/jackc/pgtype" "github.com/jackc/pgtype/testutil" + "github.com/stretchr/testify/require" ) func TestPointTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "point", []interface{}{ - &pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Status: pgtype.Present}, - &pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Status: pgtype.Present}, - &pgtype.Point{Status: pgtype.Null}, + &pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true}, + &pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Valid: true}, + &pgtype.Point{}, }) } @@ -20,31 +21,31 @@ func TestPoint_Set(t *testing.T) { tests := []struct { name string arg interface{} - status pgtype.Status + valid bool wantErr bool }{ { name: "first", arg: "(12312.123123,123123.123123)", - status: pgtype.Present, + valid: true, wantErr: false, }, { name: "second", arg: "(1231s2.123123,123123.123123)", - status: pgtype.Undefined, + valid: false, wantErr: true, }, { name: "third", arg: []byte("(122.123123,123.123123)"), - status: pgtype.Present, + valid: true, wantErr: false, }, { name: "third", arg: nil, - status: pgtype.Null, + valid: false, wantErr: false, }, } @@ -54,8 +55,8 @@ func TestPoint_Set(t *testing.T) { if err := dst.Set(tt.arg); (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) } - if dst.Status != tt.status { - t.Errorf("Expected status: %v; got: %v", tt.status, dst.Status) + if dst.Valid != tt.valid { + t.Errorf("Expected status: %v; got: %v", tt.valid, dst.Valid) } }) } @@ -63,46 +64,30 @@ func TestPoint_Set(t *testing.T) { func TestPoint_MarshalJSON(t *testing.T) { tests := []struct { - name string - point pgtype.Point - want []byte - wantErr bool + name string + point pgtype.Point + want []byte }{ - { - name: "first", - point: pgtype.Point{ - P: pgtype.Vec2{}, - Status: pgtype.Undefined, - }, - want: nil, - wantErr: true, - }, { name: "second", point: pgtype.Point{ - P: pgtype.Vec2{X: 12.245, Y: 432.12}, - Status: pgtype.Present, + P: pgtype.Vec2{X: 12.245, Y: 432.12}, + Valid: true, }, - want: []byte(`"(12.245,432.12)"`), - wantErr: false, + want: []byte(`"(12.245,432.12)"`), }, { name: "third", point: pgtype.Point{ - P: pgtype.Vec2{}, - Status: pgtype.Null, + P: pgtype.Vec2{}, }, - want: []byte("null"), - wantErr: false, + want: []byte("null"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.point.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } + require.NoError(t, err) if !reflect.DeepEqual(got, tt.want) { t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want) } @@ -113,25 +98,25 @@ func TestPoint_MarshalJSON(t *testing.T) { func TestPoint_UnmarshalJSON(t *testing.T) { tests := []struct { name string - status pgtype.Status + valid bool arg []byte wantErr bool }{ { name: "first", - status: pgtype.Present, + valid: true, arg: []byte(`"(123.123,54.12)"`), wantErr: false, }, { name: "second", - status: pgtype.Undefined, + valid: false, arg: []byte(`"(123.123,54.1sad2)"`), wantErr: true, }, { name: "third", - status: pgtype.Null, + valid: false, arg: []byte("null"), wantErr: false, }, @@ -142,8 +127,8 @@ func TestPoint_UnmarshalJSON(t *testing.T) { if err := dst.UnmarshalJSON(tt.arg); (err != nil) != tt.wantErr { t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) } - if dst.Status != tt.status { - t.Errorf("Status mismatch: %v != %v", dst.Status, tt.status) + if dst.Valid != tt.valid { + t.Errorf("Valid mismatch: %v != %v", dst.Valid, tt.valid) } }) } diff --git a/polygon.go b/polygon.go index 207cadc0..956920e6 100644 --- a/polygon.go +++ b/polygon.go @@ -12,8 +12,8 @@ import ( ) type Polygon struct { - P []Vec2 - Status Status + P []Vec2 + Valid bool } // Set converts src to dest. @@ -24,7 +24,7 @@ type Polygon struct { // Important that there are no spaces in it. func (dst *Polygon) Set(src interface{}) error { if src == nil { - dst.Status = Null + dst.Valid = false return nil } err := fmt.Errorf("cannot convert %v to Polygon", src) @@ -33,7 +33,7 @@ func (dst *Polygon) Set(src interface{}) error { case string: p, err = stringToPolygon(value) case []Vec2: - p = &Polygon{Status: Present, P: value} + p = &Polygon{Valid: true, P: value} err = nil case []float64: p, err = float64ToPolygon(value) @@ -54,15 +54,14 @@ func stringToPolygon(src string) (*Polygon, error) { } func float64ToPolygon(src []float64) (*Polygon, error) { - p := &Polygon{Status: Null} + p := &Polygon{} if len(src) == 0 { return p, nil } if len(src)%2 != 0 { - p.Status = Undefined return p, fmt.Errorf("invalid length for polygon: %v", len(src)) } - p.Status = Present + p.Valid = true p.P = make([]Vec2, 0) for i := 0; i < len(src); i += 2 { p.P = append(p.P, Vec2{X: src[i], Y: src[i+1]}) @@ -71,14 +70,10 @@ func float64ToPolygon(src []float64) (*Polygon, error) { } func (dst Polygon) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Polygon) AssignTo(dst interface{}) error { @@ -87,7 +82,7 @@ func (src *Polygon) AssignTo(dst interface{}) error { func (dst *Polygon) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Polygon{Status: Null} + *dst = Polygon{} return nil } @@ -123,13 +118,13 @@ func (dst *Polygon) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Polygon{P: points, Status: Present} + *dst = Polygon{P: points, Valid: true} return nil } func (dst *Polygon) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Polygon{Status: Null} + *dst = Polygon{} return nil } @@ -154,18 +149,15 @@ func (dst *Polygon) DecodeBinary(ci *ConnInfo, src []byte) error { } *dst = Polygon{ - P: points, - Status: Present, + P: points, + Valid: true, } return nil } func (src Polygon) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, '(') @@ -184,11 +176,8 @@ func (src Polygon) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Polygon) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendInt32(buf, int32(len(src.P))) @@ -204,7 +193,7 @@ func (src Polygon) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Polygon) Scan(src interface{}) error { if src == nil { - *dst = Polygon{Status: Null} + *dst = Polygon{} return nil } diff --git a/polygon_test.go b/polygon_test.go index 1a139444..34f8d59a 100644 --- a/polygon_test.go +++ b/polygon_test.go @@ -10,14 +10,14 @@ import ( func TestPolygonTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "polygon", []interface{}{ &pgtype.Polygon{ - P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}}, - Status: pgtype.Present, + P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}}, + Valid: true, }, &pgtype.Polygon{ - P: []pgtype.Vec2{{3.14, -1.678}, {7.1, -5.234}, {23.1, 9.34}}, - Status: pgtype.Present, + P: []pgtype.Vec2{{3.14, -1.678}, {7.1, -5.234}, {23.1, 9.34}}, + Valid: true, }, - &pgtype.Polygon{Status: pgtype.Null}, + &pgtype.Polygon{}, }) } @@ -25,53 +25,53 @@ func TestPolygon_Set(t *testing.T) { tests := []struct { name string arg interface{} - status pgtype.Status + valid bool wantErr bool }{ { name: "string", arg: "((3.14,1.678901234),(7.1,5.234),(5.0,3.234))", - status: pgtype.Present, + valid: true, wantErr: false, }, { name: "[]float64", arg: []float64{1, 2, 3.45, 6.78, 1.23, 4.567, 8.9, 1.0}, - status: pgtype.Present, + valid: true, wantErr: false, }, { name: "[]Vec2", arg: []pgtype.Vec2{{1, 2}, {2.3, 4.5}, {6.78, 9.123}}, - status: pgtype.Present, + valid: true, wantErr: false, }, { name: "null", arg: nil, - status: pgtype.Null, + valid: false, wantErr: false, }, { name: "invalid_string_1", arg: "((3.14,1.678901234),(7.1,5.234),(5.0,3.234x))", - status: pgtype.Undefined, + valid: false, wantErr: true, }, { name: "invalid_string_2", arg: "(3,4)", - status: pgtype.Undefined, + valid: false, wantErr: true, }, { name: "invalid_[]float64", arg: []float64{1, 2, 3.45, 6.78, 1.23, 4.567, 8.9}, - status: pgtype.Undefined, + valid: false, wantErr: true, }, { name: "invalid_type", arg: []int{1, 2, 3, 6}, - status: pgtype.Undefined, + valid: false, wantErr: true, }, { name: "empty_[]float64", arg: []float64{}, - status: pgtype.Null, + valid: false, wantErr: false, }, } @@ -81,8 +81,8 @@ func TestPolygon_Set(t *testing.T) { if err := dst.Set(tt.arg); (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) } - if dst.Status != tt.status { - t.Errorf("Expected status: %v; got: %v", tt.status, dst.Status) + if dst.Valid != tt.valid { + t.Errorf("Expected valid: %v; got: %v", tt.valid, dst.Valid) } }) } diff --git a/qchar.go b/qchar.go index 574f6066..e56bf142 100644 --- a/qchar.go +++ b/qchar.go @@ -18,13 +18,13 @@ import ( // addition, database/sql Scanner and database/sql/driver Value are not // implemented. type QChar struct { - Int int8 - Status Status + Int int8 + Valid bool } func (dst *QChar) Set(src interface{}) error { if src == nil { - *dst = QChar{Status: Null} + *dst = QChar{} return nil } @@ -37,12 +37,12 @@ func (dst *QChar) Set(src interface{}) error { switch value := src.(type) { case int8: - *dst = QChar{Int: value, Status: Present} + *dst = QChar{Int: value, Valid: true} case uint8: if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case int16: if value < math.MinInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) @@ -50,12 +50,12 @@ func (dst *QChar) Set(src interface{}) error { if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case uint16: if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case int32: if value < math.MinInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) @@ -63,12 +63,12 @@ func (dst *QChar) Set(src interface{}) error { if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case uint32: if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case int64: if value < math.MinInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) @@ -76,12 +76,12 @@ func (dst *QChar) Set(src interface{}) error { if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case uint64: if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case int: if value < math.MinInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) @@ -89,18 +89,18 @@ func (dst *QChar) Set(src interface{}) error { if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case uint: if value > math.MaxInt8 { return fmt.Errorf("%d is greater than maximum value for QChar", value) } - *dst = QChar{Int: int8(value), Status: Present} + *dst = QChar{Int: int8(value), Valid: true} case string: num, err := strconv.ParseInt(value, 10, 8) if err != nil { return err } - *dst = QChar{Int: int8(num), Status: Present} + *dst = QChar{Int: int8(num), Valid: true} default: if originalSrc, ok := underlyingNumberType(src); ok { return dst.Set(originalSrc) @@ -112,23 +112,19 @@ func (dst *QChar) Set(src interface{}) error { } func (dst QChar) Get() interface{} { - switch dst.Status { - case Present: - return dst.Int - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Int } func (src *QChar) AssignTo(dst interface{}) error { - return int64AssignTo(int64(src.Int), src.Status, dst) + return int64AssignTo(int64(src.Int), src.Valid, dst) } func (dst *QChar) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = QChar{Status: Null} + *dst = QChar{} return nil } @@ -136,16 +132,13 @@ func (dst *QChar) DecodeBinary(ci *ConnInfo, src []byte) error { return fmt.Errorf(`invalid length for "char": %v`, len(src)) } - *dst = QChar{Int: int8(src[0]), Status: Present} + *dst = QChar{Int: int8(src[0]), Valid: true} return nil } func (src QChar) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, byte(src.Int)), nil diff --git a/qchar_test.go b/qchar_test.go index 4b60339c..eb54bf65 100644 --- a/qchar_test.go +++ b/qchar_test.go @@ -11,12 +11,12 @@ import ( func TestQCharTranscode(t *testing.T) { testutil.TestPgxSuccessfulTranscodeEqFunc(t, `"char"`, []interface{}{ - &pgtype.QChar{Int: math.MinInt8, Status: pgtype.Present}, - &pgtype.QChar{Int: -1, Status: pgtype.Present}, - &pgtype.QChar{Int: 0, Status: pgtype.Present}, - &pgtype.QChar{Int: 1, Status: pgtype.Present}, - &pgtype.QChar{Int: math.MaxInt8, Status: pgtype.Present}, - &pgtype.QChar{Int: 0, Status: pgtype.Null}, + &pgtype.QChar{Int: math.MinInt8, Valid: true}, + &pgtype.QChar{Int: -1, Valid: true}, + &pgtype.QChar{Int: 0, Valid: true}, + &pgtype.QChar{Int: 1, Valid: true}, + &pgtype.QChar{Int: math.MaxInt8, Valid: true}, + &pgtype.QChar{Int: 0}, }, func(a, b interface{}) bool { return reflect.DeepEqual(a, b) }) @@ -27,20 +27,20 @@ func TestQCharSet(t *testing.T) { source interface{} result pgtype.QChar }{ - {source: int8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int16(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int32(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int64(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: int8(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int16(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int32(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: int64(-1), result: pgtype.QChar{Int: -1, Status: pgtype.Present}}, - {source: uint8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint16(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint32(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: uint64(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: "1", result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, - {source: _int8(1), result: pgtype.QChar{Int: 1, Status: pgtype.Present}}, + {source: int8(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: int16(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: int32(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: int64(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: int8(-1), result: pgtype.QChar{Int: -1, Valid: true}}, + {source: int16(-1), result: pgtype.QChar{Int: -1, Valid: true}}, + {source: int32(-1), result: pgtype.QChar{Int: -1, Valid: true}}, + {source: int64(-1), result: pgtype.QChar{Int: -1, Valid: true}}, + {source: uint8(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: uint16(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: uint32(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: uint64(1), result: pgtype.QChar{Int: 1, Valid: true}}, + {source: "1", result: pgtype.QChar{Int: 1, Valid: true}}, + {source: _int8(1), result: pgtype.QChar{Int: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -76,19 +76,19 @@ func TestQCharAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &i8, expected: int8(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &i16, expected: int16(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &i32, expected: int32(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &i64, expected: int64(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &i, expected: int(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &ui8, expected: uint8(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &ui16, expected: uint16(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &ui64, expected: uint64(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &ui, expected: uint(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &_i8, expected: _int8(42)}, + {src: pgtype.QChar{Int: 0}, dst: &pi8, expected: ((*int8)(nil))}, + {src: pgtype.QChar{Int: 0}, dst: &_pi8, expected: ((*_int8)(nil))}, } for i, tt := range simpleTests { @@ -107,8 +107,8 @@ func TestQCharAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)}, - {src: pgtype.QChar{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &pi8, expected: int8(42)}, + {src: pgtype.QChar{Int: 42, Valid: true}, dst: &_pi8, expected: _int8(42)}, } for i, tt := range pointerAllocTests { @@ -126,12 +126,12 @@ func TestQCharAssignTo(t *testing.T) { src pgtype.QChar dst interface{} }{ - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui8}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui16}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui32}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui64}, - {src: pgtype.QChar{Int: -1, Status: pgtype.Present}, dst: &ui}, - {src: pgtype.QChar{Int: 0, Status: pgtype.Null}, dst: &i16}, + {src: pgtype.QChar{Int: -1, Valid: true}, dst: &ui8}, + {src: pgtype.QChar{Int: -1, Valid: true}, dst: &ui16}, + {src: pgtype.QChar{Int: -1, Valid: true}, dst: &ui32}, + {src: pgtype.QChar{Int: -1, Valid: true}, dst: &ui64}, + {src: pgtype.QChar{Int: -1, Valid: true}, dst: &ui}, + {src: pgtype.QChar{Int: 0}, dst: &i16}, } for i, tt := range errorTests { diff --git a/record.go b/record.go index 718c3570..20b119c6 100644 --- a/record.go +++ b/record.go @@ -12,12 +12,12 @@ import ( // PostgreSQL does not support input of generic records. type Record struct { Fields []Value - Status Status + Valid bool } func (dst *Record) Set(src interface{}) error { if src == nil { - *dst = Record{Status: Null} + *dst = Record{} return nil } @@ -30,7 +30,7 @@ func (dst *Record) Set(src interface{}) error { switch value := src.(type) { case []Value: - *dst = Record{Fields: value, Status: Present} + *dst = Record{Fields: value, Valid: true} default: return fmt.Errorf("cannot convert %v to Record", src) } @@ -39,41 +39,34 @@ func (dst *Record) Set(src interface{}) error { } func (dst Record) Get() interface{} { - switch dst.Status { - case Present: - return dst.Fields - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Fields } func (src *Record) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[]Value: - *v = make([]Value, len(src.Fields)) - copy(*v, src.Fields) - return nil - case *[]interface{}: - *v = make([]interface{}, len(src.Fields)) - for i := range *v { - (*v)[i] = src.Fields[i].Get() - } - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *[]Value: + *v = make([]Value, len(src.Fields)) + copy(*v, src.Fields) + return nil + case *[]interface{}: + *v = make([]interface{}, len(src.Fields)) + for i := range *v { + (*v)[i] = src.Fields[i].Get() + } + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func prepareNewBinaryDecoder(ci *ConnInfo, fieldOID uint32, v *Value) (BinaryDecoder, error) { @@ -97,7 +90,7 @@ func prepareNewBinaryDecoder(ci *ConnInfo, fieldOID uint32, v *Value) (BinaryDec func (dst *Record) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Record{Status: Null} + *dst = Record{} return nil } @@ -120,7 +113,7 @@ func (dst *Record) DecodeBinary(ci *ConnInfo, src []byte) error { return scanner.Err() } - *dst = Record{Fields: fields, Status: Present} + *dst = Record{Fields: fields, Valid: true} return nil } diff --git a/record_test.go b/record_test.go index 240812a6..c8e7d4b7 100644 --- a/record_test.go +++ b/record_test.go @@ -19,63 +19,61 @@ var recordTests = []struct { sql: `select row()`, expected: pgtype.Record{ Fields: []pgtype.Value{}, - Status: pgtype.Present, + Valid: true, }, }, { sql: `select row('foo'::text, 42::int4)`, expected: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, + &pgtype.Text{String: "foo", Valid: true}, + &pgtype.Int4{Int: 42, Valid: true}, }, - Status: pgtype.Present, + Valid: true, }, }, { sql: `select row(100.0::float4, 1.09::float4)`, expected: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Float4{Float: 100, Status: pgtype.Present}, - &pgtype.Float4{Float: 1.09, Status: pgtype.Present}, + &pgtype.Float4{Float: 100, Valid: true}, + &pgtype.Float4{Float: 1.09, Valid: true}, }, - Status: pgtype.Present, + Valid: true, }, }, { sql: `select row('foo'::text, array[1, 2, null, 4]::int4[], 42::int4)`, expected: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, + &pgtype.Text{String: "foo", Valid: true}, &pgtype.Int4Array{ Elements: []pgtype.Int4{ - {Int: 1, Status: pgtype.Present}, - {Int: 2, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Int: 4, Status: pgtype.Present}, + {Int: 1, Valid: true}, + {Int: 2, Valid: true}, + {}, + {Int: 4, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 4, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, + &pgtype.Int4{Int: 42, Valid: true}, }, - Status: pgtype.Present, + Valid: true, }, }, { sql: `select row(null)`, expected: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Unknown{Status: pgtype.Null}, + &pgtype.Unknown{}, }, - Status: pgtype.Present, + Valid: true, }, }, { - sql: `select null::record`, - expected: pgtype.Record{ - Status: pgtype.Null, - }, + sql: `select null::record`, + expected: pgtype.Record{}, }, } @@ -139,35 +137,35 @@ func TestRecordAssignTo(t *testing.T) { { src: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, + &pgtype.Text{String: "foo", Valid: true}, + &pgtype.Int4{Int: 42, Valid: true}, }, - Status: pgtype.Present, + Valid: true, }, dst: &valueSlice, expected: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, + &pgtype.Text{String: "foo", Valid: true}, + &pgtype.Int4{Int: 42, Valid: true}, }, }, { src: pgtype.Record{ Fields: []pgtype.Value{ - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Int4{Int: 42, Status: pgtype.Present}, + &pgtype.Text{String: "foo", Valid: true}, + &pgtype.Int4{Int: 42, Valid: true}, }, - Status: pgtype.Present, + Valid: true, }, dst: &interfaceSlice, expected: []interface{}{"foo", int32(42)}, }, { - src: pgtype.Record{Status: pgtype.Null}, + src: pgtype.Record{}, dst: &valueSlice, expected: (([]pgtype.Value)(nil)), }, { - src: pgtype.Record{Status: pgtype.Null}, + src: pgtype.Record{}, dst: &interfaceSlice, expected: (([]interface{})(nil)), }, diff --git a/text.go b/text.go index a01815d9..5d27c44f 100644 --- a/text.go +++ b/text.go @@ -8,12 +8,12 @@ import ( type Text struct { String string - Status Status + Valid bool } func (dst *Text) Set(src interface{}) error { if src == nil { - *dst = Text{Status: Null} + *dst = Text{} return nil } @@ -26,24 +26,24 @@ func (dst *Text) Set(src interface{}) error { switch value := src.(type) { case string: - *dst = Text{String: value, Status: Present} + *dst = Text{String: value, Valid: true} case *string: if value == nil { - *dst = Text{Status: Null} + *dst = Text{} } else { - *dst = Text{String: *value, Status: Present} + *dst = Text{String: *value, Valid: true} } case []byte: if value == nil { - *dst = Text{Status: Null} + *dst = Text{} } else { - *dst = Text{String: string(value), Status: Present} + *dst = Text{String: string(value), Valid: true} } case fmt.Stringer: if value == fmt.Stringer(nil) { - *dst = Text{Status: Null} + *dst = Text{} } else { - *dst = Text{String: value.String(), Status: Present} + *dst = Text{String: value.String(), Valid: true} } default: // Cannot be part of the switch: If Value() returns nil on @@ -54,7 +54,7 @@ func (dst *Text) Set(src interface{}) error { // pointer receiver and fmt.Stringer with value receiver. if value, ok := src.(driver.Valuer); ok { if value == driver.Valuer(nil) { - *dst = Text{Status: Null} + *dst = Text{} return nil } else { v, err := value.Value() @@ -64,7 +64,7 @@ func (dst *Text) Set(src interface{}) error { // Handles also v == nil case. if s, ok := v.(string); ok { - *dst = Text{String: s, Status: Present} + *dst = Text{String: s, Valid: true} return nil } } @@ -80,38 +80,31 @@ func (dst *Text) Set(src interface{}) error { } func (dst Text) Get() interface{} { - switch dst.Status { - case Present: - return dst.String - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.String } func (src *Text) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *string: - *v = src.String - return nil - case *[]byte: - *v = make([]byte, len(src.String)) - copy(*v, src.String) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *string: + *v = src.String + return nil + case *[]byte: + *v = make([]byte, len(src.String)) + copy(*v, src.String) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (Text) PreferredResultFormat() int16 { @@ -120,11 +113,11 @@ func (Text) PreferredResultFormat() int16 { func (dst *Text) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Text{Status: Null} + *dst = Text{} return nil } - *dst = Text{String: string(src), Status: Present} + *dst = Text{String: string(src), Valid: true} return nil } @@ -137,11 +130,8 @@ func (Text) PreferredParamFormat() int16 { } func (src Text) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.String...), nil @@ -154,7 +144,7 @@ func (src Text) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Text) Scan(src interface{}) error { if src == nil { - *dst = Text{Status: Null} + *dst = Text{} return nil } @@ -172,27 +162,18 @@ func (dst *Text) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Text) Value() (driver.Value, error) { - switch src.Status { - case Present: - return src.String, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + return src.String, nil } func (src Text) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - return json.Marshal(src.String) - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - return nil, errBadStatus + return json.Marshal(src.String) } func (dst *Text) UnmarshalJSON(b []byte) error { @@ -203,9 +184,9 @@ func (dst *Text) UnmarshalJSON(b []byte) error { } if s == nil { - *dst = Text{Status: Null} + *dst = Text{} } else { - *dst = Text{String: *s, Status: Present} + *dst = Text{String: *s, Valid: true} } return nil diff --git a/text_array.go b/text_array.go index 2461966b..7fcc1c4d 100644 --- a/text_array.go +++ b/text_array.go @@ -14,13 +14,13 @@ import ( type TextArray struct { Elements []Text Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *TextArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} return nil } @@ -36,9 +36,9 @@ func (dst *TextArray) Set(src interface{}) error { case []string: if value == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} } else if len(value) == 0 { - *dst = TextArray{Status: Present} + *dst = TextArray{Valid: true} } else { elements := make([]Text, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *TextArray) Set(src interface{}) error { *dst = TextArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} } else if len(value) == 0 { - *dst = TextArray{Status: Present} + *dst = TextArray{Valid: true} } else { elements := make([]Text, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *TextArray) Set(src interface{}) error { *dst = TextArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Text: if value == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} } else if len(value) == 0 { - *dst = TextArray{Status: Present} + *dst = TextArray{Valid: true} } else { *dst = TextArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *TextArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = TextArray{Status: Null} + *dst = TextArray{} return nil } @@ -99,7 +99,7 @@ func (dst *TextArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for TextArray", src) } if elementsLength == 0 { - *dst = TextArray{Status: Present} + *dst = TextArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *TextArray) Set(src interface{}) error { *dst = TextArray{ Elements: make([]Text, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *TextArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst TextArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TextArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *TextArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *TextArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *TextArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} return nil } @@ -337,14 +330,14 @@ func (dst *TextArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = TextArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = TextArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *TextArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TextArray{Status: Null} + *dst = TextArray{} return nil } @@ -355,7 +348,7 @@ func (dst *TextArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = TextArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TextArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *TextArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = TextArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TextArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src TextArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src TextArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src TextArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src TextArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/text_array_test.go b/text_array_test.go index a5d050f6..4caeb692 100644 --- a/text_array_test.go +++ b/text_array_test.go @@ -16,8 +16,8 @@ func TestTextArrayDecodeTextNull(t *testing.T) { err := textArray.DecodeText(nil, []byte(`{abc,"NULL",NULL,def}`)) require.NoError(t, err) require.Len(t, textArray.Elements, 4) - assert.Equal(t, pgtype.Present, textArray.Elements[1].Status) - assert.Equal(t, pgtype.Null, textArray.Elements[2].Status) + assert.Equal(t, true, textArray.Elements[1].Valid) + assert.Equal(t, false, textArray.Elements[2].Valid) } func TestTextArrayTranscode(t *testing.T) { @@ -25,41 +25,41 @@ func TestTextArrayTranscode(t *testing.T) { &pgtype.TextArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {Status: pgtype.Null}, + {String: "foo", Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.TextArray{Status: pgtype.Null}, + &pgtype.TextArray{}, &pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "bar ", Status: pgtype.Present}, - {String: "NuLL", Status: pgtype.Present}, - {String: `wow"quz\`, Status: pgtype.Present}, - {String: "", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "null", Status: pgtype.Present}, + {String: "bar ", Valid: true}, + {String: "NuLL", Valid: true}, + {String: `wow"quz\`, Valid: true}, + {String: "", Valid: true}, + {}, + {String: "null", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "quz", Status: pgtype.Present}, - {String: "foo", Status: pgtype.Present}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "quz", Valid: true}, + {String: "foo", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -72,61 +72,61 @@ func TestTextArraySet(t *testing.T) { { source: []string{"foo"}, result: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]string)(nil)), - result: pgtype.TextArray{Status: pgtype.Null}, + result: pgtype.TextArray{}, }, { source: [][]string{{"foo"}, {"bar"}}, result: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]string{{"foo"}, {"bar"}}, result: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -159,81 +159,81 @@ func TestTextArrayAssignTo(t *testing.T) { }{ { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, expected: []string{"foo"}, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedStringSlice, expected: _stringSlice{"bar"}, }, { - src: pgtype.TextArray{Status: pgtype.Null}, + src: pgtype.TextArray{}, dst: &stringSlice, expected: (([]string)(nil)), }, { - src: pgtype.TextArray{Status: pgtype.Present}, + src: pgtype.TextArray{Valid: true}, dst: &stringSlice, expected: []string{}, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim2, expected: [][]string{{"foo"}, {"bar"}}, }, { src: pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim4, expected: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, expected: [2][1]string{{"foo"}, {"bar"}}, }, { src: pgtype.TextArray{ Elements: []pgtype.Text{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, expected: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, @@ -256,31 +256,31 @@ func TestTextArrayAssignTo(t *testing.T) { }{ { src: pgtype.TextArray{ - Elements: []pgtype.Text{{Status: pgtype.Null}}, + Elements: []pgtype.Text{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSlice, }, { src: pgtype.TextArray{ - Elements: []pgtype.Text{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Text{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, }, } diff --git a/text_test.go b/text_test.go index cca3a05d..5f34f8c0 100644 --- a/text_test.go +++ b/text_test.go @@ -12,9 +12,9 @@ import ( func TestTextTranscode(t *testing.T) { for _, pgTypeName := range []string{"text", "varchar"} { testutil.TestSuccessfulTranscode(t, pgTypeName, []interface{}{ - &pgtype.Text{String: "", Status: pgtype.Present}, - &pgtype.Text{String: "foo", Status: pgtype.Present}, - &pgtype.Text{Status: pgtype.Null}, + &pgtype.Text{String: "", Valid: true}, + &pgtype.Text{String: "foo", Valid: true}, + &pgtype.Text{}, }) } } @@ -24,9 +24,9 @@ func TestTextSet(t *testing.T) { source interface{} result pgtype.Text }{ - {source: "foo", result: pgtype.Text{String: "foo", Status: pgtype.Present}}, - {source: _string("bar"), result: pgtype.Text{String: "bar", Status: pgtype.Present}}, - {source: (*string)(nil), result: pgtype.Text{Status: pgtype.Null}}, + {source: "foo", result: pgtype.Text{String: "foo", Valid: true}}, + {source: _string("bar"), result: pgtype.Text{String: "bar", Valid: true}}, + {source: (*string)(nil), result: pgtype.Text{}}, } for i, tt := range successfulTests { @@ -51,8 +51,8 @@ func TestTextAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &s, expected: "foo"}, - {src: pgtype.Text{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, + {src: pgtype.Text{String: "foo", Valid: true}, dst: &s, expected: "foo"}, + {src: pgtype.Text{}, dst: &ps, expected: ((*string)(nil))}, } for i, tt := range stringTests { @@ -73,8 +73,8 @@ func TestTextAssignTo(t *testing.T) { dst *[]byte expected []byte }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &buf, expected: []byte("foo")}, - {src: pgtype.Text{Status: pgtype.Null}, dst: &buf, expected: nil}, + {src: pgtype.Text{String: "foo", Valid: true}, dst: &buf, expected: []byte("foo")}, + {src: pgtype.Text{}, dst: &buf, expected: nil}, } for i, tt := range bytesTests { @@ -93,7 +93,7 @@ func TestTextAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &ps, expected: "foo"}, + {src: pgtype.Text{String: "foo", Valid: true}, dst: &ps, expected: "foo"}, } for i, tt := range pointerAllocTests { @@ -111,7 +111,7 @@ func TestTextAssignTo(t *testing.T) { src pgtype.Text dst interface{} }{ - {src: pgtype.Text{Status: pgtype.Null}, dst: &s}, + {src: pgtype.Text{}, dst: &s}, } for i, tt := range errorTests { @@ -127,8 +127,8 @@ func TestTextMarshalJSON(t *testing.T) { source pgtype.Text result string }{ - {source: pgtype.Text{String: "", Status: pgtype.Null}, result: "null"}, - {source: pgtype.Text{String: "a", Status: pgtype.Present}, result: "\"a\""}, + {source: pgtype.Text{String: ""}, result: "null"}, + {source: pgtype.Text{String: "a", Valid: true}, result: "\"a\""}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -147,8 +147,8 @@ func TestTextUnmarshalJSON(t *testing.T) { source string result pgtype.Text }{ - {source: "null", result: pgtype.Text{String: "", Status: pgtype.Null}}, - {source: "\"a\"", result: pgtype.Text{String: "a", Status: pgtype.Present}}, + {source: "null", result: pgtype.Text{String: ""}}, + {source: "\"a\"", result: pgtype.Text{String: "a", Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Text diff --git a/tid.go b/tid.go index 4bb57f64..0108d219 100644 --- a/tid.go +++ b/tid.go @@ -24,7 +24,7 @@ import ( type TID struct { BlockNumber uint32 OffsetNumber uint16 - Status Status + Valid bool } func (dst *TID) Set(src interface{}) error { @@ -32,36 +32,32 @@ func (dst *TID) Set(src interface{}) error { } func (dst TID) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TID) AssignTo(dst interface{}) error { - if src.Status == Present { - switch v := dst.(type) { - case *string: - *v = fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } + if !src.Valid { + return fmt.Errorf("cannot assign %v to %T", src, dst) } - return fmt.Errorf("cannot assign %v to %T", src, dst) + switch v := dst.(type) { + case *string: + *v = fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *TID) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TID{Status: Null} + *dst = TID{} return nil } @@ -84,13 +80,13 @@ func (dst *TID) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = TID{BlockNumber: uint32(blockNumber), OffsetNumber: uint16(offsetNumber), Status: Present} + *dst = TID{BlockNumber: uint32(blockNumber), OffsetNumber: uint16(offsetNumber), Valid: true} return nil } func (dst *TID) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TID{Status: Null} + *dst = TID{} return nil } @@ -101,17 +97,14 @@ func (dst *TID) DecodeBinary(ci *ConnInfo, src []byte) error { *dst = TID{ BlockNumber: binary.BigEndian.Uint32(src), OffsetNumber: binary.BigEndian.Uint16(src[4:]), - Status: Present, + Valid: true, } return nil } func (src TID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = append(buf, fmt.Sprintf(`(%d,%d)`, src.BlockNumber, src.OffsetNumber)...) @@ -119,11 +112,8 @@ func (src TID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src TID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendUint32(buf, src.BlockNumber) @@ -134,7 +124,7 @@ func (src TID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *TID) Scan(src interface{}) error { if src == nil { - *dst = TID{Status: Null} + *dst = TID{} return nil } diff --git a/tid_test.go b/tid_test.go index 818be8af..fcf93259 100644 --- a/tid_test.go +++ b/tid_test.go @@ -10,9 +10,9 @@ import ( func TestTIDTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "tid", []interface{}{ - &pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Status: pgtype.Present}, - &pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Status: pgtype.Present}, - &pgtype.TID{Status: pgtype.Null}, + &pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true}, + &pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true}, + &pgtype.TID{}, }) } @@ -25,8 +25,8 @@ func TestTIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Status: pgtype.Present}, dst: &s, expected: "(42,43)"}, - {src: pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Status: pgtype.Present}, dst: &s, expected: "(4294967295,65535)"}, + {src: pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true}, dst: &s, expected: "(42,43)"}, + {src: pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true}, dst: &s, expected: "(4294967295,65535)"}, } for i, tt := range simpleTests { @@ -45,8 +45,8 @@ func TestTIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Status: pgtype.Present}, dst: &sp, expected: "(42,43)"}, - {src: pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Status: pgtype.Present}, dst: &sp, expected: "(4294967295,65535)"}, + {src: pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true}, dst: &sp, expected: "(42,43)"}, + {src: pgtype.TID{BlockNumber: 4294967295, OffsetNumber: 65535, Valid: true}, dst: &sp, expected: "(4294967295,65535)"}, } for i, tt := range pointerAllocTests { @@ -60,4 +60,3 @@ func TestTIDAssignTo(t *testing.T) { } } } - diff --git a/time.go b/time.go index f7a28870..3252a633 100644 --- a/time.go +++ b/time.go @@ -17,13 +17,13 @@ import ( // to needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day. type Time struct { Microseconds int64 // Number of microseconds since midnight - Status Status + Valid bool } // Set converts src into a Time and stores in dst. func (dst *Time) Set(src interface{}) error { if src == nil { - *dst = Time{Status: Null} + *dst = Time{} return nil } @@ -40,10 +40,10 @@ func (dst *Time) Set(src interface{}) error { int64(value.Minute())*microsecondsPerMinute + int64(value.Second())*microsecondsPerSecond + int64(value.Nanosecond())/1000 - *dst = Time{Microseconds: usec, Status: Present} + *dst = Time{Microseconds: usec, Valid: true} case *time.Time: if value == nil { - *dst = Time{Status: Null} + *dst = Time{} } else { return dst.Set(*value) } @@ -58,54 +58,47 @@ func (dst *Time) Set(src interface{}) error { } func (dst Time) Get() interface{} { - switch dst.Status { - case Present: - return dst.Microseconds - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Microseconds } func (src *Time) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - // 24:00:00 is max allowed time in PostgreSQL, but time.Time will normalize that to 00:00:00 the next day. - var maxRepresentableByTime int64 = 24*60*60*1000000 - 1 - if src.Microseconds > maxRepresentableByTime { - return fmt.Errorf("%d microseconds cannot be represented as time.Time", src.Microseconds) - } - - usec := src.Microseconds - hours := usec / microsecondsPerHour - usec -= hours * microsecondsPerHour - minutes := usec / microsecondsPerMinute - usec -= minutes * microsecondsPerMinute - seconds := usec / microsecondsPerSecond - usec -= seconds * microsecondsPerSecond - ns := usec * 1000 - *v = time.Date(2000, 1, 1, int(hours), int(minutes), int(seconds), int(ns), time.UTC) - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *time.Time: + // 24:00:00 is max allowed time in PostgreSQL, but time.Time will normalize that to 00:00:00 the next day. + var maxRepresentableByTime int64 = 24*60*60*1000000 - 1 + if src.Microseconds > maxRepresentableByTime { + return fmt.Errorf("%d microseconds cannot be represented as time.Time", src.Microseconds) + } + + usec := src.Microseconds + hours := usec / microsecondsPerHour + usec -= hours * microsecondsPerHour + minutes := usec / microsecondsPerMinute + usec -= minutes * microsecondsPerMinute + seconds := usec / microsecondsPerSecond + usec -= seconds * microsecondsPerSecond + ns := usec * 1000 + *v = time.Date(2000, 1, 1, int(hours), int(minutes), int(seconds), int(ns), time.UTC) + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } // DecodeText decodes from src into dst. func (dst *Time) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Time{Status: Null} + *dst = Time{} return nil } @@ -147,7 +140,7 @@ func (dst *Time) DecodeText(ci *ConnInfo, src []byte) error { usec += n } - *dst = Time{Microseconds: usec, Status: Present} + *dst = Time{Microseconds: usec, Valid: true} return nil } @@ -155,7 +148,7 @@ func (dst *Time) DecodeText(ci *ConnInfo, src []byte) error { // DecodeBinary decodes from src into dst. func (dst *Time) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Time{Status: Null} + *dst = Time{} return nil } @@ -164,18 +157,15 @@ func (dst *Time) DecodeBinary(ci *ConnInfo, src []byte) error { } usec := int64(binary.BigEndian.Uint64(src)) - *dst = Time{Microseconds: usec, Status: Present} + *dst = Time{Microseconds: usec, Valid: true} return nil } // EncodeText writes the text encoding of src into w. func (src Time) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } usec := src.Microseconds @@ -194,11 +184,8 @@ func (src Time) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // EncodeBinary writes the binary encoding of src into w. If src.Time is not in // the UTC time zone it returns an error. func (src Time) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return pgio.AppendInt64(buf, src.Microseconds), nil @@ -207,7 +194,7 @@ func (src Time) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Time) Scan(src interface{}) error { if src == nil { - *dst = Time{Status: Null} + *dst = Time{} return nil } diff --git a/time_test.go b/time_test.go index 09ca3c4d..4a989375 100644 --- a/time_test.go +++ b/time_test.go @@ -11,11 +11,11 @@ import ( func TestTimeTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "time", []interface{}{ - &pgtype.Time{Microseconds: 0, Status: pgtype.Present}, - &pgtype.Time{Microseconds: 1, Status: pgtype.Present}, - &pgtype.Time{Microseconds: 86399999999, Status: pgtype.Present}, - &pgtype.Time{Microseconds: 86400000000, Status: pgtype.Present}, - &pgtype.Time{Status: pgtype.Null}, + &pgtype.Time{Microseconds: 0, Valid: true}, + &pgtype.Time{Microseconds: 1, Valid: true}, + &pgtype.Time{Microseconds: 86399999999, Valid: true}, + &pgtype.Time{Microseconds: 86400000000, Valid: true}, + &pgtype.Time{}, }) } @@ -26,18 +26,18 @@ func TestTimeSet(t *testing.T) { source interface{} result pgtype.Time }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 0, Status: pgtype.Present}}, - {source: time.Date(1900, 1, 1, 1, 0, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 3600000000, Status: pgtype.Present}}, - {source: time.Date(1900, 1, 1, 0, 1, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 60000000, Status: pgtype.Present}}, - {source: time.Date(1900, 1, 1, 0, 0, 1, 0, time.UTC), result: pgtype.Time{Microseconds: 1000000, Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 1, time.UTC), result: pgtype.Time{Microseconds: 0, Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 1000, time.UTC), result: pgtype.Time{Microseconds: 1, Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 23, 59, 59, 999999999, time.UTC), result: pgtype.Time{Microseconds: 86399999999, Status: pgtype.Present}}, - {source: time.Date(2015, 1, 1, 0, 0, 0, 2000, time.Local), result: pgtype.Time{Microseconds: 2, Status: pgtype.Present}}, - {source: func(t time.Time) *time.Time { return &t }(time.Date(2015, 1, 1, 0, 0, 0, 2000, time.Local)), result: pgtype.Time{Microseconds: 2, Status: pgtype.Present}}, - {source: nil, result: pgtype.Time{Status: pgtype.Null}}, - {source: (*time.Time)(nil), result: pgtype.Time{Status: pgtype.Null}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 3000, time.UTC)), result: pgtype.Time{Microseconds: 3, Status: pgtype.Present}}, + {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 0, Valid: true}}, + {source: time.Date(1900, 1, 1, 1, 0, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 3600000000, Valid: true}}, + {source: time.Date(1900, 1, 1, 0, 1, 0, 0, time.UTC), result: pgtype.Time{Microseconds: 60000000, Valid: true}}, + {source: time.Date(1900, 1, 1, 0, 0, 1, 0, time.UTC), result: pgtype.Time{Microseconds: 1000000, Valid: true}}, + {source: time.Date(1970, 1, 1, 0, 0, 0, 1, time.UTC), result: pgtype.Time{Microseconds: 0, Valid: true}}, + {source: time.Date(1970, 1, 1, 0, 0, 0, 1000, time.UTC), result: pgtype.Time{Microseconds: 1, Valid: true}}, + {source: time.Date(1999, 12, 31, 23, 59, 59, 999999999, time.UTC), result: pgtype.Time{Microseconds: 86399999999, Valid: true}}, + {source: time.Date(2015, 1, 1, 0, 0, 0, 2000, time.Local), result: pgtype.Time{Microseconds: 2, Valid: true}}, + {source: func(t time.Time) *time.Time { return &t }(time.Date(2015, 1, 1, 0, 0, 0, 2000, time.Local)), result: pgtype.Time{Microseconds: 2, Valid: true}}, + {source: nil, result: pgtype.Time{}}, + {source: (*time.Time)(nil), result: pgtype.Time{}}, + {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 3000, time.UTC)), result: pgtype.Time{Microseconds: 3, Valid: true}}, } for i, tt := range successfulTests { @@ -62,13 +62,13 @@ func TestTimeAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Time{Microseconds: 0, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)}, - {src: pgtype.Time{Microseconds: 3600000000, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 1, 0, 0, 0, time.UTC)}, - {src: pgtype.Time{Microseconds: 60000000, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 1, 0, 0, time.UTC)}, - {src: pgtype.Time{Microseconds: 1000000, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC)}, - {src: pgtype.Time{Microseconds: 1, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 0, 1000, time.UTC)}, - {src: pgtype.Time{Microseconds: 86399999999, Status: pgtype.Present}, dst: &tim, expected: time.Date(2000, 1, 1, 23, 59, 59, 999999000, time.UTC)}, - {src: pgtype.Time{Microseconds: 0, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, + {src: pgtype.Time{Microseconds: 0, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)}, + {src: pgtype.Time{Microseconds: 3600000000, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 1, 0, 0, 0, time.UTC)}, + {src: pgtype.Time{Microseconds: 60000000, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 1, 0, 0, time.UTC)}, + {src: pgtype.Time{Microseconds: 1000000, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC)}, + {src: pgtype.Time{Microseconds: 1, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 0, 0, 0, 1000, time.UTC)}, + {src: pgtype.Time{Microseconds: 86399999999, Valid: true}, dst: &tim, expected: time.Date(2000, 1, 1, 23, 59, 59, 999999000, time.UTC)}, + {src: pgtype.Time{Microseconds: 0}, dst: &ptim, expected: ((*time.Time)(nil))}, } for i, tt := range simpleTests { @@ -87,7 +87,7 @@ func TestTimeAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Time{Microseconds: 0, Status: pgtype.Present}, dst: &ptim, expected: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)}, + {src: pgtype.Time{Microseconds: 0, Valid: true}, dst: &ptim, expected: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)}, } for i, tt := range pointerAllocTests { @@ -105,7 +105,7 @@ func TestTimeAssignTo(t *testing.T) { src pgtype.Time dst interface{} }{ - {src: pgtype.Time{Microseconds: 86400000000, Status: pgtype.Present}, dst: &tim}, + {src: pgtype.Time{Microseconds: 86400000000, Valid: true}, dst: &tim}, } for i, tt := range errorTests { diff --git a/timestamp.go b/timestamp.go index 5517acb1..882cd41a 100644 --- a/timestamp.go +++ b/timestamp.go @@ -18,7 +18,7 @@ const pgTimestampFormat = "2006-01-02 15:04:05.999999999" // convert to UTC or return an error on non-UTC times. type Timestamp struct { Time time.Time // Time must always be in UTC. - Status Status + Valid bool InfinityModifier InfinityModifier } @@ -26,7 +26,7 @@ type Timestamp struct { // time.Time in a non-UTC time zone, the time zone is discarded. func (dst *Timestamp) Set(src interface{}) error { if src == nil { - *dst = Timestamp{Status: Null} + *dst = Timestamp{} return nil } @@ -39,15 +39,15 @@ func (dst *Timestamp) Set(src interface{}) error { switch value := src.(type) { case time.Time: - *dst = Timestamp{Time: time.Date(value.Year(), value.Month(), value.Day(), value.Hour(), value.Minute(), value.Second(), value.Nanosecond(), time.UTC), Status: Present} + *dst = Timestamp{Time: time.Date(value.Year(), value.Month(), value.Day(), value.Hour(), value.Minute(), value.Second(), value.Nanosecond(), time.UTC), Valid: true} case *time.Time: if value == nil { - *dst = Timestamp{Status: Null} + *dst = Timestamp{} } else { return dst.Set(*value) } case InfinityModifier: - *dst = Timestamp{InfinityModifier: value, Status: Present} + *dst = Timestamp{InfinityModifier: value, Valid: true} default: if originalSrc, ok := underlyingTimeType(src); ok { return dst.Set(originalSrc) @@ -59,63 +59,56 @@ func (dst *Timestamp) Set(src interface{}) error { } func (dst Timestamp) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + if dst.InfinityModifier != None { + return dst.InfinityModifier + } + return dst.Time } func (src *Timestamp) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return fmt.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *time.Time: + if src.InfinityModifier != None { + return fmt.Errorf("cannot assign %v to %T", src, dst) + } + *v = src.Time + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } // DecodeText decodes from src into dst. The decoded time is considered to // be in UTC. func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Timestamp{Status: Null} + *dst = Timestamp{} return nil } sbuf := string(src) switch sbuf { case "infinity": - *dst = Timestamp{Status: Present, InfinityModifier: Infinity} + *dst = Timestamp{Valid: true, InfinityModifier: Infinity} case "-infinity": - *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} + *dst = Timestamp{Valid: true, InfinityModifier: -Infinity} default: tim, err := time.Parse(pgTimestampFormat, sbuf) if err != nil { return err } - *dst = Timestamp{Time: tim, Status: Present} + *dst = Timestamp{Time: tim, Valid: true} } return nil @@ -125,7 +118,7 @@ func (dst *Timestamp) DecodeText(ci *ConnInfo, src []byte) error { // be in UTC. func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Timestamp{Status: Null} + *dst = Timestamp{} return nil } @@ -137,15 +130,15 @@ func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error { switch microsecSinceY2K { case infinityMicrosecondOffset: - *dst = Timestamp{Status: Present, InfinityModifier: Infinity} + *dst = Timestamp{Valid: true, InfinityModifier: Infinity} case negativeInfinityMicrosecondOffset: - *dst = Timestamp{Status: Present, InfinityModifier: -Infinity} + *dst = Timestamp{Valid: true, InfinityModifier: -Infinity} default: tim := time.Unix( microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000, (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000), ).UTC() - *dst = Timestamp{Time: tim, Status: Present} + *dst = Timestamp{Time: tim, Valid: true} } return nil @@ -154,11 +147,8 @@ func (dst *Timestamp) DecodeBinary(ci *ConnInfo, src []byte) error { // EncodeText writes the text encoding of src into w. If src.Time is not in // the UTC time zone it returns an error. func (src Timestamp) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.Time.Location() != time.UTC { return nil, fmt.Errorf("cannot encode non-UTC time into timestamp") @@ -181,11 +171,8 @@ func (src Timestamp) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { // EncodeBinary writes the binary encoding of src into w. If src.Time is not in // the UTC time zone it returns an error. func (src Timestamp) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if src.Time.Location() != time.UTC { return nil, fmt.Errorf("cannot encode non-UTC time into timestamp") @@ -208,7 +195,7 @@ func (src Timestamp) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Timestamp) Scan(src interface{}) error { if src == nil { - *dst = Timestamp{Status: Null} + *dst = Timestamp{} return nil } @@ -220,7 +207,7 @@ func (dst *Timestamp) Scan(src interface{}) error { copy(srcCopy, src) return dst.DecodeText(nil, srcCopy) case time.Time: - *dst = Timestamp{Time: src, Status: Present} + *dst = Timestamp{Time: src, Valid: true} return nil } @@ -229,15 +216,12 @@ func (dst *Timestamp) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Timestamp) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + if src.InfinityModifier != None { + return src.InfinityModifier.String(), nil + } + return src.Time, nil } diff --git a/timestamp_array.go b/timestamp_array.go index e12481e3..fbf7c48a 100644 --- a/timestamp_array.go +++ b/timestamp_array.go @@ -15,13 +15,13 @@ import ( type TimestampArray struct { Elements []Timestamp Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *TimestampArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} return nil } @@ -37,9 +37,9 @@ func (dst *TimestampArray) Set(src interface{}) error { case []time.Time: if value == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} } else if len(value) == 0 { - *dst = TimestampArray{Status: Present} + *dst = TimestampArray{Valid: true} } else { elements := make([]Timestamp, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *TimestampArray) Set(src interface{}) error { *dst = TimestampArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*time.Time: if value == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} } else if len(value) == 0 { - *dst = TimestampArray{Status: Present} + *dst = TimestampArray{Valid: true} } else { elements := make([]Timestamp, len(value)) for i := range value { @@ -69,20 +69,20 @@ func (dst *TimestampArray) Set(src interface{}) error { *dst = TimestampArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Timestamp: if value == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} } else if len(value) == 0 { - *dst = TimestampArray{Status: Present} + *dst = TimestampArray{Valid: true} } else { *dst = TimestampArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -91,7 +91,7 @@ func (dst *TimestampArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} return nil } @@ -100,7 +100,7 @@ func (dst *TimestampArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for TimestampArray", src) } if elementsLength == 0 { - *dst = TimestampArray{Status: Present} + *dst = TimestampArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -113,7 +113,7 @@ func (dst *TimestampArray) Set(src interface{}) error { *dst = TimestampArray{ Elements: make([]Timestamp, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -180,84 +180,77 @@ func (dst *TimestampArray) setRecursive(value reflect.Value, index, dimension in } func (dst TimestampArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TimestampArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*time.Time: - *v = make([]*time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]time.Time: + *v = make([]time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*time.Time: + *v = make([]*time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *TimestampArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -309,7 +302,7 @@ func (src *TimestampArray) assignToRecursive(value reflect.Value, index, dimensi func (dst *TimestampArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} return nil } @@ -338,14 +331,14 @@ func (dst *TimestampArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = TimestampArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = TimestampArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *TimestampArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TimestampArray{Status: Null} + *dst = TimestampArray{} return nil } @@ -356,7 +349,7 @@ func (dst *TimestampArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = TimestampArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TimestampArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -381,16 +374,13 @@ func (dst *TimestampArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = TimestampArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TimestampArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src TimestampArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -443,11 +433,8 @@ func (src TimestampArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src TimestampArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -461,7 +448,7 @@ func (src TimestampArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/timestamp_array_test.go b/timestamp_array_test.go index 54d15b24..214c8a71 100644 --- a/timestamp_array_test.go +++ b/timestamp_array_test.go @@ -14,53 +14,53 @@ func TestTimestampArrayTranscode(t *testing.T) { &pgtype.TimestampArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.TimestampArray{Status: pgtype.Null}, + &pgtype.TimestampArray{}, &pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }, func(a, b interface{}) bool { ata := a.(pgtype.TimestampArray) bta := b.(pgtype.TimestampArray) - if len(ata.Elements) != len(bta.Elements) || ata.Status != bta.Status { + if len(ata.Elements) != len(bta.Elements) || ata.Valid != bta.Valid { return false } for i := range ata.Elements { ae, be := ata.Elements[i], bta.Elements[i] - if !(ae.Time.Equal(be.Time) && ae.Status == be.Status && ae.InfinityModifier == be.InfinityModifier) { + if !(ae.Time.Equal(be.Time) && ae.Valid == be.Valid && ae.InfinityModifier == be.InfinityModifier) { return false } } @@ -77,13 +77,13 @@ func TestTimestampArraySet(t *testing.T) { { source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, result: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]time.Time)(nil)), - result: pgtype.TimestampArray{Status: pgtype.Null}, + result: pgtype.TimestampArray{}, }, { source: [][]time.Time{ @@ -91,10 +91,10 @@ func TestTimestampArraySet(t *testing.T) { {time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC)}}, result: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]time.Time{ @@ -108,18 +108,18 @@ func TestTimestampArraySet(t *testing.T) { time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC)}}}}, result: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -150,30 +150,30 @@ func TestTimestampArrayAssignTo(t *testing.T) { }{ { src: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Timestamp{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, }, { - src: pgtype.TimestampArray{Status: pgtype.Null}, + src: pgtype.TimestampArray{}, dst: &timeSlice, expected: (([]time.Time)(nil)), }, { - src: pgtype.TimestampArray{Status: pgtype.Present}, + src: pgtype.TimestampArray{Valid: true}, dst: &timeSlice, expected: []time.Time{}, }, { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim2, expected: [][]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -182,18 +182,18 @@ func TestTimestampArrayAssignTo(t *testing.T) { { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim4, expected: [][][][]time.Time{ {{{ @@ -208,10 +208,10 @@ func TestTimestampArrayAssignTo(t *testing.T) { { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, expected: [2][1]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -220,18 +220,18 @@ func TestTimestampArrayAssignTo(t *testing.T) { { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, expected: [2][1][1][3]time.Time{ {{{ @@ -262,37 +262,37 @@ func TestTimestampArrayAssignTo(t *testing.T) { }{ { src: pgtype.TimestampArray{ - Elements: []pgtype.Timestamp{{Status: pgtype.Null}}, + Elements: []pgtype.Timestamp{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, }, { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, }, { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSlice, }, { src: pgtype.TimestampArray{ Elements: []pgtype.Timestamp{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, }, } diff --git a/timestamp_test.go b/timestamp_test.go index ea7ef57a..88e2bca8 100644 --- a/timestamp_test.go +++ b/timestamp_test.go @@ -13,24 +13,24 @@ import ( func TestTimestampTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "timestamp", []interface{}{ - &pgtype.Timestamp{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - &pgtype.Timestamp{Status: pgtype.Null}, - &pgtype.Timestamp{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Timestamp{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, + &pgtype.Timestamp{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + &pgtype.Timestamp{}, + &pgtype.Timestamp{Valid: true, InfinityModifier: pgtype.Infinity}, + &pgtype.Timestamp{Valid: true, InfinityModifier: -pgtype.Infinity}, }, func(a, b interface{}) bool { at := a.(pgtype.Timestamp) bt := b.(pgtype.Timestamp) - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier + return at.Time.Equal(bt.Time) && at.Valid == bt.Valid && at.InfinityModifier == bt.InfinityModifier }) } @@ -42,7 +42,7 @@ func TestTimestampTranscodeBigTimeBinary(t *testing.T) { } defer testutil.MustCloseContext(t, conn) - in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Status: pgtype.Present} + in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true} var out pgtype.Timestamp err := conn.QueryRow(context.Background(), "select $1::timestamptz", in).Scan(&out) @@ -50,7 +50,7 @@ func TestTimestampTranscodeBigTimeBinary(t *testing.T) { t.Fatal(err) } - require.Equal(t, in.Status, out.Status) + require.Equal(t, in.Valid, out.Valid) require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time) } @@ -64,7 +64,7 @@ func TestTimestampNanosecondsTruncated(t *testing.T) { } for i, tt := range tests { { - ts := pgtype.Timestamp{Time: tt.input, Status: pgtype.Present} + ts := pgtype.Timestamp{Time: tt.input, Valid: true} buf, err := ts.EncodeText(nil, nil) if err != nil { t.Errorf("%d. EncodeText failed - %v", i, err) @@ -75,13 +75,13 @@ func TestTimestampNanosecondsTruncated(t *testing.T) { t.Errorf("%d. DecodeText failed - %v", i, err) } - if !(ts.Status == pgtype.Present && ts.Time.Equal(tt.expected)) { + if !(ts.Valid && ts.Time.Equal(tt.expected)) { t.Errorf("%d. EncodeText did not truncate nanoseconds", i) } } { - ts := pgtype.Timestamp{Time: tt.input, Status: pgtype.Present} + ts := pgtype.Timestamp{Time: tt.input, Valid: true} buf, err := ts.EncodeBinary(nil, nil) if err != nil { t.Errorf("%d. EncodeBinary failed - %v", i, err) @@ -92,7 +92,7 @@ func TestTimestampNanosecondsTruncated(t *testing.T) { t.Errorf("%d. DecodeBinary failed - %v", i, err) } - if !(ts.Status == pgtype.Present && ts.Time.Equal(tt.expected)) { + if !(ts.Valid && ts.Time.Equal(tt.expected)) { t.Errorf("%d. EncodeBinary did not truncate nanoseconds", i) } } @@ -113,16 +113,16 @@ func TestTimestampSet(t *testing.T) { source interface{} result pgtype.Timestamp }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, - {source: pgtype.Infinity, result: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}}, - {source: pgtype.NegativeInfinity, result: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}}, + {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.UTC), Valid: true}}, + {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.UTC), Valid: true}}, + {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Timestamp{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, + {source: pgtype.Infinity, result: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}}, + {source: pgtype.NegativeInfinity, result: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}}, } for i, tt := range successfulTests { @@ -147,8 +147,8 @@ func TestTimestampAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, - {src: pgtype.Timestamp{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, + {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, + {src: pgtype.Timestamp{Time: time.Time{}}, dst: &ptim, expected: ((*time.Time)(nil))}, } for i, tt := range simpleTests { @@ -167,7 +167,7 @@ func TestTimestampAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, + {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, } for i, tt := range pointerAllocTests { @@ -185,9 +185,9 @@ func TestTimestampAssignTo(t *testing.T) { src pgtype.Timestamp dst interface{} }{ - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, + {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Valid: true}, dst: &tim}, + {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Valid: true}, dst: &tim}, + {src: pgtype.Timestamp{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, dst: &tim}, } for i, tt := range errorTests { diff --git a/timestamptz.go b/timestamptz.go index 299a8668..2a711ffa 100644 --- a/timestamptz.go +++ b/timestamptz.go @@ -22,13 +22,13 @@ const ( type Timestamptz struct { Time time.Time - Status Status + Valid bool InfinityModifier InfinityModifier } func (dst *Timestamptz) Set(src interface{}) error { if src == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} return nil } @@ -41,15 +41,15 @@ func (dst *Timestamptz) Set(src interface{}) error { switch value := src.(type) { case time.Time: - *dst = Timestamptz{Time: value, Status: Present} + *dst = Timestamptz{Time: value, Valid: true} case *time.Time: if value == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} } else { return dst.Set(*value) } case InfinityModifier: - *dst = Timestamptz{InfinityModifier: value, Status: Present} + *dst = Timestamptz{InfinityModifier: value, Valid: true} default: if originalSrc, ok := underlyingTimeType(src); ok { return dst.Set(originalSrc) @@ -61,54 +61,47 @@ func (dst *Timestamptz) Set(src interface{}) error { } func (dst Timestamptz) Get() interface{} { - switch dst.Status { - case Present: - if dst.InfinityModifier != None { - return dst.InfinityModifier - } - return dst.Time - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + if dst.InfinityModifier != None { + return dst.InfinityModifier + } + return dst.Time } func (src *Timestamptz) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *time.Time: - if src.InfinityModifier != None { - return fmt.Errorf("cannot assign %v to %T", src, dst) - } - *v = src.Time - return nil - default: - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - return fmt.Errorf("unable to assign to %T", dst) - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + switch v := dst.(type) { + case *time.Time: + if src.InfinityModifier != None { + return fmt.Errorf("cannot assign %v to %T", src, dst) + } + *v = src.Time + return nil + default: + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + return fmt.Errorf("unable to assign to %T", dst) + } } func (dst *Timestamptz) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} return nil } sbuf := string(src) switch sbuf { case "infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: Infinity} case "-infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: -Infinity} default: var format string if len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') { @@ -124,7 +117,7 @@ func (dst *Timestamptz) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Timestamptz{Time: tim, Status: Present} + *dst = Timestamptz{Time: tim, Valid: true} } return nil @@ -132,7 +125,7 @@ func (dst *Timestamptz) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Timestamptz) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} return nil } @@ -144,26 +137,23 @@ func (dst *Timestamptz) DecodeBinary(ci *ConnInfo, src []byte) error { switch microsecSinceY2K { case infinityMicrosecondOffset: - *dst = Timestamptz{Status: Present, InfinityModifier: Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: Infinity} case negativeInfinityMicrosecondOffset: - *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: -Infinity} default: tim := time.Unix( microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000, (microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000), ) - *dst = Timestamptz{Time: tim, Status: Present} + *dst = Timestamptz{Time: tim, Valid: true} } return nil } func (src Timestamptz) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var s string @@ -181,11 +171,8 @@ func (src Timestamptz) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Timestamptz) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var microsecSinceY2K int64 @@ -205,7 +192,7 @@ func (src Timestamptz) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Timestamptz) Scan(src interface{}) error { if src == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} return nil } @@ -217,7 +204,7 @@ func (dst *Timestamptz) Scan(src interface{}) error { copy(srcCopy, src) return dst.DecodeText(nil, srcCopy) case time.Time: - *dst = Timestamptz{Time: src, Status: Present} + *dst = Timestamptz{Time: src, Valid: true} return nil } @@ -226,29 +213,19 @@ func (dst *Timestamptz) Scan(src interface{}) error { // Value implements the database/sql/driver Valuer interface. func (src Timestamptz) Value() (driver.Value, error) { - switch src.Status { - case Present: - if src.InfinityModifier != None { - return src.InfinityModifier.String(), nil - } - return src.Time, nil - case Null: + if !src.Valid { return nil, nil - default: - return nil, errUndefined } + + if src.InfinityModifier != None { + return src.InfinityModifier.String(), nil + } + return src.Time, nil } func (src Timestamptz) MarshalJSON() ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined - } - - if src.Status != Present { - return nil, errBadStatus } var s string @@ -273,15 +250,15 @@ func (dst *Timestamptz) UnmarshalJSON(b []byte) error { } if s == nil { - *dst = Timestamptz{Status: Null} + *dst = Timestamptz{} return nil } switch *s { case "infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: Infinity} case "-infinity": - *dst = Timestamptz{Status: Present, InfinityModifier: -Infinity} + *dst = Timestamptz{Valid: true, InfinityModifier: -Infinity} default: // PostgreSQL uses ISO 8601 for to_json function and casting from a string to timestamptz tim, err := time.Parse(time.RFC3339Nano, *s) @@ -289,7 +266,7 @@ func (dst *Timestamptz) UnmarshalJSON(b []byte) error { return err } - *dst = Timestamptz{Time: tim, Status: Present} + *dst = Timestamptz{Time: tim, Valid: true} } return nil diff --git a/timestamptz_array.go b/timestamptz_array.go index a3b4b263..4523b251 100644 --- a/timestamptz_array.go +++ b/timestamptz_array.go @@ -15,13 +15,13 @@ import ( type TimestamptzArray struct { Elements []Timestamptz Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *TimestamptzArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} return nil } @@ -37,9 +37,9 @@ func (dst *TimestamptzArray) Set(src interface{}) error { case []time.Time: if value == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} } else if len(value) == 0 { - *dst = TimestamptzArray{Status: Present} + *dst = TimestamptzArray{Valid: true} } else { elements := make([]Timestamptz, len(value)) for i := range value { @@ -50,15 +50,15 @@ func (dst *TimestamptzArray) Set(src interface{}) error { *dst = TimestamptzArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*time.Time: if value == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} } else if len(value) == 0 { - *dst = TimestamptzArray{Status: Present} + *dst = TimestamptzArray{Valid: true} } else { elements := make([]Timestamptz, len(value)) for i := range value { @@ -69,20 +69,20 @@ func (dst *TimestamptzArray) Set(src interface{}) error { *dst = TimestamptzArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Timestamptz: if value == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} } else if len(value) == 0 { - *dst = TimestamptzArray{Status: Present} + *dst = TimestamptzArray{Valid: true} } else { *dst = TimestamptzArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -91,7 +91,7 @@ func (dst *TimestamptzArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} return nil } @@ -100,7 +100,7 @@ func (dst *TimestamptzArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for TimestamptzArray", src) } if elementsLength == 0 { - *dst = TimestamptzArray{Status: Present} + *dst = TimestamptzArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -113,7 +113,7 @@ func (dst *TimestamptzArray) Set(src interface{}) error { *dst = TimestamptzArray{ Elements: make([]Timestamptz, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -180,84 +180,77 @@ func (dst *TimestamptzArray) setRecursive(value reflect.Value, index, dimension } func (dst TimestamptzArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TimestamptzArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]time.Time: - *v = make([]time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*time.Time: - *v = make([]*time.Time, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]time.Time: + *v = make([]time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*time.Time: + *v = make([]*time.Time, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *TimestamptzArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -309,7 +302,7 @@ func (src *TimestamptzArray) assignToRecursive(value reflect.Value, index, dimen func (dst *TimestamptzArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} return nil } @@ -338,14 +331,14 @@ func (dst *TimestamptzArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = TimestamptzArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = TimestamptzArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *TimestamptzArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TimestamptzArray{Status: Null} + *dst = TimestamptzArray{} return nil } @@ -356,7 +349,7 @@ func (dst *TimestamptzArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = TimestamptzArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TimestamptzArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -381,16 +374,13 @@ func (dst *TimestamptzArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = TimestamptzArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TimestamptzArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src TimestamptzArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -443,11 +433,8 @@ func (src TimestamptzArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) } func (src TimestamptzArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -461,7 +448,7 @@ func (src TimestamptzArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, erro } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/timestamptz_array_test.go b/timestamptz_array_test.go index 9856e4e7..22e07b59 100644 --- a/timestamptz_array_test.go +++ b/timestamptz_array_test.go @@ -14,53 +14,53 @@ func TestTimestamptzArrayTranscode(t *testing.T) { &pgtype.TimestamptzArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.TimestamptzArray{Status: pgtype.Null}, + &pgtype.TimestamptzArray{}, &pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Status: pgtype.Null}, - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }, func(a, b interface{}) bool { ata := a.(pgtype.TimestamptzArray) bta := b.(pgtype.TimestamptzArray) - if len(ata.Elements) != len(bta.Elements) || ata.Status != bta.Status { + if len(ata.Elements) != len(bta.Elements) || ata.Valid != bta.Valid { return false } for i := range ata.Elements { ae, be := ata.Elements[i], bta.Elements[i] - if !(ae.Time.Equal(be.Time) && ae.Status == be.Status && ae.InfinityModifier == be.InfinityModifier) { + if !(ae.Time.Equal(be.Time) && ae.Valid == be.Valid && ae.InfinityModifier == be.InfinityModifier) { return false } } @@ -77,13 +77,13 @@ func TestTimestamptzArraySet(t *testing.T) { { source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, result: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]time.Time)(nil)), - result: pgtype.TimestamptzArray{Status: pgtype.Null}, + result: pgtype.TimestamptzArray{}, }, { source: [][]time.Time{ @@ -91,10 +91,10 @@ func TestTimestamptzArraySet(t *testing.T) { {time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC)}}, result: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]time.Time{ @@ -108,18 +108,18 @@ func TestTimestamptzArraySet(t *testing.T) { time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC)}}}}, result: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]time.Time{ @@ -127,10 +127,10 @@ func TestTimestamptzArraySet(t *testing.T) { {time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC)}}, result: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]time.Time{ @@ -144,18 +144,18 @@ func TestTimestamptzArraySet(t *testing.T) { time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC)}}}}, result: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -186,30 +186,30 @@ func TestTimestamptzArrayAssignTo(t *testing.T) { }{ { src: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + Elements: []pgtype.Timestamptz{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, }, { - src: pgtype.TimestamptzArray{Status: pgtype.Null}, + src: pgtype.TimestamptzArray{}, dst: &timeSlice, expected: (([]time.Time)(nil)), }, { - src: pgtype.TimestamptzArray{Status: pgtype.Present}, + src: pgtype.TimestamptzArray{Valid: true}, dst: &timeSlice, expected: []time.Time{}, }, { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim2, expected: [][]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -218,18 +218,18 @@ func TestTimestamptzArrayAssignTo(t *testing.T) { { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSliceDim4, expected: [][][][]time.Time{ {{{ @@ -244,10 +244,10 @@ func TestTimestamptzArrayAssignTo(t *testing.T) { { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, expected: [2][1]time.Time{ {time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)}, @@ -256,18 +256,18 @@ func TestTimestamptzArrayAssignTo(t *testing.T) { { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2017, 5, 6, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2018, 7, 8, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2019, 9, 10, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2020, 11, 12, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, expected: [2][1][1][3]time.Time{ {{{ @@ -298,37 +298,37 @@ func TestTimestamptzArrayAssignTo(t *testing.T) { }{ { src: pgtype.TimestamptzArray{ - Elements: []pgtype.Timestamptz{{Status: pgtype.Null}}, + Elements: []pgtype.Timestamptz{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &timeSlice, }, { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim2, }, { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeSlice, }, { src: pgtype.TimestamptzArray{ Elements: []pgtype.Timestamptz{ - {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}}, + {Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Valid: true}, + {Time: time.Date(2016, 3, 4, 0, 0, 0, 0, time.UTC), Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &timeArrayDim4, }, } diff --git a/timestamptz_test.go b/timestamptz_test.go index c3f63967..fa2a7e89 100644 --- a/timestamptz_test.go +++ b/timestamptz_test.go @@ -13,24 +13,24 @@ import ( func TestTimestamptzTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "timestamptz", []interface{}{ - &pgtype.Timestamptz{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, - &pgtype.Timestamptz{Status: pgtype.Null}, - &pgtype.Timestamptz{Status: pgtype.Present, InfinityModifier: pgtype.Infinity}, - &pgtype.Timestamptz{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity}, + &pgtype.Timestamptz{Time: time.Date(1800, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1905, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1940, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1960, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, + &pgtype.Timestamptz{}, + &pgtype.Timestamptz{Valid: true, InfinityModifier: pgtype.Infinity}, + &pgtype.Timestamptz{Valid: true, InfinityModifier: -pgtype.Infinity}, }, func(a, b interface{}) bool { at := a.(pgtype.Timestamptz) bt := b.(pgtype.Timestamptz) - return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier + return at.Time.Equal(bt.Time) && at.Valid == bt.Valid && at.InfinityModifier == bt.InfinityModifier }) } @@ -42,7 +42,7 @@ func TestTimestamptzTranscodeBigTimeBinary(t *testing.T) { } defer testutil.MustCloseContext(t, conn) - in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Status: pgtype.Present} + in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true} var out pgtype.Timestamptz err := conn.QueryRow(context.Background(), "select $1::timestamptz", in).Scan(&out) @@ -50,7 +50,7 @@ func TestTimestamptzTranscodeBigTimeBinary(t *testing.T) { t.Fatal(err) } - require.Equal(t, in.Status, out.Status) + require.Equal(t, in.Valid, out.Valid) require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time) } @@ -64,7 +64,7 @@ func TestTimestamptzNanosecondsTruncated(t *testing.T) { } for i, tt := range tests { { - tstz := pgtype.Timestamptz{Time: tt.input, Status: pgtype.Present} + tstz := pgtype.Timestamptz{Time: tt.input, Valid: true} buf, err := tstz.EncodeText(nil, nil) if err != nil { t.Errorf("%d. EncodeText failed - %v", i, err) @@ -75,13 +75,13 @@ func TestTimestamptzNanosecondsTruncated(t *testing.T) { t.Errorf("%d. DecodeText failed - %v", i, err) } - if !(tstz.Status == pgtype.Present && tstz.Time.Equal(tt.expected)) { + if !(tstz.Valid && tstz.Time.Equal(tt.expected)) { t.Errorf("%d. EncodeText did not truncate nanoseconds", i) } } { - tstz := pgtype.Timestamptz{Time: tt.input, Status: pgtype.Present} + tstz := pgtype.Timestamptz{Time: tt.input, Valid: true} buf, err := tstz.EncodeBinary(nil, nil) if err != nil { t.Errorf("%d. EncodeBinary failed - %v", i, err) @@ -92,7 +92,7 @@ func TestTimestamptzNanosecondsTruncated(t *testing.T) { t.Errorf("%d. DecodeBinary failed - %v", i, err) } - if !(tstz.Status == pgtype.Present && tstz.Time.Equal(tt.expected)) { + if !(tstz.Valid && tstz.Time.Equal(tt.expected)) { t.Errorf("%d. EncodeBinary did not truncate nanoseconds", i) } } @@ -113,15 +113,15 @@ func TestTimestamptzSet(t *testing.T) { source interface{} result pgtype.Timestamptz }{ - {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), Status: pgtype.Present}}, - {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local)), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}}, - {source: pgtype.Infinity, result: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}}, - {source: pgtype.NegativeInfinity, result: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}}, + {source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, + {source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, + {source: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(1999, 12, 31, 12, 59, 59, 0, time.Local), Valid: true}}, + {source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, + {source: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2000, 1, 1, 0, 0, 1, 0, time.Local), Valid: true}}, + {source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), result: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, + {source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local)), result: pgtype.Timestamptz{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, + {source: pgtype.Infinity, result: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}}, + {source: pgtype.NegativeInfinity, result: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}}, } for i, tt := range successfulTests { @@ -146,8 +146,8 @@ func TestTimestamptzAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, - {src: pgtype.Timestamptz{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))}, + {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, + {src: pgtype.Timestamptz{Time: time.Time{}}, dst: &ptim, expected: ((*time.Time)(nil))}, } for i, tt := range simpleTests { @@ -166,7 +166,7 @@ func TestTimestamptzAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, + {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, } for i, tt := range pointerAllocTests { @@ -184,9 +184,9 @@ func TestTimestamptzAssignTo(t *testing.T) { src pgtype.Timestamptz dst interface{} }{ - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim}, - {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim}, + {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Valid: true}, dst: &tim}, + {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Valid: true}, dst: &tim}, + {src: pgtype.Timestamptz{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)}, dst: &tim}, } for i, tt := range errorTests { @@ -202,11 +202,11 @@ func TestTimestamptzMarshalJSON(t *testing.T) { source pgtype.Timestamptz result string }{ - {source: pgtype.Timestamptz{Status: pgtype.Null}, result: "null"}, - {source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Status: pgtype.Present}, result: "\"2012-03-29T10:05:45-06:00\""}, - {source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Status: pgtype.Present}, result: "\"2012-03-29T10:05:45.555-06:00\""}, - {source: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, result: "\"infinity\""}, - {source: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, result: "\"-infinity\""}, + {source: pgtype.Timestamptz{}, result: "null"}, + {source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Valid: true}, result: "\"2012-03-29T10:05:45-06:00\""}, + {source: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Valid: true}, result: "\"2012-03-29T10:05:45.555-06:00\""}, + {source: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}, result: "\"infinity\""}, + {source: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: "\"-infinity\""}, } for i, tt := range successfulTests { r, err := tt.source.MarshalJSON() @@ -225,11 +225,11 @@ func TestTimestamptzUnmarshalJSON(t *testing.T) { source string result pgtype.Timestamptz }{ - {source: "null", result: pgtype.Timestamptz{Status: pgtype.Null}}, - {source: "\"2012-03-29T10:05:45-06:00\"", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Status: pgtype.Present}}, - {source: "\"2012-03-29T10:05:45.555-06:00\"", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Status: pgtype.Present}}, - {source: "\"infinity\"", result: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Status: pgtype.Present}}, - {source: "\"-infinity\"", result: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}}, + {source: "null", result: pgtype.Timestamptz{}}, + {source: "\"2012-03-29T10:05:45-06:00\"", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.FixedZone("", -6*60*60)), Valid: true}}, + {source: "\"2012-03-29T10:05:45.555-06:00\"", result: pgtype.Timestamptz{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.FixedZone("", -6*60*60)), Valid: true}}, + {source: "\"infinity\"", result: pgtype.Timestamptz{InfinityModifier: pgtype.Infinity, Valid: true}}, + {source: "\"-infinity\"", result: pgtype.Timestamptz{InfinityModifier: pgtype.NegativeInfinity, Valid: true}}, } for i, tt := range successfulTests { var r pgtype.Timestamptz @@ -238,7 +238,7 @@ func TestTimestamptzUnmarshalJSON(t *testing.T) { t.Errorf("%d: %v", i, err) } - if !r.Time.Equal(tt.result.Time) || r.Status != tt.result.Status || r.InfinityModifier != tt.result.InfinityModifier { + if !r.Time.Equal(tt.result.Time) || r.Valid != tt.result.Valid || r.InfinityModifier != tt.result.InfinityModifier { t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r) } } diff --git a/tsrange.go b/tsrange.go index 19ecf446..7495d972 100644 --- a/tsrange.go +++ b/tsrange.go @@ -12,13 +12,13 @@ type Tsrange struct { Upper Timestamp LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Tsrange) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Tsrange{Status: Null} + *dst = Tsrange{} return nil } @@ -36,15 +36,11 @@ func (dst *Tsrange) Set(src interface{}) error { return nil } -func (dst Tsrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Tsrange) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Tsrange) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Tsrange) AssignTo(dst interface{}) error { func (dst *Tsrange) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Tsrange{Status: Null} + *dst = Tsrange{} return nil } @@ -62,7 +58,7 @@ func (dst *Tsrange) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Tsrange{Status: Present} + *dst = Tsrange{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Tsrange) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Tsrange) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Tsrange{Status: Null} + *dst = Tsrange{} return nil } @@ -97,7 +93,7 @@ func (dst *Tsrange) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Tsrange{Status: Present} + *dst = Tsrange{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Tsrange) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Tsrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Tsrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Tsrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Tsrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Tsrange) Scan(src interface{}) error { if src == nil { - *dst = Tsrange{Status: Null} + *dst = Tsrange{} return nil } diff --git a/tsrange_array.go b/tsrange_array.go index c64048eb..2af25f8d 100644 --- a/tsrange_array.go +++ b/tsrange_array.go @@ -14,13 +14,13 @@ import ( type TsrangeArray struct { Elements []Tsrange Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *TsrangeArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = TsrangeArray{Status: Null} + *dst = TsrangeArray{} return nil } @@ -36,14 +36,14 @@ func (dst *TsrangeArray) Set(src interface{}) error { case []Tsrange: if value == nil { - *dst = TsrangeArray{Status: Null} + *dst = TsrangeArray{} } else if len(value) == 0 { - *dst = TsrangeArray{Status: Present} + *dst = TsrangeArray{Valid: true} } else { *dst = TsrangeArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -52,7 +52,7 @@ func (dst *TsrangeArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = TsrangeArray{Status: Null} + *dst = TsrangeArray{} return nil } @@ -61,7 +61,7 @@ func (dst *TsrangeArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for TsrangeArray", src) } if elementsLength == 0 { - *dst = TsrangeArray{Status: Present} + *dst = TsrangeArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -74,7 +74,7 @@ func (dst *TsrangeArray) Set(src interface{}) error { *dst = TsrangeArray{ Elements: make([]Tsrange, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -141,75 +141,68 @@ func (dst *TsrangeArray) setRecursive(value reflect.Value, index, dimension int) } func (dst TsrangeArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TsrangeArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]Tsrange: - *v = make([]Tsrange, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]Tsrange: + *v = make([]Tsrange, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *TsrangeArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -261,7 +254,7 @@ func (src *TsrangeArray) assignToRecursive(value reflect.Value, index, dimension func (dst *TsrangeArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TsrangeArray{Status: Null} + *dst = TsrangeArray{} return nil } @@ -290,14 +283,14 @@ func (dst *TsrangeArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = TsrangeArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = TsrangeArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *TsrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TsrangeArray{Status: Null} + *dst = TsrangeArray{} return nil } @@ -308,7 +301,7 @@ func (dst *TsrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = TsrangeArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TsrangeArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -333,16 +326,13 @@ func (dst *TsrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = TsrangeArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TsrangeArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src TsrangeArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -395,11 +385,8 @@ func (src TsrangeArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src TsrangeArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -413,7 +400,7 @@ func (src TsrangeArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/tsrange_test.go b/tsrange_test.go index 1be0c7d2..daea59bb 100644 --- a/tsrange_test.go +++ b/tsrange_test.go @@ -10,32 +10,32 @@ import ( func TestTsrangeTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "tsrange", []interface{}{ - &pgtype.Tsrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, + &pgtype.Tsrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, &pgtype.Tsrange{ - Lower: pgtype.Timestamp{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamp{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Timestamp{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Timestamp{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, &pgtype.Tsrange{ - Lower: pgtype.Timestamp{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Timestamp{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Timestamp{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Tsrange{Status: pgtype.Null}, + &pgtype.Tsrange{}, }, func(aa, bb interface{}) bool { a := aa.(pgtype.Tsrange) b := bb.(pgtype.Tsrange) - return a.Status == b.Status && + return a.Valid == b.Valid && a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && + a.Lower.Valid == b.Lower.Valid && a.Lower.InfinityModifier == b.Lower.InfinityModifier && a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && + a.Upper.Valid == b.Upper.Valid && a.Upper.InfinityModifier == b.Upper.InfinityModifier }) } diff --git a/tstzrange.go b/tstzrange.go index 25576308..3d4e2cde 100644 --- a/tstzrange.go +++ b/tstzrange.go @@ -12,13 +12,13 @@ type Tstzrange struct { Upper Timestamptz LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *Tstzrange) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = Tstzrange{Status: Null} + *dst = Tstzrange{} return nil } @@ -36,15 +36,11 @@ func (dst *Tstzrange) Set(src interface{}) error { return nil } -func (dst Tstzrange) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: +func (src Tstzrange) Get() interface{} { + if !src.Valid { return nil - default: - return dst.Status } + return src } func (src *Tstzrange) AssignTo(dst interface{}) error { @@ -53,7 +49,7 @@ func (src *Tstzrange) AssignTo(dst interface{}) error { func (dst *Tstzrange) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Tstzrange{Status: Null} + *dst = Tstzrange{} return nil } @@ -62,7 +58,7 @@ func (dst *Tstzrange) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = Tstzrange{Status: Present} + *dst = Tstzrange{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -88,7 +84,7 @@ func (dst *Tstzrange) DecodeText(ci *ConnInfo, src []byte) error { func (dst *Tstzrange) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Tstzrange{Status: Null} + *dst = Tstzrange{} return nil } @@ -97,7 +93,7 @@ func (dst *Tstzrange) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = Tstzrange{Status: Present} + *dst = Tstzrange{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -122,11 +118,8 @@ func (dst *Tstzrange) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src Tstzrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -175,11 +168,8 @@ func (src Tstzrange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Tstzrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -245,7 +235,7 @@ func (src Tstzrange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Tstzrange) Scan(src interface{}) error { if src == nil { - *dst = Tstzrange{Status: Null} + *dst = Tstzrange{} return nil } diff --git a/tstzrange_array.go b/tstzrange_array.go index a216820a..389d6b4c 100644 --- a/tstzrange_array.go +++ b/tstzrange_array.go @@ -14,13 +14,13 @@ import ( type TstzrangeArray struct { Elements []Tstzrange Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *TstzrangeArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = TstzrangeArray{Status: Null} + *dst = TstzrangeArray{} return nil } @@ -36,14 +36,14 @@ func (dst *TstzrangeArray) Set(src interface{}) error { case []Tstzrange: if value == nil { - *dst = TstzrangeArray{Status: Null} + *dst = TstzrangeArray{} } else if len(value) == 0 { - *dst = TstzrangeArray{Status: Present} + *dst = TstzrangeArray{Valid: true} } else { *dst = TstzrangeArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -52,7 +52,7 @@ func (dst *TstzrangeArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = TstzrangeArray{Status: Null} + *dst = TstzrangeArray{} return nil } @@ -61,7 +61,7 @@ func (dst *TstzrangeArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for TstzrangeArray", src) } if elementsLength == 0 { - *dst = TstzrangeArray{Status: Present} + *dst = TstzrangeArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -74,7 +74,7 @@ func (dst *TstzrangeArray) Set(src interface{}) error { *dst = TstzrangeArray{ Elements: make([]Tstzrange, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -141,75 +141,68 @@ func (dst *TstzrangeArray) setRecursive(value reflect.Value, index, dimension in } func (dst TstzrangeArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *TstzrangeArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]Tstzrange: - *v = make([]Tstzrange, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]Tstzrange: + *v = make([]Tstzrange, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *TstzrangeArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -261,7 +254,7 @@ func (src *TstzrangeArray) assignToRecursive(value reflect.Value, index, dimensi func (dst *TstzrangeArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TstzrangeArray{Status: Null} + *dst = TstzrangeArray{} return nil } @@ -290,14 +283,14 @@ func (dst *TstzrangeArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = TstzrangeArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = TstzrangeArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *TstzrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = TstzrangeArray{Status: Null} + *dst = TstzrangeArray{} return nil } @@ -308,7 +301,7 @@ func (dst *TstzrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = TstzrangeArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TstzrangeArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -333,16 +326,13 @@ func (dst *TstzrangeArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = TstzrangeArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = TstzrangeArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src TstzrangeArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -395,11 +385,8 @@ func (src TstzrangeArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src TstzrangeArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -413,7 +400,7 @@ func (src TstzrangeArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/tstzrange_test.go b/tstzrange_test.go index f8e2c2c5..49cfc63e 100644 --- a/tstzrange_test.go +++ b/tstzrange_test.go @@ -11,32 +11,32 @@ import ( func TestTstzrangeTranscode(t *testing.T) { testutil.TestSuccessfulTranscodeEqFunc(t, "tstzrange", []interface{}{ - &pgtype.Tstzrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present}, + &pgtype.Tstzrange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, &pgtype.Tstzrange{ - Lower: pgtype.Timestamptz{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamptz{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Timestamptz{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Timestamptz{Time: time.Date(2028, 1, 1, 0, 23, 12, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, &pgtype.Tstzrange{ - Lower: pgtype.Timestamptz{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}, - Upper: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Status: pgtype.Present}, + Lower: pgtype.Timestamptz{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Valid: true}, + Upper: pgtype.Timestamptz{Time: time.Date(2200, 1, 1, 0, 23, 12, 0, time.UTC), Valid: true}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, - Status: pgtype.Present, + Valid: true, }, - &pgtype.Tstzrange{Status: pgtype.Null}, + &pgtype.Tstzrange{}, }, func(aa, bb interface{}) bool { a := aa.(pgtype.Tstzrange) b := bb.(pgtype.Tstzrange) - return a.Status == b.Status && + return a.Valid == b.Valid && a.Lower.Time.Equal(b.Lower.Time) && - a.Lower.Status == b.Lower.Status && + a.Lower.Valid == b.Lower.Valid && a.Lower.InfinityModifier == b.Lower.InfinityModifier && a.Upper.Time.Equal(b.Upper.Time) && - a.Upper.Status == b.Upper.Status && + a.Upper.Valid == b.Upper.Valid && a.Upper.InfinityModifier == b.Upper.InfinityModifier }) } diff --git a/typed_array.go.erb b/typed_array.go.erb index 5788626b..e1ead59c 100644 --- a/typed_array.go.erb +++ b/typed_array.go.erb @@ -13,13 +13,13 @@ import ( type <%= pgtype_array_type %> struct { Elements []<%= pgtype_element_type %> Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} return nil } @@ -36,9 +36,9 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { <% if t != "[]#{pgtype_element_type}" %> case <%= t %>: if value == nil { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} } else if len(value) == 0 { - *dst = <%= pgtype_array_type %>{Status: Present} + *dst = <%= pgtype_array_type %>{Valid: true} } else { elements := make([]<%= pgtype_element_type %>, len(value)) for i := range value { @@ -49,21 +49,21 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { *dst = <%= pgtype_array_type %>{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } <% end %> <% end %> case []<%= pgtype_element_type %>: if value == nil { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} } else if len(value) == 0 { - *dst = <%= pgtype_array_type %>{Status: Present} + *dst = <%= pgtype_array_type %>{Valid: true} } else { *dst = <%= pgtype_array_type %>{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status : Present, + Valid: true, } } default: @@ -72,7 +72,7 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} return nil } @@ -81,7 +81,7 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for <%= pgtype_array_type %>", src) } if elementsLength == 0 { - *dst = <%= pgtype_array_type %>{Status: Present} + *dst = <%= pgtype_array_type %>{Valid: true} return nil } if len(dimensions) == 0 { @@ -94,7 +94,7 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { *dst = <%= pgtype_array_type %> { Elements: make([]<%= pgtype_element_type %>, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -161,75 +161,68 @@ func (dst *<%= pgtype_array_type %>) setRecursive(value reflect.Value, index, di } func (dst <%= pgtype_array_type %>) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } + if !dst.Valid { + return nil + } + return dst } func (src *<%= pgtype_array_type %>) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1{ - // Attempt to match to select common types: - switch v := dst.(type) { - <% go_array_types.split(",").each do |t| %> - case *<%= t %>: - *v = make(<%= t %>, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - <% end %> - } - } + if !src.Valid { + return NullAssignTo(dst) + } - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } + if len(src.Dimensions) <= 1{ + // Attempt to match to select common types: + switch v := dst.(type) { + <% go_array_types.split(",").each do |t| %> + case *<%= t %>: + *v = make(<%= t %>, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + <% end %> + } + } - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } - return nil - case Null: - return NullAssignTo(dst) - } + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + return nil } func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -281,7 +274,7 @@ func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, inde func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} return nil } @@ -310,7 +303,7 @@ func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error } } - *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } @@ -318,7 +311,7 @@ func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error <% if binary_format == "true" %> func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = <%= pgtype_array_type %>{Status: Null} + *dst = <%= pgtype_array_type %>{} return nil } @@ -329,7 +322,7 @@ func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) erro } if len(arrayHeader.Dimensions) == 0 { - *dst = <%= pgtype_array_type %>{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = <%= pgtype_array_type %>{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -354,18 +347,15 @@ func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) erro } } - *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = <%= pgtype_array_type %>{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } <% end %> func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined - } + } if len(src.Dimensions) == 0 { return append(buf, '{', '}'), nil @@ -418,12 +408,9 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte <% if binary_format == "true" %> func (src <%= pgtype_array_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined - } + } arrayHeader := ArrayHeader{ Dimensions: src.Dimensions, @@ -436,7 +423,7 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/typed_range.go.erb b/typed_range.go.erb index 5625587a..99d8c22d 100644 --- a/typed_range.go.erb +++ b/typed_range.go.erb @@ -14,13 +14,13 @@ type <%= range_type %> struct { Upper <%= element_type %> LowerType BoundType UpperType BoundType - Status Status + Valid bool } func (dst *<%= range_type %>) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = <%= range_type %>{Status: Null} + *dst = <%= range_type %>{} return nil } @@ -38,15 +38,11 @@ func (dst *<%= range_type %>) Set(src interface{}) error { return nil } -func (dst <%= range_type %>) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: - return nil - default: - return dst.Status - } +func (src <%= range_type %>) Get() interface{} { + if !src.Valid { + return nil + } + return src } func (src *<%= range_type %>) AssignTo(dst interface{}) error { @@ -55,7 +51,7 @@ func (src *<%= range_type %>) AssignTo(dst interface{}) error { func (dst *<%= range_type %>) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = <%= range_type %>{Status: Null} + *dst = <%= range_type %>{} return nil } @@ -64,7 +60,7 @@ func (dst *<%= range_type %>) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = <%= range_type %>{Status: Present} + *dst = <%= range_type %>{Valid: true} dst.LowerType = utr.LowerType dst.UpperType = utr.UpperType @@ -90,7 +86,7 @@ func (dst *<%= range_type %>) DecodeText(ci *ConnInfo, src []byte) error { func (dst *<%= range_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = <%= range_type %>{Status: Null} + *dst = <%= range_type %>{} return nil } @@ -99,7 +95,7 @@ func (dst *<%= range_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { return err } - *dst = <%= range_type %>{Status: Present} + *dst = <%= range_type %>{Valid: true} dst.LowerType = ubr.LowerType dst.UpperType = ubr.UpperType @@ -124,11 +120,8 @@ func (dst *<%= range_type %>) DecodeBinary(ci *ConnInfo, src []byte) error { } func (src <%= range_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } switch src.LowerType { @@ -177,11 +170,8 @@ func (src <%= range_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error } func (src <%= range_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } var rangeType byte @@ -247,7 +237,7 @@ func (src <%= range_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, err // Scan implements the database/sql Scanner interface. func (dst *<%= range_type %>) Scan(src interface{}) error { if src == nil { - *dst = <%= range_type %>{Status: Null} + *dst = <%= range_type %>{} return nil } diff --git a/unknown.go b/unknown.go index c591b708..0e576ee9 100644 --- a/unknown.go +++ b/unknown.go @@ -8,7 +8,7 @@ import "database/sql/driver" // type information. e.g. SELECT NULL; type Unknown struct { String string - Status Status + Valid bool } func (dst *Unknown) Set(src interface{}) error { diff --git a/uuid.go b/uuid.go index fa0be07f..d46111d3 100644 --- a/uuid.go +++ b/uuid.go @@ -8,13 +8,13 @@ import ( ) type UUID struct { - Bytes [16]byte - Status Status + Bytes [16]byte + Valid bool } func (dst *UUID) Set(src interface{}) error { if src == nil { - *dst = UUID{Status: Null} + *dst = UUID{} return nil } @@ -27,26 +27,26 @@ func (dst *UUID) Set(src interface{}) error { switch value := src.(type) { case [16]byte: - *dst = UUID{Bytes: value, Status: Present} + *dst = UUID{Bytes: value, Valid: true} case []byte: if value != nil { if len(value) != 16 { return fmt.Errorf("[]byte must be 16 bytes to convert to UUID: %d", len(value)) } - *dst = UUID{Status: Present} + *dst = UUID{Valid: true} copy(dst.Bytes[:], value) } else { - *dst = UUID{Status: Null} + *dst = UUID{} } case string: uuid, err := parseUUID(value) if err != nil { return err } - *dst = UUID{Bytes: uuid, Status: Present} + *dst = UUID{Bytes: uuid, Valid: true} case *string: if value == nil { - *dst = UUID{Status: Null} + *dst = UUID{} } else { return dst.Set(*value) } @@ -61,40 +61,35 @@ func (dst *UUID) Set(src interface{}) error { } func (dst UUID) Get() interface{} { - switch dst.Status { - case Present: - return dst.Bytes - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst.Bytes } func (src *UUID) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - switch v := dst.(type) { - case *[16]byte: - *v = src.Bytes - return nil - case *[]byte: - *v = make([]byte, 16) - copy(*v, src.Bytes[:]) - return nil - case *string: - *v = encodeUUID(src.Bytes) - return nil - default: - if nextDst, retry := GetAssignToDstType(v); retry { - return src.AssignTo(nextDst) - } - } - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot assign %v into %T", src, dst) + switch v := dst.(type) { + case *[16]byte: + *v = src.Bytes + return nil + case *[]byte: + *v = make([]byte, 16) + copy(*v, src.Bytes[:]) + return nil + case *string: + *v = encodeUUID(src.Bytes) + return nil + default: + if nextDst, retry := GetAssignToDstType(v); retry { + return src.AssignTo(nextDst) + } + } + + return nil } // parseUUID converts a string UUID in standard form to a byte array. @@ -125,7 +120,7 @@ func encodeUUID(src [16]byte) string { func (dst *UUID) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = UUID{Status: Null} + *dst = UUID{} return nil } @@ -138,13 +133,13 @@ func (dst *UUID) DecodeText(ci *ConnInfo, src []byte) error { return err } - *dst = UUID{Bytes: buf, Status: Present} + *dst = UUID{Bytes: buf, Valid: true} return nil } func (dst *UUID) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = UUID{Status: Null} + *dst = UUID{} return nil } @@ -152,28 +147,22 @@ func (dst *UUID) DecodeBinary(ci *ConnInfo, src []byte) error { return fmt.Errorf("invalid length for UUID: %v", len(src)) } - *dst = UUID{Status: Present} + *dst = UUID{Valid: true} copy(dst.Bytes[:], src) return nil } func (src UUID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, encodeUUID(src.Bytes)...), nil } func (src UUID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } return append(buf, src.Bytes[:]...), nil @@ -182,7 +171,7 @@ func (src UUID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *UUID) Scan(src interface{}) error { if src == nil { - *dst = UUID{Status: Null} + *dst = UUID{} return nil } @@ -204,19 +193,15 @@ func (src UUID) Value() (driver.Value, error) { } func (src UUID) MarshalJSON() ([]byte, error) { - switch src.Status { - case Present: - var buff bytes.Buffer - buff.WriteByte('"') - buff.WriteString(encodeUUID(src.Bytes)) - buff.WriteByte('"') - return buff.Bytes(), nil - case Null: + if !src.Valid { return []byte("null"), nil - case Undefined: - return nil, errUndefined } - return nil, errBadStatus + + var buff bytes.Buffer + buff.WriteByte('"') + buff.WriteString(encodeUUID(src.Bytes)) + buff.WriteByte('"') + return buff.Bytes(), nil } func (dst *UUID) UnmarshalJSON(src []byte) error { diff --git a/uuid_array.go b/uuid_array.go index 00721ef9..98904f9f 100644 --- a/uuid_array.go +++ b/uuid_array.go @@ -14,13 +14,13 @@ import ( type UUIDArray struct { Elements []UUID Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *UUIDArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} return nil } @@ -36,9 +36,9 @@ func (dst *UUIDArray) Set(src interface{}) error { case [][16]byte: if value == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} } else { elements := make([]UUID, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *UUIDArray) Set(src interface{}) error { *dst = UUIDArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case [][]byte: if value == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} } else { elements := make([]UUID, len(value)) for i := range value { @@ -68,15 +68,15 @@ func (dst *UUIDArray) Set(src interface{}) error { *dst = UUIDArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []string: if value == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} } else { elements := make([]UUID, len(value)) for i := range value { @@ -87,15 +87,15 @@ func (dst *UUIDArray) Set(src interface{}) error { *dst = UUIDArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} } else { elements := make([]UUID, len(value)) for i := range value { @@ -106,20 +106,20 @@ func (dst *UUIDArray) Set(src interface{}) error { *dst = UUIDArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []UUID: if value == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} } else if len(value) == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} } else { *dst = UUIDArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -128,7 +128,7 @@ func (dst *UUIDArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} return nil } @@ -137,7 +137,7 @@ func (dst *UUIDArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for UUIDArray", src) } if elementsLength == 0 { - *dst = UUIDArray{Status: Present} + *dst = UUIDArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -150,7 +150,7 @@ func (dst *UUIDArray) Set(src interface{}) error { *dst = UUIDArray{ Elements: make([]UUID, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -217,102 +217,95 @@ func (dst *UUIDArray) setRecursive(value reflect.Value, index, dimension int) (i } func (dst UUIDArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *UUIDArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[][16]byte: - *v = make([][16]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[][]byte: - *v = make([][]byte, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[][16]byte: + *v = make([][16]byte, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[][]byte: + *v = make([][]byte, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *UUIDArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -364,7 +357,7 @@ func (src *UUIDArray) assignToRecursive(value reflect.Value, index, dimension in func (dst *UUIDArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} return nil } @@ -393,14 +386,14 @@ func (dst *UUIDArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = UUIDArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = UUIDArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *UUIDArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = UUIDArray{Status: Null} + *dst = UUIDArray{} return nil } @@ -411,7 +404,7 @@ func (dst *UUIDArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = UUIDArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = UUIDArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -436,16 +429,13 @@ func (dst *UUIDArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = UUIDArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = UUIDArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src UUIDArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -498,11 +488,8 @@ func (src UUIDArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src UUIDArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -516,7 +503,7 @@ func (src UUIDArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/uuid_array_test.go b/uuid_array_test.go index 7d822e7a..47afadff 100644 --- a/uuid_array_test.go +++ b/uuid_array_test.go @@ -13,41 +13,41 @@ func TestUUIDArrayTranscode(t *testing.T) { &pgtype.UUIDArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Status: pgtype.Null}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.UUIDArray{Status: pgtype.Null}, + &pgtype.UUIDArray{}, &pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, + {}, + {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -59,29 +59,29 @@ func TestUUIDArraySet(t *testing.T) { }{ { source: nil, - result: pgtype.UUIDArray{Status: pgtype.Null}, + result: pgtype.UUIDArray{}, }, { source: [][16]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][16]byte{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, + result: pgtype.UUIDArray{Valid: true}, }, { source: ([][16]byte)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, + result: pgtype.UUIDArray{}, }, { source: [][]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][]byte{ @@ -92,36 +92,36 @@ func TestUUIDArraySet(t *testing.T) { }, result: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Status: pgtype.Null}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 4}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][]byte{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, + result: pgtype.UUIDArray{Valid: true}, }, { source: ([][]byte)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, + result: pgtype.UUIDArray{}, }, { source: []string{"00010203-0405-0607-0809-0a0b0c0d0e0f"}, result: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: []string{}, - result: pgtype.UUIDArray{Status: pgtype.Present}, + result: pgtype.UUIDArray{Valid: true}, }, { source: ([]string)(nil), - result: pgtype.UUIDArray{Status: pgtype.Null}, + result: pgtype.UUIDArray{}, }, { source: [][][16]byte{{ @@ -129,10 +129,10 @@ func TestUUIDArraySet(t *testing.T) { {{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}}}, result: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]string{ @@ -146,18 +146,18 @@ func TestUUIDArraySet(t *testing.T) { "50515253-5455-5657-5859-5a5b5c5d5e5f"}}}}, result: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, - {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, + {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Valid: true}, + {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][16]byte{{ @@ -165,10 +165,10 @@ func TestUUIDArraySet(t *testing.T) { {{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}}}, result: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]string{ @@ -182,18 +182,18 @@ func TestUUIDArraySet(t *testing.T) { "50515253-5455-5657-5859-5a5b5c5d5e5f"}}}}, result: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, - {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, + {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Valid: true}, + {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -227,63 +227,63 @@ func TestUUIDArrayAssignTo(t *testing.T) { }{ { src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &byteArraySlice, expected: [][16]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, }, { - src: pgtype.UUIDArray{Status: pgtype.Null}, + src: pgtype.UUIDArray{}, dst: &byteArraySlice, expected: ([][16]byte)(nil), }, { src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &byteSliceSlice, expected: [][]byte{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, }, { - src: pgtype.UUIDArray{Status: pgtype.Null}, + src: pgtype.UUIDArray{}, dst: &byteSliceSlice, expected: ([][]byte)(nil), }, { - src: pgtype.UUIDArray{Status: pgtype.Present}, + src: pgtype.UUIDArray{Valid: true}, dst: &byteSlice, expected: []byte{}, }, { - src: pgtype.UUIDArray{Status: pgtype.Present}, + src: pgtype.UUIDArray{Valid: true}, dst: &stringSlice, expected: []string{}, }, { src: pgtype.UUIDArray{ - Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}}, + Elements: []pgtype.UUID{{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, expected: []string{"00010203-0405-0607-0809-0a0b0c0d0e0f"}, }, { - src: pgtype.UUIDArray{Status: pgtype.Null}, + src: pgtype.UUIDArray{}, dst: &stringSlice, expected: ([]string)(nil), }, { src: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteArraySliceDim2, expected: [][][16]byte{{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, @@ -292,18 +292,18 @@ func TestUUIDArrayAssignTo(t *testing.T) { { src: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, - {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, + {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Valid: true}, + {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim4, expected: [][][][]string{ {{{ @@ -318,10 +318,10 @@ func TestUUIDArrayAssignTo(t *testing.T) { { src: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &byteArrayDim2, expected: [2][1][16]byte{{ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}, @@ -330,18 +330,18 @@ func TestUUIDArrayAssignTo(t *testing.T) { { src: pgtype.UUIDArray{ Elements: []pgtype.UUID{ - {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Status: pgtype.Present}, - {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Status: pgtype.Present}, - {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Status: pgtype.Present}, - {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Status: pgtype.Present}, - {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Status: pgtype.Present}}, + {Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + {Bytes: [16]byte{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, Valid: true}, + {Bytes: [16]byte{32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, Valid: true}, + {Bytes: [16]byte{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, Valid: true}, + {Bytes: [16]byte{64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79}, Valid: true}, + {Bytes: [16]byte{80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95}, Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, expected: [2][1][1][3]string{ {{{ diff --git a/uuid_test.go b/uuid_test.go index 5a93ea8d..887f45dd 100644 --- a/uuid_test.go +++ b/uuid_test.go @@ -7,12 +7,13 @@ import ( "github.com/jackc/pgtype" "github.com/jackc/pgtype/testutil" + "github.com/stretchr/testify/require" ) func TestUUIDTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{ - &pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, - &pgtype.UUID{Status: pgtype.Null}, + &pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, + &pgtype.UUID{}, }) } @@ -29,31 +30,31 @@ func TestUUIDSet(t *testing.T) { }{ { source: nil, - result: pgtype.UUID{Status: pgtype.Null}, + result: pgtype.UUID{}, }, { source: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: SomeUUIDType{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: ([]byte)(nil), - result: pgtype.UUID{Status: pgtype.Null}, + result: pgtype.UUID{}, }, { source: "00010203-0405-0607-0809-0a0b0c0d0e0f", - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, { source: "000102030405060708090a0b0c0d0e0f", - result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}, + result: pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, }, } @@ -72,7 +73,7 @@ func TestUUIDSet(t *testing.T) { func TestUUIDAssignTo(t *testing.T) { { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst [16]byte expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -87,7 +88,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst []byte expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -102,7 +103,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst SomeUUIDType expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -117,7 +118,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst string expected := "00010203-0405-0607-0809-0a0b0c0d0e0f" @@ -132,7 +133,7 @@ func TestUUIDAssignTo(t *testing.T) { } { - src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present} + src := pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true} var dst SomeUUIDWrapper expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} @@ -149,46 +150,30 @@ func TestUUIDAssignTo(t *testing.T) { func TestUUID_MarshalJSON(t *testing.T) { tests := []struct { - name string - src pgtype.UUID - want []byte - wantErr bool + name string + src pgtype.UUID + want []byte }{ { name: "first", src: pgtype.UUID{ - Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122}, - Status: pgtype.Present, + Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122}, + Valid: true, }, - want: []byte(`"1d485a7a-6d18-4599-8c6c-34425616887a"`), - wantErr: false, - }, - { - name: "second", - src: pgtype.UUID{ - Bytes: [16]byte{}, - Status: pgtype.Undefined, - }, - want: nil, - wantErr: true, + want: []byte(`"1d485a7a-6d18-4599-8c6c-34425616887a"`), }, { name: "third", src: pgtype.UUID{ - Bytes: [16]byte{}, - Status: pgtype.Null, + Bytes: [16]byte{}, }, - want: []byte("null"), - wantErr: false, + want: []byte("null"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := tt.src.MarshalJSON() - if (err != nil) != tt.wantErr { - t.Errorf("MarshalJSON() error = %v, wantErr %v", err, tt.wantErr) - return - } + require.NoError(t, err) if !reflect.DeepEqual(got, tt.want) { t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want) } @@ -206,8 +191,8 @@ func TestUUID_UnmarshalJSON(t *testing.T) { { name: "first", want: &pgtype.UUID{ - Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122}, - Status: pgtype.Present, + Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122}, + Valid: true, }, src: []byte(`"1d485a7a-6d18-4599-8c6c-34425616887a"`), wantErr: false, @@ -215,8 +200,7 @@ func TestUUID_UnmarshalJSON(t *testing.T) { { name: "second", want: &pgtype.UUID{ - Bytes: [16]byte{}, - Status: pgtype.Null, + Bytes: [16]byte{}, }, src: []byte("null"), wantErr: false, @@ -224,8 +208,8 @@ func TestUUID_UnmarshalJSON(t *testing.T) { { name: "third", want: &pgtype.UUID{ - Bytes: [16]byte{}, - Status: pgtype.Undefined, + Bytes: [16]byte{}, + Valid: false, }, src: []byte("1d485a7a-6d18-4599-8c6c-34425616887a"), wantErr: true, diff --git a/varbit.go b/varbit.go index f24dc5bc..bc6fdac4 100644 --- a/varbit.go +++ b/varbit.go @@ -9,9 +9,9 @@ import ( ) type Varbit struct { - Bytes []byte - Len int32 // Number of bits - Status Status + Bytes []byte + Len int32 // Number of bits + Valid bool } func (dst *Varbit) Set(src interface{}) error { @@ -19,14 +19,10 @@ func (dst *Varbit) Set(src interface{}) error { } func (dst Varbit) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *Varbit) AssignTo(dst interface{}) error { @@ -35,7 +31,7 @@ func (src *Varbit) AssignTo(dst interface{}) error { func (dst *Varbit) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Varbit{Status: Null} + *dst = Varbit{} return nil } @@ -54,13 +50,13 @@ func (dst *Varbit) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = Varbit{Bytes: buf, Len: int32(bitLen), Status: Present} + *dst = Varbit{Bytes: buf, Len: int32(bitLen), Valid: true} return nil } func (dst *Varbit) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = Varbit{Status: Null} + *dst = Varbit{} return nil } @@ -71,16 +67,13 @@ func (dst *Varbit) DecodeBinary(ci *ConnInfo, src []byte) error { bitLen := int32(binary.BigEndian.Uint32(src)) rp := 4 - *dst = Varbit{Bytes: src[rp:], Len: bitLen, Status: Present} + *dst = Varbit{Bytes: src[rp:], Len: bitLen, Valid: true} return nil } func (src Varbit) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } for i := int32(0); i < src.Len; i++ { @@ -97,11 +90,8 @@ func (src Varbit) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src Varbit) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } buf = pgio.AppendInt32(buf, src.Len) @@ -111,7 +101,7 @@ func (src Varbit) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { // Scan implements the database/sql Scanner interface. func (dst *Varbit) Scan(src interface{}) error { if src == nil { - *dst = Varbit{Status: Null} + *dst = Varbit{} return nil } diff --git a/varbit_test.go b/varbit_test.go index 3c5aea1e..b81bdc0e 100644 --- a/varbit_test.go +++ b/varbit_test.go @@ -9,10 +9,10 @@ import ( func TestVarbitTranscode(t *testing.T) { testutil.TestSuccessfulTranscode(t, "varbit", []interface{}{ - &pgtype.Varbit{Bytes: []byte{}, Len: 0, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present}, - &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Status: pgtype.Present}, - &pgtype.Varbit{Status: pgtype.Null}, + &pgtype.Varbit{Bytes: []byte{}, Len: 0, Valid: true}, + &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Valid: true}, + &pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 128}, Len: 33, Valid: true}, + &pgtype.Varbit{}, }) } @@ -20,7 +20,7 @@ func TestVarbitNormalize(t *testing.T) { testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{ { SQL: "select B'111111111'", - Value: &pgtype.Varbit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present}, + Value: &pgtype.Varbit{Bytes: []byte{255, 128}, Len: 9, Valid: true}, }, }) } diff --git a/varchar_array.go b/varchar_array.go index 8a309a3f..3e0913dc 100644 --- a/varchar_array.go +++ b/varchar_array.go @@ -14,13 +14,13 @@ import ( type VarcharArray struct { Elements []Varchar Dimensions []ArrayDimension - Status Status + Valid bool } func (dst *VarcharArray) Set(src interface{}) error { // untyped nil and typed nil interfaces are different if src == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} return nil } @@ -36,9 +36,9 @@ func (dst *VarcharArray) Set(src interface{}) error { case []string: if value == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} } else if len(value) == 0 { - *dst = VarcharArray{Status: Present} + *dst = VarcharArray{Valid: true} } else { elements := make([]Varchar, len(value)) for i := range value { @@ -49,15 +49,15 @@ func (dst *VarcharArray) Set(src interface{}) error { *dst = VarcharArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []*string: if value == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} } else if len(value) == 0 { - *dst = VarcharArray{Status: Present} + *dst = VarcharArray{Valid: true} } else { elements := make([]Varchar, len(value)) for i := range value { @@ -68,20 +68,20 @@ func (dst *VarcharArray) Set(src interface{}) error { *dst = VarcharArray{ Elements: elements, Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, - Status: Present, + Valid: true, } } case []Varchar: if value == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} } else if len(value) == 0 { - *dst = VarcharArray{Status: Present} + *dst = VarcharArray{Valid: true} } else { *dst = VarcharArray{ Elements: value, Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, - Status: Present, + Valid: true, } } default: @@ -90,7 +90,7 @@ func (dst *VarcharArray) Set(src interface{}) error { // but it comes with a 20-50% performance penalty for large arrays/slices reflectedValue := reflect.ValueOf(src) if !reflectedValue.IsValid() || reflectedValue.IsZero() { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} return nil } @@ -99,7 +99,7 @@ func (dst *VarcharArray) Set(src interface{}) error { return fmt.Errorf("cannot find dimensions of %v for VarcharArray", src) } if elementsLength == 0 { - *dst = VarcharArray{Status: Present} + *dst = VarcharArray{Valid: true} return nil } if len(dimensions) == 0 { @@ -112,7 +112,7 @@ func (dst *VarcharArray) Set(src interface{}) error { *dst = VarcharArray{ Elements: make([]Varchar, elementsLength), Dimensions: dimensions, - Status: Present, + Valid: true, } elementCount, err := dst.setRecursive(reflectedValue, 0, 0) if err != nil { @@ -179,84 +179,77 @@ func (dst *VarcharArray) setRecursive(value reflect.Value, index, dimension int) } func (dst VarcharArray) Get() interface{} { - switch dst.Status { - case Present: - return dst - case Null: + if !dst.Valid { return nil - default: - return dst.Status } + return dst } func (src *VarcharArray) AssignTo(dst interface{}) error { - switch src.Status { - case Present: - if len(src.Dimensions) <= 1 { - // Attempt to match to select common types: - switch v := dst.(type) { - - case *[]string: - *v = make([]string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - case *[]*string: - *v = make([]*string, len(src.Elements)) - for i := range src.Elements { - if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { - return err - } - } - return nil - - } - } - - // Try to convert to something AssignTo can use directly. - if nextDst, retry := GetAssignToDstType(dst); retry { - return src.AssignTo(nextDst) - } - - // Fallback to reflection if an optimised match was not found. - // The reflection is necessary for arrays and multidimensional slices, - // but it comes with a 20-50% performance penalty for large arrays/slices - value := reflect.ValueOf(dst) - if value.Kind() == reflect.Ptr { - value = value.Elem() - } - - switch value.Kind() { - case reflect.Array, reflect.Slice: - default: - return fmt.Errorf("cannot assign %T to %T", src, dst) - } - - if len(src.Elements) == 0 { - if value.Kind() == reflect.Slice { - value.Set(reflect.MakeSlice(value.Type(), 0, 0)) - return nil - } - } - - elementCount, err := src.assignToRecursive(value, 0, 0) - if err != nil { - return err - } - if elementCount != len(src.Elements) { - return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) - } - - return nil - case Null: + if !src.Valid { return NullAssignTo(dst) } - return fmt.Errorf("cannot decode %#v into %T", src, dst) + if len(src.Dimensions) <= 1 { + // Attempt to match to select common types: + switch v := dst.(type) { + + case *[]string: + *v = make([]string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + case *[]*string: + *v = make([]*string, len(src.Elements)) + for i := range src.Elements { + if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { + return err + } + } + return nil + + } + } + + // Try to convert to something AssignTo can use directly. + if nextDst, retry := GetAssignToDstType(dst); retry { + return src.AssignTo(nextDst) + } + + // Fallback to reflection if an optimised match was not found. + // The reflection is necessary for arrays and multidimensional slices, + // but it comes with a 20-50% performance penalty for large arrays/slices + value := reflect.ValueOf(dst) + if value.Kind() == reflect.Ptr { + value = value.Elem() + } + + switch value.Kind() { + case reflect.Array, reflect.Slice: + default: + return fmt.Errorf("cannot assign %T to %T", src, dst) + } + + if len(src.Elements) == 0 { + if value.Kind() == reflect.Slice { + value.Set(reflect.MakeSlice(value.Type(), 0, 0)) + return nil + } + } + + elementCount, err := src.assignToRecursive(value, 0, 0) + if err != nil { + return err + } + if elementCount != len(src.Elements) { + return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount) + } + + return nil } func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) { @@ -308,7 +301,7 @@ func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension func (dst *VarcharArray) DecodeText(ci *ConnInfo, src []byte) error { if src == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} return nil } @@ -337,14 +330,14 @@ func (dst *VarcharArray) DecodeText(ci *ConnInfo, src []byte) error { } } - *dst = VarcharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + *dst = VarcharArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true} return nil } func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error { if src == nil { - *dst = VarcharArray{Status: Null} + *dst = VarcharArray{} return nil } @@ -355,7 +348,7 @@ func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error { } if len(arrayHeader.Dimensions) == 0 { - *dst = VarcharArray{Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = VarcharArray{Dimensions: arrayHeader.Dimensions, Valid: true} return nil } @@ -380,16 +373,13 @@ func (dst *VarcharArray) DecodeBinary(ci *ConnInfo, src []byte) error { } } - *dst = VarcharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} + *dst = VarcharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true} return nil } func (src VarcharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } if len(src.Dimensions) == 0 { @@ -442,11 +432,8 @@ func (src VarcharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { } func (src VarcharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { - switch src.Status { - case Null: + if !src.Valid { return nil, nil - case Undefined: - return nil, errUndefined } arrayHeader := ArrayHeader{ @@ -460,7 +447,7 @@ func (src VarcharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { } for i := range src.Elements { - if src.Elements[i].Status == Null { + if !src.Elements[i].Valid { arrayHeader.ContainsNull = true break } diff --git a/varchar_array_test.go b/varchar_array_test.go index 5fb7326d..cf0efd6d 100644 --- a/varchar_array_test.go +++ b/varchar_array_test.go @@ -13,41 +13,41 @@ func TestVarcharArrayTranscode(t *testing.T) { &pgtype.VarcharArray{ Elements: nil, Dimensions: nil, - Status: pgtype.Present, + Valid: true, }, &pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {Status: pgtype.Null}, + {String: "foo", Valid: true}, + {}, }, Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, - &pgtype.VarcharArray{Status: pgtype.Null}, + &pgtype.VarcharArray{}, &pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "bar ", Status: pgtype.Present}, - {String: "NuLL", Status: pgtype.Present}, - {String: `wow"quz\`, Status: pgtype.Present}, - {String: "", Status: pgtype.Present}, - {Status: pgtype.Null}, - {String: "null", Status: pgtype.Present}, + {String: "bar ", Valid: true}, + {String: "NuLL", Valid: true}, + {String: `wow"quz\`, Valid: true}, + {String: "", Valid: true}, + {}, + {String: "null", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}}, - Status: pgtype.Present, + Valid: true, }, &pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "quz", Status: pgtype.Present}, - {String: "foo", Status: pgtype.Present}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "quz", Valid: true}, + {String: "foo", Valid: true}, }, Dimensions: []pgtype.ArrayDimension{ {Length: 2, LowerBound: 4}, {Length: 2, LowerBound: 2}, }, - Status: pgtype.Present, + Valid: true, }, }) } @@ -60,61 +60,61 @@ func TestVarcharArraySet(t *testing.T) { { source: []string{"foo"}, result: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: (([]string)(nil)), - result: pgtype.VarcharArray{Status: pgtype.Null}, + result: pgtype.VarcharArray{}, }, { source: [][]string{{"foo"}, {"bar"}}, result: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1]string{{"foo"}, {"bar"}}, result: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, }, { source: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, result: pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, }, } @@ -147,81 +147,81 @@ func TestVarcharArrayAssignTo(t *testing.T) { }{ { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, expected: []string{"foo"}, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &namedStringSlice, expected: _stringSlice{"bar"}, }, { - src: pgtype.VarcharArray{Status: pgtype.Null}, + src: pgtype.VarcharArray{}, dst: &stringSlice, expected: (([]string)(nil)), }, { - src: pgtype.VarcharArray{Status: pgtype.Present}, + src: pgtype.VarcharArray{Valid: true}, dst: &stringSlice, expected: []string{}, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim2, expected: [][]string{{"foo"}, {"bar"}}, }, { src: pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSliceDim4, expected: [][][][]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, expected: [2][1]string{{"foo"}, {"bar"}}, }, { src: pgtype.VarcharArray{ Elements: []pgtype.Varchar{ - {String: "foo", Status: pgtype.Present}, - {String: "bar", Status: pgtype.Present}, - {String: "baz", Status: pgtype.Present}, - {String: "wibble", Status: pgtype.Present}, - {String: "wobble", Status: pgtype.Present}, - {String: "wubble", Status: pgtype.Present}}, + {String: "foo", Valid: true}, + {String: "bar", Valid: true}, + {String: "baz", Valid: true}, + {String: "wibble", Valid: true}, + {String: "wobble", Valid: true}, + {String: "wubble", Valid: true}}, Dimensions: []pgtype.ArrayDimension{ {LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 3}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, expected: [2][1][1][3]string{{{{"foo", "bar", "baz"}}}, {{{"wibble", "wobble", "wubble"}}}}, }, @@ -244,31 +244,31 @@ func TestVarcharArrayAssignTo(t *testing.T) { }{ { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{Status: pgtype.Null}}, + Elements: []pgtype.Varchar{{}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}}, - Status: pgtype.Present, + Valid: true, }, dst: &stringSlice, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim2, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringSlice, }, { src: pgtype.VarcharArray{ - Elements: []pgtype.Varchar{{String: "foo", Status: pgtype.Present}, {String: "bar", Status: pgtype.Present}}, + Elements: []pgtype.Varchar{{String: "foo", Valid: true}, {String: "bar", Valid: true}}, Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}}, - Status: pgtype.Present}, + Valid: true}, dst: &stringArrayDim4, }, } diff --git a/xid_test.go b/xid_test.go index 531867f6..fab10f79 100644 --- a/xid_test.go +++ b/xid_test.go @@ -11,8 +11,8 @@ import ( func TestXIDTranscode(t *testing.T) { pgTypeName := "xid" values := []interface{}{ - &pgtype.XID{Uint: 42, Status: pgtype.Present}, - &pgtype.XID{Status: pgtype.Null}, + &pgtype.XID{Uint: 42, Valid: true}, + &pgtype.XID{}, } eqFunc := func(a, b interface{}) bool { return reflect.DeepEqual(a, b) @@ -27,7 +27,7 @@ func TestXIDSet(t *testing.T) { source interface{} result pgtype.XID }{ - {source: uint32(1), result: pgtype.XID{Uint: 1, Status: pgtype.Present}}, + {source: uint32(1), result: pgtype.XID{Uint: 1, Valid: true}}, } for i, tt := range successfulTests { @@ -52,8 +52,8 @@ func TestXIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.XID{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)}, - {src: pgtype.XID{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))}, + {src: pgtype.XID{Uint: 42, Valid: true}, dst: &ui32, expected: uint32(42)}, + {src: pgtype.XID{}, dst: &pui32, expected: ((*uint32)(nil))}, } for i, tt := range simpleTests { @@ -72,7 +72,7 @@ func TestXIDAssignTo(t *testing.T) { dst interface{} expected interface{} }{ - {src: pgtype.XID{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(42)}, + {src: pgtype.XID{Uint: 42, Valid: true}, dst: &pui32, expected: uint32(42)}, } for i, tt := range pointerAllocTests { @@ -90,7 +90,7 @@ func TestXIDAssignTo(t *testing.T) { src pgtype.XID dst interface{} }{ - {src: pgtype.XID{Status: pgtype.Null}, dst: &ui32}, + {src: pgtype.XID{}, dst: &ui32}, } for i, tt := range errorTests { diff --git a/zeronull/float8.go b/zeronull/float8.go index ebc86ac3..07d5e1a5 100644 --- a/zeronull/float8.go +++ b/zeronull/float8.go @@ -15,7 +15,7 @@ func (dst *Float8) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Float8(nullable.Float) } else { *dst = 0 @@ -31,7 +31,7 @@ func (dst *Float8) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Float8(nullable.Float) } else { *dst = 0 @@ -46,8 +46,8 @@ func (src Float8) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Float8{ - Float: float64(src), - Status: pgtype.Present, + Float: float64(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -59,8 +59,8 @@ func (src Float8) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) } nullable := pgtype.Float8{ - Float: float64(src), - Status: pgtype.Present, + Float: float64(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/int2.go b/zeronull/int2.go index a528642f..b3f9c328 100644 --- a/zeronull/int2.go +++ b/zeronull/int2.go @@ -15,7 +15,7 @@ func (dst *Int2) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int2(nullable.Int) } else { *dst = 0 @@ -31,7 +31,7 @@ func (dst *Int2) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int2(nullable.Int) } else { *dst = 0 @@ -46,8 +46,8 @@ func (src Int2) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int2{ - Int: int16(src), - Status: pgtype.Present, + Int: int16(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -59,8 +59,8 @@ func (src Int2) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int2{ - Int: int16(src), - Status: pgtype.Present, + Int: int16(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/int4.go b/zeronull/int4.go index c539e43a..3efca4e6 100644 --- a/zeronull/int4.go +++ b/zeronull/int4.go @@ -15,7 +15,7 @@ func (dst *Int4) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int4(nullable.Int) } else { *dst = 0 @@ -31,7 +31,7 @@ func (dst *Int4) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int4(nullable.Int) } else { *dst = 0 @@ -46,8 +46,8 @@ func (src Int4) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int4{ - Int: int32(src), - Status: pgtype.Present, + Int: int32(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -59,8 +59,8 @@ func (src Int4) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int4{ - Int: int32(src), - Status: pgtype.Present, + Int: int32(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/int8.go b/zeronull/int8.go index 19774645..5cb063d8 100644 --- a/zeronull/int8.go +++ b/zeronull/int8.go @@ -15,7 +15,7 @@ func (dst *Int8) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int8(nullable.Int) } else { *dst = 0 @@ -31,7 +31,7 @@ func (dst *Int8) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Int8(nullable.Int) } else { *dst = 0 @@ -46,8 +46,8 @@ func (src Int8) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int8{ - Int: int64(src), - Status: pgtype.Present, + Int: int64(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -59,8 +59,8 @@ func (src Int8) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.Int8{ - Int: int64(src), - Status: pgtype.Present, + Int: int64(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/text.go b/zeronull/text.go index 8e79fc6a..afcb1a42 100644 --- a/zeronull/text.go +++ b/zeronull/text.go @@ -15,7 +15,7 @@ func (dst *Text) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Text(nullable.String) } else { *dst = Text("") @@ -31,7 +31,7 @@ func (dst *Text) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Text(nullable.String) } else { *dst = Text("") @@ -47,7 +47,7 @@ func (src Text) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { nullable := pgtype.Text{ String: string(src), - Status: pgtype.Present, + Valid: true, } return nullable.EncodeText(ci, buf) @@ -60,7 +60,7 @@ func (src Text) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { nullable := pgtype.Text{ String: string(src), - Status: pgtype.Present, + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/timestamp.go b/zeronull/timestamp.go index a94c67cc..61787818 100644 --- a/zeronull/timestamp.go +++ b/zeronull/timestamp.go @@ -16,7 +16,7 @@ func (dst *Timestamp) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Timestamp(nullable.Time) } else { *dst = Timestamp{} @@ -32,7 +32,7 @@ func (dst *Timestamp) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Timestamp(nullable.Time) } else { *dst = Timestamp{} @@ -47,8 +47,8 @@ func (src Timestamp) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) } nullable := pgtype.Timestamp{ - Time: time.Time(src), - Status: pgtype.Present, + Time: time.Time(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -60,8 +60,8 @@ func (src Timestamp) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, erro } nullable := pgtype.Timestamp{ - Time: time.Time(src), - Status: pgtype.Present, + Time: time.Time(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/timestamptz.go b/zeronull/timestamptz.go index c641ca10..4896e9b7 100644 --- a/zeronull/timestamptz.go +++ b/zeronull/timestamptz.go @@ -16,7 +16,7 @@ func (dst *Timestamptz) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Timestamptz(nullable.Time) } else { *dst = Timestamptz{} @@ -32,7 +32,7 @@ func (dst *Timestamptz) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = Timestamptz(nullable.Time) } else { *dst = Timestamptz{} @@ -47,8 +47,8 @@ func (src Timestamptz) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, erro } nullable := pgtype.Timestamptz{ - Time: time.Time(src), - Status: pgtype.Present, + Time: time.Time(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -60,8 +60,8 @@ func (src Timestamptz) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, er } nullable := pgtype.Timestamptz{ - Time: time.Time(src), - Status: pgtype.Present, + Time: time.Time(src), + Valid: true, } return nullable.EncodeBinary(ci, buf) diff --git a/zeronull/uuid.go b/zeronull/uuid.go index 18fc667e..25211122 100644 --- a/zeronull/uuid.go +++ b/zeronull/uuid.go @@ -15,7 +15,7 @@ func (dst *UUID) DecodeText(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = UUID(nullable.Bytes) } else { *dst = UUID{} @@ -31,7 +31,7 @@ func (dst *UUID) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error { return err } - if nullable.Status == pgtype.Present { + if nullable.Valid { *dst = UUID(nullable.Bytes) } else { *dst = UUID{} @@ -46,8 +46,8 @@ func (src UUID) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.UUID{ - Bytes: [16]byte(src), - Status: pgtype.Present, + Bytes: [16]byte(src), + Valid: true, } return nullable.EncodeText(ci, buf) @@ -59,8 +59,8 @@ func (src UUID) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) { } nullable := pgtype.UUID{ - Bytes: [16]byte(src), - Status: pgtype.Present, + Bytes: [16]byte(src), + Valid: true, } return nullable.EncodeBinary(ci, buf)