From b90570feb55e00d4977de788e840bd3b10d5414f Mon Sep 17 00:00:00 2001 From: Simo Haasanen Date: Sat, 8 Aug 2020 19:51:37 +0100 Subject: [PATCH] Restored more optimised array type conversions for a few select 1D-slice types. Results of calls to the reflect lib are now stored as local variables for small performance gains. --- aclitem_array.go | 188 ++++++++++---- bool_array.go | 188 ++++++++++---- bpchar_array.go | 188 ++++++++++---- bytea_array.go | 160 ++++++++---- cidr_array.go | 217 ++++++++++++---- date_array.go | 189 ++++++++++---- enum_array.go | 188 ++++++++++---- float4_array.go | 188 ++++++++++---- float8_array.go | 188 ++++++++++---- hstore_array.go | 160 ++++++++---- inet_array.go | 217 ++++++++++++---- int2_array.go | 580 +++++++++++++++++++++++++++++++++++++++---- int4_array.go | 580 +++++++++++++++++++++++++++++++++++++++---- int8_array.go | 580 +++++++++++++++++++++++++++++++++++++++---- jsonb_array.go | 160 ++++++++---- macaddr_array.go | 189 ++++++++++---- numeric_array.go | 356 ++++++++++++++++++++++---- text_array.go | 188 ++++++++++---- timestamp_array.go | 189 ++++++++++---- timestamptz_array.go | 189 ++++++++++---- tstzrange_array.go | 143 +++++++---- typed_array.go.erb | 164 ++++++++---- typed_array_gen.sh | 46 ++-- uuid_array.go | 244 ++++++++++++++---- varchar_array.go | 188 ++++++++++---- 25 files changed, 4594 insertions(+), 1273 deletions(-) diff --git a/aclitem_array.go b/aclitem_array.go index 09a64fb6..52b67d85 100644 --- a/aclitem_array.go +++ b/aclitem_array.go @@ -29,56 +29,110 @@ func (dst *ACLItemArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = ACLItemArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for ACLItemArray", src) - } - if elementsLength == 0 { - *dst = ACLItemArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to ACLItemArray", src) - } - - *dst = ACLItemArray{ - Elements: make([]ACLItem, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = ACLItemArray{Status: Null} + } else if len(value) == 0 { + *dst = ACLItemArray{Status: Present} + } else { + elements := make([]ACLItem, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]ACLItem, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = ACLItemArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = ACLItemArray{Status: Null} + } else if len(value) == 0 { + *dst = ACLItemArray{Status: Present} + } else { + elements := make([]ACLItem, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = ACLItemArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []ACLItem: + if value == nil { + *dst = ACLItemArray{Status: Null} + } else if len(value) == 0 { + *dst = ACLItemArray{Status: Present} + } else { + *dst = ACLItemArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = ACLItemArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for ACLItemArray", src) + } + if elementsLength == 0 { + *dst = ACLItemArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to ACLItemArray", src) + } + + *dst = ACLItemArray{ + Elements: make([]ACLItem, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]ACLItem, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to ACLItemArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to ACLItemArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -93,10 +147,11 @@ func (dst *ACLItemArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -131,6 +186,30 @@ func (dst ACLItemArray) Get() interface{} { func (src *ACLItemArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -169,10 +248,12 @@ func (src *ACLItemArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -190,11 +271,14 @@ func (src *ACLItemArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from ACLItemArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from ACLItemArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/bool_array.go b/bool_array.go index 6569d5ca..6a4b3454 100644 --- a/bool_array.go +++ b/bool_array.go @@ -31,56 +31,110 @@ func (dst *BoolArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = BoolArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for BoolArray", src) - } - if elementsLength == 0 { - *dst = BoolArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to BoolArray", src) - } - - *dst = BoolArray{ - Elements: make([]Bool, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []bool: + if value == nil { + *dst = BoolArray{Status: Null} + } else if len(value) == 0 { + *dst = BoolArray{Status: Present} + } else { + elements := make([]Bool, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Bool, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = BoolArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*bool: + if value == nil { + *dst = BoolArray{Status: Null} + } else if len(value) == 0 { + *dst = BoolArray{Status: Present} + } else { + elements := make([]Bool, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = BoolArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Bool: + if value == nil { + *dst = BoolArray{Status: Null} + } else if len(value) == 0 { + *dst = BoolArray{Status: Present} + } else { + *dst = BoolArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = BoolArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for BoolArray", src) + } + if elementsLength == 0 { + *dst = BoolArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to BoolArray", src) + } + + *dst = BoolArray{ + Elements: make([]Bool, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Bool, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to BoolArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to BoolArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *BoolArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst BoolArray) Get() interface{} { func (src *BoolArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *BoolArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *BoolArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from BoolArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from BoolArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/bpchar_array.go b/bpchar_array.go index 8aef8330..1f79a3fe 100644 --- a/bpchar_array.go +++ b/bpchar_array.go @@ -31,56 +31,110 @@ func (dst *BPCharArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = BPCharArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for BPCharArray", src) - } - if elementsLength == 0 { - *dst = BPCharArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to BPCharArray", src) - } - - *dst = BPCharArray{ - Elements: make([]BPChar, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = BPCharArray{Status: Null} + } else if len(value) == 0 { + *dst = BPCharArray{Status: Present} + } else { + elements := make([]BPChar, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]BPChar, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = BPCharArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = BPCharArray{Status: Null} + } else if len(value) == 0 { + *dst = BPCharArray{Status: Present} + } else { + elements := make([]BPChar, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = BPCharArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []BPChar: + if value == nil { + *dst = BPCharArray{Status: Null} + } else if len(value) == 0 { + *dst = BPCharArray{Status: Present} + } else { + *dst = BPCharArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = BPCharArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for BPCharArray", src) + } + if elementsLength == 0 { + *dst = BPCharArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to BPCharArray", src) + } + + *dst = BPCharArray{ + Elements: make([]BPChar, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]BPChar, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to BPCharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to BPCharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *BPCharArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst BPCharArray) Get() interface{} { func (src *BPCharArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *BPCharArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *BPCharArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from BPCharArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from BPCharArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/bytea_array.go b/bytea_array.go index 3addb99a..17136554 100644 --- a/bytea_array.go +++ b/bytea_array.go @@ -31,56 +31,91 @@ func (dst *ByteaArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = ByteaArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for ByteaArray", src) - } - if elementsLength == 0 { - *dst = ByteaArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to ByteaArray", src) - } - - *dst = ByteaArray{ - Elements: make([]Bytea, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case [][]byte: + if value == nil { + *dst = ByteaArray{Status: Null} + } else if len(value) == 0 { + *dst = ByteaArray{Status: Present} + } else { + elements := make([]Bytea, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Bytea, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = ByteaArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Bytea: + if value == nil { + *dst = ByteaArray{Status: Null} + } else if len(value) == 0 { + *dst = ByteaArray{Status: Present} + } else { + *dst = ByteaArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = ByteaArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for ByteaArray", src) + } + if elementsLength == 0 { + *dst = ByteaArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to ByteaArray", src) + } + + *dst = ByteaArray{ + Elements: make([]Bytea, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Bytea, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to ByteaArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to ByteaArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +130,11 @@ func (dst *ByteaArray) setRecursive(value reflect.Value, index, dimension int) ( break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +169,21 @@ func (dst ByteaArray) Get() interface{} { func (src *ByteaArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +222,12 @@ func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension i length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +245,14 @@ func (src *ByteaArray) assignToRecursive(value reflect.Value, index, dimension i if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from ByteaArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from ByteaArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/cidr_array.go b/cidr_array.go index 1ef2f428..770c4b8c 100644 --- a/cidr_array.go +++ b/cidr_array.go @@ -5,6 +5,7 @@ package pgtype import ( "database/sql/driver" "encoding/binary" + "net" "reflect" "github.com/jackc/pgio" @@ -31,56 +32,129 @@ func (dst *CIDRArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = CIDRArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for CIDRArray", src) - } - if elementsLength == 0 { - *dst = CIDRArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to CIDRArray", src) - } - - *dst = CIDRArray{ - Elements: make([]CIDR, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []*net.IPNet: + if value == nil { + *dst = CIDRArray{Status: Null} + } else if len(value) == 0 { + *dst = CIDRArray{Status: Present} + } else { + elements := make([]CIDR, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]CIDR, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = CIDRArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []net.IP: + if value == nil { + *dst = CIDRArray{Status: Null} + } else if len(value) == 0 { + *dst = CIDRArray{Status: Present} + } else { + elements := make([]CIDR, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = CIDRArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*net.IP: + if value == nil { + *dst = CIDRArray{Status: Null} + } else if len(value) == 0 { + *dst = CIDRArray{Status: Present} + } else { + elements := make([]CIDR, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = CIDRArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []CIDR: + if value == nil { + *dst = CIDRArray{Status: Null} + } else if len(value) == 0 { + *dst = CIDRArray{Status: Present} + } else { + *dst = CIDRArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = CIDRArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for CIDRArray", src) + } + if elementsLength == 0 { + *dst = CIDRArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to CIDRArray", src) + } + + *dst = CIDRArray{ + Elements: make([]CIDR, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]CIDR, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to CIDRArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to CIDRArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +169,11 @@ func (dst *CIDRArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +208,39 @@ func (dst CIDRArray) Get() interface{} { func (src *CIDRArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +279,12 @@ func (src *CIDRArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +302,14 @@ func (src *CIDRArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from CIDRArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from CIDRArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/date_array.go b/date_array.go index 4ccdafe0..7ba93daa 100644 --- a/date_array.go +++ b/date_array.go @@ -6,6 +6,7 @@ import ( "database/sql/driver" "encoding/binary" "reflect" + "time" "github.com/jackc/pgio" errors "golang.org/x/xerrors" @@ -31,56 +32,110 @@ func (dst *DateArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = DateArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for DateArray", src) - } - if elementsLength == 0 { - *dst = DateArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to DateArray", src) - } - - *dst = DateArray{ - Elements: make([]Date, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []time.Time: + if value == nil { + *dst = DateArray{Status: Null} + } else if len(value) == 0 { + *dst = DateArray{Status: Present} + } else { + elements := make([]Date, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Date, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = DateArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*time.Time: + if value == nil { + *dst = DateArray{Status: Null} + } else if len(value) == 0 { + *dst = DateArray{Status: Present} + } else { + elements := make([]Date, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = DateArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Date: + if value == nil { + *dst = DateArray{Status: Null} + } else if len(value) == 0 { + *dst = DateArray{Status: Present} + } else { + *dst = DateArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = DateArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for DateArray", src) + } + if elementsLength == 0 { + *dst = DateArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to DateArray", src) + } + + *dst = DateArray{ + Elements: make([]Date, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Date, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to DateArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to DateArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +150,11 @@ func (dst *DateArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +189,30 @@ func (dst DateArray) Get() interface{} { func (src *DateArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +251,12 @@ func (src *DateArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +274,14 @@ func (src *DateArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from DateArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from DateArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/enum_array.go b/enum_array.go index 2c83db24..561d4495 100644 --- a/enum_array.go +++ b/enum_array.go @@ -29,56 +29,110 @@ func (dst *EnumArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = EnumArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for EnumArray", src) - } - if elementsLength == 0 { - *dst = EnumArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to EnumArray", src) - } - - *dst = EnumArray{ - Elements: make([]GenericText, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = EnumArray{Status: Null} + } else if len(value) == 0 { + *dst = EnumArray{Status: Present} + } else { + elements := make([]GenericText, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]GenericText, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = EnumArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = EnumArray{Status: Null} + } else if len(value) == 0 { + *dst = EnumArray{Status: Present} + } else { + elements := make([]GenericText, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = EnumArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []GenericText: + if value == nil { + *dst = EnumArray{Status: Null} + } else if len(value) == 0 { + *dst = EnumArray{Status: Present} + } else { + *dst = EnumArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = EnumArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for EnumArray", src) + } + if elementsLength == 0 { + *dst = EnumArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to EnumArray", src) + } + + *dst = EnumArray{ + Elements: make([]GenericText, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]GenericText, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to EnumArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to EnumArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -93,10 +147,11 @@ func (dst *EnumArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -131,6 +186,30 @@ func (dst EnumArray) Get() interface{} { func (src *EnumArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -169,10 +248,12 @@ func (src *EnumArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -190,11 +271,14 @@ func (src *EnumArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from EnumArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from EnumArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/float4_array.go b/float4_array.go index 78d1a860..829708e1 100644 --- a/float4_array.go +++ b/float4_array.go @@ -31,56 +31,110 @@ func (dst *Float4Array) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = Float4Array{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for Float4Array", src) - } - if elementsLength == 0 { - *dst = Float4Array{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float4Array", src) - } - - *dst = Float4Array{ - Elements: make([]Float4, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []float32: + if value == nil { + *dst = Float4Array{Status: Null} + } else if len(value) == 0 { + *dst = Float4Array{Status: Present} + } else { + elements := make([]Float4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Float4, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = Float4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*float32: + if value == nil { + *dst = Float4Array{Status: Null} + } else if len(value) == 0 { + *dst = Float4Array{Status: Present} + } else { + elements := make([]Float4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Float4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Float4: + if value == nil { + *dst = Float4Array{Status: Null} + } else if len(value) == 0 { + *dst = Float4Array{Status: Present} + } else { + *dst = Float4Array{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = Float4Array{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for Float4Array", src) + } + if elementsLength == 0 { + *dst = Float4Array{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to Float4Array", src) + } + + *dst = Float4Array{ + Elements: make([]Float4, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Float4, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to Float4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to Float4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *Float4Array) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst Float4Array) Get() interface{} { func (src *Float4Array) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *Float4Array) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *Float4Array) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from Float4Array") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from Float4Array") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/float8_array.go b/float8_array.go index 19223c52..6932cb88 100644 --- a/float8_array.go +++ b/float8_array.go @@ -31,56 +31,110 @@ func (dst *Float8Array) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = Float8Array{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for Float8Array", src) - } - if elementsLength == 0 { - *dst = Float8Array{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Float8Array", src) - } - - *dst = Float8Array{ - Elements: make([]Float8, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []float64: + if value == nil { + *dst = Float8Array{Status: Null} + } else if len(value) == 0 { + *dst = Float8Array{Status: Present} + } else { + elements := make([]Float8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Float8, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = Float8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*float64: + if value == nil { + *dst = Float8Array{Status: Null} + } else if len(value) == 0 { + *dst = Float8Array{Status: Present} + } else { + elements := make([]Float8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Float8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Float8: + if value == nil { + *dst = Float8Array{Status: Null} + } else if len(value) == 0 { + *dst = Float8Array{Status: Present} + } else { + *dst = Float8Array{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = Float8Array{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for Float8Array", src) + } + if elementsLength == 0 { + *dst = Float8Array{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to Float8Array", src) + } + + *dst = Float8Array{ + Elements: make([]Float8, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Float8, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to Float8Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to Float8Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *Float8Array) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst Float8Array) Get() interface{} { func (src *Float8Array) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *Float8Array) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *Float8Array) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from Float8Array") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from Float8Array") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/hstore_array.go b/hstore_array.go index 8764aae7..4dc172be 100644 --- a/hstore_array.go +++ b/hstore_array.go @@ -31,56 +31,91 @@ func (dst *HstoreArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = HstoreArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for HstoreArray", src) - } - if elementsLength == 0 { - *dst = HstoreArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to HstoreArray", src) - } - - *dst = HstoreArray{ - Elements: make([]Hstore, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []map[string]string: + if value == nil { + *dst = HstoreArray{Status: Null} + } else if len(value) == 0 { + *dst = HstoreArray{Status: Present} + } else { + elements := make([]Hstore, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Hstore, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = HstoreArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Hstore: + if value == nil { + *dst = HstoreArray{Status: Null} + } else if len(value) == 0 { + *dst = HstoreArray{Status: Present} + } else { + *dst = HstoreArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = HstoreArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for HstoreArray", src) + } + if elementsLength == 0 { + *dst = HstoreArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to HstoreArray", src) + } + + *dst = HstoreArray{ + Elements: make([]Hstore, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Hstore, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to HstoreArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to HstoreArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +130,11 @@ func (dst *HstoreArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +169,21 @@ func (dst HstoreArray) Get() interface{} { func (src *HstoreArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +222,12 @@ func (src *HstoreArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +245,14 @@ func (src *HstoreArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from HstoreArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from HstoreArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/inet_array.go b/inet_array.go index 91f5d6e8..75f1328f 100644 --- a/inet_array.go +++ b/inet_array.go @@ -5,6 +5,7 @@ package pgtype import ( "database/sql/driver" "encoding/binary" + "net" "reflect" "github.com/jackc/pgio" @@ -31,56 +32,129 @@ func (dst *InetArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = InetArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for InetArray", src) - } - if elementsLength == 0 { - *dst = InetArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to InetArray", src) - } - - *dst = InetArray{ - Elements: make([]Inet, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []*net.IPNet: + if value == nil { + *dst = InetArray{Status: Null} + } else if len(value) == 0 { + *dst = InetArray{Status: Present} + } else { + elements := make([]Inet, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Inet, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = InetArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []net.IP: + if value == nil { + *dst = InetArray{Status: Null} + } else if len(value) == 0 { + *dst = InetArray{Status: Present} + } else { + elements := make([]Inet, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = InetArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*net.IP: + if value == nil { + *dst = InetArray{Status: Null} + } else if len(value) == 0 { + *dst = InetArray{Status: Present} + } else { + elements := make([]Inet, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = InetArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Inet: + if value == nil { + *dst = InetArray{Status: Null} + } else if len(value) == 0 { + *dst = InetArray{Status: Present} + } else { + *dst = InetArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = InetArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for InetArray", src) + } + if elementsLength == 0 { + *dst = InetArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to InetArray", src) + } + + *dst = InetArray{ + Elements: make([]Inet, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Inet, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to InetArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to InetArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +169,11 @@ func (dst *InetArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +208,39 @@ func (dst InetArray) Get() interface{} { func (src *InetArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +279,12 @@ func (src *InetArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +302,14 @@ func (src *InetArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from InetArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from InetArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/int2_array.go b/int2_array.go index 06febf01..ede35bac 100644 --- a/int2_array.go +++ b/int2_array.go @@ -31,56 +31,376 @@ func (dst *Int2Array) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = Int2Array{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for Int2Array", src) - } - if elementsLength == 0 { - *dst = Int2Array{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int2Array", src) - } - - *dst = Int2Array{ - Elements: make([]Int2, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []int16: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Int2, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int16: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint16: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint16: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int32: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int32: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint32: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint32: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int64: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int64: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint64: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint64: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + elements := make([]Int2, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int2Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Int2: + if value == nil { + *dst = Int2Array{Status: Null} + } else if len(value) == 0 { + *dst = Int2Array{Status: Present} + } else { + *dst = Int2Array{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = Int2Array{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for Int2Array", src) + } + if elementsLength == 0 { + *dst = Int2Array{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to Int2Array", src) + } + + *dst = Int2Array{ + Elements: make([]Int2, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Int2, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to Int2Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to Int2Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +415,11 @@ func (dst *Int2Array) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +454,156 @@ func (dst Int2Array) Get() interface{} { func (src *Int2Array) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +642,12 @@ func (src *Int2Array) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +665,14 @@ func (src *Int2Array) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from Int2Array") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from Int2Array") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/int4_array.go b/int4_array.go index 189bd238..b0856da9 100644 --- a/int4_array.go +++ b/int4_array.go @@ -31,56 +31,376 @@ func (dst *Int4Array) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = Int4Array{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for Int4Array", src) - } - if elementsLength == 0 { - *dst = Int4Array{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int4Array", src) - } - - *dst = Int4Array{ - Elements: make([]Int4, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []int16: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Int4, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int16: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint16: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint16: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int32: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int32: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint32: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint32: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int64: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int64: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint64: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint64: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + elements := make([]Int4, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int4Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Int4: + if value == nil { + *dst = Int4Array{Status: Null} + } else if len(value) == 0 { + *dst = Int4Array{Status: Present} + } else { + *dst = Int4Array{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = Int4Array{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for Int4Array", src) + } + if elementsLength == 0 { + *dst = Int4Array{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to Int4Array", src) + } + + *dst = Int4Array{ + Elements: make([]Int4, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Int4, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to Int4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to Int4Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +415,11 @@ func (dst *Int4Array) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +454,156 @@ func (dst Int4Array) Get() interface{} { func (src *Int4Array) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +642,12 @@ func (src *Int4Array) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +665,14 @@ func (src *Int4Array) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from Int4Array") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from Int4Array") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/int8_array.go b/int8_array.go index edb232cb..c95ebef5 100644 --- a/int8_array.go +++ b/int8_array.go @@ -31,56 +31,376 @@ func (dst *Int8Array) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = Int8Array{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for Int8Array", src) - } - if elementsLength == 0 { - *dst = Int8Array{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to Int8Array", src) - } - - *dst = Int8Array{ - Elements: make([]Int8, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []int16: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Int8, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int16: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint16: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint16: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int32: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int32: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint32: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint32: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int64: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int64: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint64: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint64: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + elements := make([]Int8, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = Int8Array{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Int8: + if value == nil { + *dst = Int8Array{Status: Null} + } else if len(value) == 0 { + *dst = Int8Array{Status: Present} + } else { + *dst = Int8Array{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = Int8Array{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for Int8Array", src) + } + if elementsLength == 0 { + *dst = Int8Array{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to Int8Array", src) + } + + *dst = Int8Array{ + Elements: make([]Int8, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Int8, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to Int8Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to Int8Array, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +415,11 @@ func (dst *Int8Array) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +454,156 @@ func (dst Int8Array) Get() interface{} { func (src *Int8Array) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +642,12 @@ func (src *Int8Array) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +665,14 @@ func (src *Int8Array) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from Int8Array") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from Int8Array") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/jsonb_array.go b/jsonb_array.go index c5a40a1d..faf2d364 100644 --- a/jsonb_array.go +++ b/jsonb_array.go @@ -31,56 +31,91 @@ func (dst *JSONBArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = JSONBArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for JSONBArray", src) - } - if elementsLength == 0 { - *dst = JSONBArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to JSONBArray", src) - } - - *dst = JSONBArray{ - Elements: make([]Text, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = JSONBArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONBArray{Status: Present} + } else { + elements := make([]Text, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Text, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = JSONBArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Text: + if value == nil { + *dst = JSONBArray{Status: Null} + } else if len(value) == 0 { + *dst = JSONBArray{Status: Present} + } else { + *dst = JSONBArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = JSONBArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for JSONBArray", src) + } + if elementsLength == 0 { + *dst = JSONBArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to JSONBArray", src) + } + + *dst = JSONBArray{ + Elements: make([]Text, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Text, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to JSONBArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to JSONBArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +130,11 @@ func (dst *JSONBArray) setRecursive(value reflect.Value, index, dimension int) ( break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +169,21 @@ func (dst JSONBArray) Get() interface{} { func (src *JSONBArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +222,12 @@ func (src *JSONBArray) assignToRecursive(value reflect.Value, index, dimension i length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +245,14 @@ func (src *JSONBArray) assignToRecursive(value reflect.Value, index, dimension i if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from JSONBArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from JSONBArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/macaddr_array.go b/macaddr_array.go index 398db1fe..6f75ffbc 100644 --- a/macaddr_array.go +++ b/macaddr_array.go @@ -5,6 +5,7 @@ package pgtype import ( "database/sql/driver" "encoding/binary" + "net" "reflect" "github.com/jackc/pgio" @@ -31,56 +32,110 @@ func (dst *MacaddrArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = MacaddrArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for MacaddrArray", src) - } - if elementsLength == 0 { - *dst = MacaddrArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to MacaddrArray", src) - } - - *dst = MacaddrArray{ - Elements: make([]Macaddr, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []net.HardwareAddr: + if value == nil { + *dst = MacaddrArray{Status: Null} + } else if len(value) == 0 { + *dst = MacaddrArray{Status: Present} + } else { + elements := make([]Macaddr, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Macaddr, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = MacaddrArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*net.HardwareAddr: + if value == nil { + *dst = MacaddrArray{Status: Null} + } else if len(value) == 0 { + *dst = MacaddrArray{Status: Present} + } else { + elements := make([]Macaddr, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = MacaddrArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Macaddr: + if value == nil { + *dst = MacaddrArray{Status: Null} + } else if len(value) == 0 { + *dst = MacaddrArray{Status: Present} + } else { + *dst = MacaddrArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = MacaddrArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for MacaddrArray", src) + } + if elementsLength == 0 { + *dst = MacaddrArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to MacaddrArray", src) + } + + *dst = MacaddrArray{ + Elements: make([]Macaddr, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Macaddr, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to MacaddrArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to MacaddrArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +150,11 @@ func (dst *MacaddrArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +189,30 @@ func (dst MacaddrArray) Get() interface{} { func (src *MacaddrArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +251,12 @@ func (src *MacaddrArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +274,14 @@ func (src *MacaddrArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from MacaddrArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from MacaddrArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/numeric_array.go b/numeric_array.go index dec81535..e848b133 100644 --- a/numeric_array.go +++ b/numeric_array.go @@ -31,56 +31,224 @@ func (dst *NumericArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = NumericArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for NumericArray", src) - } - if elementsLength == 0 { - *dst = NumericArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to NumericArray", src) - } - - *dst = NumericArray{ - Elements: make([]Numeric, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []float32: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Numeric, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*float32: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []float64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*float64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []int64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*int64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []uint64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*uint64: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + elements := make([]Numeric, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = NumericArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Numeric: + if value == nil { + *dst = NumericArray{Status: Null} + } else if len(value) == 0 { + *dst = NumericArray{Status: Present} + } else { + *dst = NumericArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = NumericArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for NumericArray", src) + } + if elementsLength == 0 { + *dst = NumericArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to NumericArray", src) + } + + *dst = NumericArray{ + Elements: make([]Numeric, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Numeric, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to NumericArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to NumericArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +263,11 @@ func (dst *NumericArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +302,84 @@ func (dst NumericArray) Get() interface{} { func (src *NumericArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +418,12 @@ func (src *NumericArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +441,14 @@ func (src *NumericArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from NumericArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from NumericArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/text_array.go b/text_array.go index 31ed04ac..c6a950f8 100644 --- a/text_array.go +++ b/text_array.go @@ -31,56 +31,110 @@ func (dst *TextArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = TextArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for TextArray", src) - } - if elementsLength == 0 { - *dst = TextArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TextArray", src) - } - - *dst = TextArray{ - Elements: make([]Text, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = TextArray{Status: Null} + } else if len(value) == 0 { + *dst = TextArray{Status: Present} + } else { + elements := make([]Text, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Text, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = TextArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = TextArray{Status: Null} + } else if len(value) == 0 { + *dst = TextArray{Status: Present} + } else { + elements := make([]Text, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = TextArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Text: + if value == nil { + *dst = TextArray{Status: Null} + } else if len(value) == 0 { + *dst = TextArray{Status: Present} + } else { + *dst = TextArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = TextArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for TextArray", src) + } + if elementsLength == 0 { + *dst = TextArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to TextArray", src) + } + + *dst = TextArray{ + Elements: make([]Text, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Text, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to TextArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to TextArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *TextArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst TextArray) Get() interface{} { func (src *TextArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *TextArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *TextArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from TextArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from TextArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/timestamp_array.go b/timestamp_array.go index 355b29c5..d0254d47 100644 --- a/timestamp_array.go +++ b/timestamp_array.go @@ -6,6 +6,7 @@ import ( "database/sql/driver" "encoding/binary" "reflect" + "time" "github.com/jackc/pgio" errors "golang.org/x/xerrors" @@ -31,56 +32,110 @@ func (dst *TimestampArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = TimestampArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for TimestampArray", src) - } - if elementsLength == 0 { - *dst = TimestampArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TimestampArray", src) - } - - *dst = TimestampArray{ - Elements: make([]Timestamp, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []time.Time: + if value == nil { + *dst = TimestampArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestampArray{Status: Present} + } else { + elements := make([]Timestamp, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Timestamp, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = TimestampArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*time.Time: + if value == nil { + *dst = TimestampArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestampArray{Status: Present} + } else { + elements := make([]Timestamp, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = TimestampArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Timestamp: + if value == nil { + *dst = TimestampArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestampArray{Status: Present} + } else { + *dst = TimestampArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = TimestampArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for TimestampArray", src) + } + if elementsLength == 0 { + *dst = TimestampArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to TimestampArray", src) + } + + *dst = TimestampArray{ + Elements: make([]Timestamp, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Timestamp, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to TimestampArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to TimestampArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +150,11 @@ func (dst *TimestampArray) setRecursive(value reflect.Value, index, dimension in break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +189,30 @@ func (dst TimestampArray) Get() interface{} { func (src *TimestampArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +251,12 @@ func (src *TimestampArray) assignToRecursive(value reflect.Value, index, dimensi length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +274,14 @@ func (src *TimestampArray) assignToRecursive(value reflect.Value, index, dimensi if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from TimestampArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from TimestampArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/timestamptz_array.go b/timestamptz_array.go index 94a791b6..97ce2715 100644 --- a/timestamptz_array.go +++ b/timestamptz_array.go @@ -6,6 +6,7 @@ import ( "database/sql/driver" "encoding/binary" "reflect" + "time" "github.com/jackc/pgio" errors "golang.org/x/xerrors" @@ -31,56 +32,110 @@ func (dst *TimestamptzArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = TimestamptzArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for TimestamptzArray", src) - } - if elementsLength == 0 { - *dst = TimestamptzArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TimestamptzArray", src) - } - - *dst = TimestamptzArray{ - Elements: make([]Timestamptz, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []time.Time: + if value == nil { + *dst = TimestamptzArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestamptzArray{Status: Present} + } else { + elements := make([]Timestamptz, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Timestamptz, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = TimestamptzArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*time.Time: + if value == nil { + *dst = TimestamptzArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestamptzArray{Status: Present} + } else { + elements := make([]Timestamptz, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = TimestamptzArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Timestamptz: + if value == nil { + *dst = TimestamptzArray{Status: Null} + } else if len(value) == 0 { + *dst = TimestamptzArray{Status: Present} + } else { + *dst = TimestamptzArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = TimestamptzArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for TimestamptzArray", src) + } + if elementsLength == 0 { + *dst = TimestamptzArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to TimestamptzArray", src) + } + + *dst = TimestamptzArray{ + Elements: make([]Timestamptz, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Timestamptz, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to TimestamptzArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to TimestamptzArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +150,11 @@ func (dst *TimestamptzArray) setRecursive(value reflect.Value, index, dimension break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +189,30 @@ func (dst TimestamptzArray) Get() interface{} { func (src *TimestamptzArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +251,12 @@ func (src *TimestamptzArray) assignToRecursive(value reflect.Value, index, dimen length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +274,14 @@ func (src *TimestamptzArray) assignToRecursive(value reflect.Value, index, dimen if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from TimestamptzArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from TimestamptzArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/tstzrange_array.go b/tstzrange_array.go index f5043c65..02a98e66 100644 --- a/tstzrange_array.go +++ b/tstzrange_array.go @@ -31,56 +31,72 @@ func (dst *TstzrangeArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = TstzrangeArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for TstzrangeArray", src) - } - if elementsLength == 0 { - *dst = TstzrangeArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to TstzrangeArray", src) - } - - *dst = TstzrangeArray{ - Elements: make([]Tstzrange, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) - } + case []Tstzrange: + if value == nil { + *dst = TstzrangeArray{Status: Null} + } else if len(value) == 0 { + *dst = TstzrangeArray{Status: Present} + } else { + *dst = TstzrangeArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, } - dst.Elements = make([]Tstzrange, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = TstzrangeArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for TstzrangeArray", src) + } + if elementsLength == 0 { + *dst = TstzrangeArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to TstzrangeArray", src) + } + + *dst = TstzrangeArray{ + Elements: make([]Tstzrange, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Tstzrange, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to TstzrangeArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to TstzrangeArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +111,11 @@ func (dst *TstzrangeArray) setRecursive(value reflect.Value, index, dimension in break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +150,21 @@ func (dst TstzrangeArray) Get() interface{} { func (src *TstzrangeArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +203,12 @@ func (src *TstzrangeArray) assignToRecursive(value reflect.Value, index, dimensi length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +226,14 @@ func (src *TstzrangeArray) assignToRecursive(value reflect.Value, index, dimensi if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from TstzrangeArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from TstzrangeArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/typed_array.go.erb b/typed_array.go.erb index fb964ec8..5bf582b2 100644 --- a/typed_array.go.erb +++ b/typed_array.go.erb @@ -30,56 +30,93 @@ func (dst *<%= pgtype_array_type %>) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = <%= pgtype_array_type %>{Status: Null} - return nil - } - - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for <%= pgtype_array_type %>", src) - } - if elementsLength == 0 { - *dst = <%= pgtype_array_type %>{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to <%= pgtype_array_type %>", src) - } - - *dst = <%= pgtype_array_type %> { - Elements: make([]<%= pgtype_element_type %>, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + switch value := src.(type) { + <% go_array_types.split(",").each do |t| %> + <% if t != "[]#{pgtype_element_type}" %> + case <%= t %>: + if value == nil { + *dst = <%= pgtype_array_type %>{Status: Null} + } else if len(value) == 0 { + *dst = <%= pgtype_array_type %>{Status: Present} + } else { + elements := make([]<%= pgtype_element_type %>, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]<%= pgtype_element_type %>, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = <%= pgtype_array_type %>{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + <% end %> + <% end %> + case []<%= pgtype_element_type %>: + if value == nil { + *dst = <%= pgtype_array_type %>{Status: Null} + } else if len(value) == 0 { + *dst = <%= pgtype_array_type %>{Status: Present} + } else { + *dst = <%= pgtype_array_type %>{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status : Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = <%= pgtype_array_type %>{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for <%= pgtype_array_type %>", src) + } + if elementsLength == 0 { + *dst = <%= pgtype_array_type %>{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to <%= pgtype_array_type %>", src) + } + + *dst = <%= pgtype_array_type %> { + Elements: make([]<%= pgtype_element_type %>, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]<%= pgtype_element_type %>, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to <%= pgtype_array_type %>, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to <%= pgtype_array_type %>, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -94,10 +131,11 @@ func (dst *<%= pgtype_array_type %>) setRecursive(value reflect.Value, index, di break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -132,6 +170,21 @@ func (dst <%= pgtype_array_type %>) Get() interface{} { func (src *<%= pgtype_array_type %>) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1{ + 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 %> + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -170,10 +223,12 @@ func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, inde length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -191,11 +246,14 @@ func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, inde if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr(){ return 0, errors.Errorf("cannot assign all values from <%= pgtype_array_type %>") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from <%= pgtype_array_type %>") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/typed_array_gen.sh b/typed_array_gen.sh index 8c594944..607d3bc3 100755 --- a/typed_array_gen.sh +++ b/typed_array_gen.sh @@ -1,27 +1,27 @@ -erb pgtype_array_type=Int2Array pgtype_element_type=Int2 element_type_name=int2 text_null=NULL binary_format=true typed_array.go.erb > int2_array.go -erb pgtype_array_type=Int4Array pgtype_element_type=Int4 element_type_name=int4 text_null=NULL binary_format=true typed_array.go.erb > int4_array.go -erb pgtype_array_type=Int8Array pgtype_element_type=Int8 element_type_name=int8 text_null=NULL binary_format=true typed_array.go.erb > int8_array.go -erb pgtype_array_type=BoolArray pgtype_element_type=Bool element_type_name=bool text_null=NULL binary_format=true typed_array.go.erb > bool_array.go -erb pgtype_array_type=DateArray pgtype_element_type=Date element_type_name=date text_null=NULL binary_format=true typed_array.go.erb > date_array.go -erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz element_type_name=timestamptz text_null=NULL binary_format=true typed_array.go.erb > timestamptz_array.go -erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange element_type_name=tstzrange text_null=NULL binary_format=true typed_array.go.erb > tstzrange_array.go -erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp element_type_name=timestamp text_null=NULL binary_format=true typed_array.go.erb > timestamp_array.go -erb pgtype_array_type=Float4Array pgtype_element_type=Float4 element_type_name=float4 text_null=NULL binary_format=true typed_array.go.erb > float4_array.go -erb pgtype_array_type=Float8Array pgtype_element_type=Float8 element_type_name=float8 text_null=NULL binary_format=true typed_array.go.erb > float8_array.go -erb pgtype_array_type=InetArray pgtype_element_type=Inet element_type_name=inet text_null=NULL binary_format=true typed_array.go.erb > inet_array.go -erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr element_type_name=macaddr text_null=NULL binary_format=true typed_array.go.erb > macaddr_array.go -erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR element_type_name=cidr text_null=NULL binary_format=true typed_array.go.erb > cidr_array.go -erb pgtype_array_type=TextArray pgtype_element_type=Text element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > text_array.go -erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar element_type_name=varchar text_null=NULL binary_format=true typed_array.go.erb > varchar_array.go -erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar element_type_name=bpchar text_null=NULL binary_format=true typed_array.go.erb > bpchar_array.go -erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea element_type_name=bytea text_null=NULL binary_format=true typed_array.go.erb > bytea_array.go -erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem element_type_name=aclitem text_null=NULL binary_format=false typed_array.go.erb > aclitem_array.go -erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore element_type_name=hstore text_null=NULL binary_format=true typed_array.go.erb > hstore_array.go -erb pgtype_array_type=NumericArray pgtype_element_type=Numeric element_type_name=numeric text_null=NULL binary_format=true typed_array.go.erb > numeric_array.go -erb pgtype_array_type=UUIDArray pgtype_element_type=UUID element_type_name=uuid text_null=NULL binary_format=true typed_array.go.erb > uuid_array.go -erb pgtype_array_type=JSONBArray pgtype_element_type=Text element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > jsonb_array.go +erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int2 text_null=NULL binary_format=true typed_array.go.erb > int2_array.go +erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int4 text_null=NULL binary_format=true typed_array.go.erb > int4_array.go +erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int8 text_null=NULL binary_format=true typed_array.go.erb > int8_array.go +erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool,[]*bool element_type_name=bool text_null=NULL binary_format=true typed_array.go.erb > bool_array.go +erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time,[]*time.Time element_type_name=date text_null=NULL binary_format=true typed_array.go.erb > date_array.go +erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time,[]*time.Time element_type_name=timestamptz text_null=NULL binary_format=true typed_array.go.erb > timestamptz_array.go +erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange go_array_types=[]Tstzrange element_type_name=tstzrange text_null=NULL binary_format=true typed_array.go.erb > tstzrange_array.go +erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time,[]*time.Time element_type_name=timestamp text_null=NULL binary_format=true typed_array.go.erb > timestamp_array.go +erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32,[]*float32 element_type_name=float4 text_null=NULL binary_format=true typed_array.go.erb > float4_array.go +erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64,[]*float64 element_type_name=float8 text_null=NULL binary_format=true typed_array.go.erb > float8_array.go +erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=inet text_null=NULL binary_format=true typed_array.go.erb > inet_array.go +erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr,[]*net.HardwareAddr element_type_name=macaddr text_null=NULL binary_format=true typed_array.go.erb > macaddr_array.go +erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=cidr text_null=NULL binary_format=true typed_array.go.erb > cidr_array.go +erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string,[]*string element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > text_array.go +erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string,[]*string element_type_name=varchar text_null=NULL binary_format=true typed_array.go.erb > varchar_array.go +erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string,[]*string element_type_name=bpchar text_null=NULL binary_format=true typed_array.go.erb > bpchar_array.go +erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea text_null=NULL binary_format=true typed_array.go.erb > bytea_array.go +erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string,[]*string element_type_name=aclitem text_null=NULL binary_format=false typed_array.go.erb > aclitem_array.go +erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore text_null=NULL binary_format=true typed_array.go.erb > hstore_array.go +erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]*float32,[]float64,[]*float64,[]int64,[]*int64,[]uint64,[]*uint64 element_type_name=numeric text_null=NULL binary_format=true typed_array.go.erb > numeric_array.go +erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string,[]*string element_type_name=uuid text_null=NULL binary_format=true typed_array.go.erb > uuid_array.go +erb pgtype_array_type=JSONBArray pgtype_element_type=Text go_array_types=[]string element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > jsonb_array.go # While the binary format is theoretically possible it is only practical to use the text format. -erb pgtype_array_type=EnumArray pgtype_element_type=GenericText text_null=NULL binary_format=false typed_array.go.erb > enum_array.go +erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string,[]*string text_null=NULL binary_format=false typed_array.go.erb > enum_array.go goimports -w *_array.go diff --git a/uuid_array.go b/uuid_array.go index e2c86cf8..09c6878f 100644 --- a/uuid_array.go +++ b/uuid_array.go @@ -31,56 +31,148 @@ func (dst *UUIDArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = UUIDArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for UUIDArray", src) - } - if elementsLength == 0 { - *dst = UUIDArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to UUIDArray", src) - } - - *dst = UUIDArray{ - Elements: make([]UUID, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case [][16]byte: + if value == nil { + *dst = UUIDArray{Status: Null} + } else if len(value) == 0 { + *dst = UUIDArray{Status: Present} + } else { + elements := make([]UUID, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]UUID, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = UUIDArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case [][]byte: + if value == nil { + *dst = UUIDArray{Status: Null} + } else if len(value) == 0 { + *dst = UUIDArray{Status: Present} + } else { + elements := make([]UUID, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = UUIDArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []string: + if value == nil { + *dst = UUIDArray{Status: Null} + } else if len(value) == 0 { + *dst = UUIDArray{Status: Present} + } else { + elements := make([]UUID, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = UUIDArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = UUIDArray{Status: Null} + } else if len(value) == 0 { + *dst = UUIDArray{Status: Present} + } else { + elements := make([]UUID, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = UUIDArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []UUID: + if value == nil { + *dst = UUIDArray{Status: Null} + } else if len(value) == 0 { + *dst = UUIDArray{Status: Present} + } else { + *dst = UUIDArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = UUIDArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for UUIDArray", src) + } + if elementsLength == 0 { + *dst = UUIDArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to UUIDArray", src) + } + + *dst = UUIDArray{ + Elements: make([]UUID, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]UUID, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to UUIDArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to UUIDArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +187,11 @@ func (dst *UUIDArray) setRecursive(value reflect.Value, index, dimension int) (i break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +226,48 @@ func (dst UUIDArray) Get() interface{} { func (src *UUIDArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +306,12 @@ func (src *UUIDArray) assignToRecursive(value reflect.Value, index, dimension in length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +329,14 @@ func (src *UUIDArray) assignToRecursive(value reflect.Value, index, dimension in if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from UUIDArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from UUIDArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++ diff --git a/varchar_array.go b/varchar_array.go index ec378ed7..ad19d423 100644 --- a/varchar_array.go +++ b/varchar_array.go @@ -31,56 +31,110 @@ func (dst *VarcharArray) Set(src interface{}) error { } } - value := reflect.ValueOf(src) - if !value.IsValid() || value.IsZero() { - *dst = VarcharArray{Status: Null} - return nil - } + switch value := src.(type) { - dimensions, elementsLength, ok := findDimensionsFromValue(reflect.ValueOf(src), nil, 0) - if !ok { - return errors.Errorf("cannot find dimensions of %v for VarcharArray", src) - } - if elementsLength == 0 { - *dst = VarcharArray{Status: Present} - return nil - } - if len(dimensions) == 0 { - if originalSrc, ok := underlyingSliceType(src); ok { - return dst.Set(originalSrc) - } - return errors.Errorf("cannot convert %v to VarcharArray", src) - } - - *dst = VarcharArray{ - Elements: make([]Varchar, elementsLength), - Dimensions: dimensions, - Status: Present, - } - elementCount, err := dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { - // Maybe the target was one dimension too far, try again: - if len(dst.Dimensions) > 1 { - dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] - elementsLength = 0 - for _, dim := range dst.Dimensions { - if elementsLength == 0 { - elementsLength = int(dim.Length) - } else { - elementsLength *= int(dim.Length) + case []string: + if value == nil { + *dst = VarcharArray{Status: Null} + } else if len(value) == 0 { + *dst = VarcharArray{Status: Present} + } else { + elements := make([]Varchar, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err } } - dst.Elements = make([]Varchar, elementsLength) - elementCount, err = dst.setRecursive(reflect.ValueOf(src), 0, 0) - if err != nil { + *dst = VarcharArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []*string: + if value == nil { + *dst = VarcharArray{Status: Null} + } else if len(value) == 0 { + *dst = VarcharArray{Status: Present} + } else { + elements := make([]Varchar, len(value)) + for i := range value { + if err := elements[i].Set(value[i]); err != nil { + return err + } + } + *dst = VarcharArray{ + Elements: elements, + Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, + Status: Present, + } + } + + case []Varchar: + if value == nil { + *dst = VarcharArray{Status: Null} + } else if len(value) == 0 { + *dst = VarcharArray{Status: Present} + } else { + *dst = VarcharArray{ + Elements: value, + Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}}, + Status: Present, + } + } + default: + reflectedValue := reflect.ValueOf(src) + if !reflectedValue.IsValid() || reflectedValue.IsZero() { + *dst = VarcharArray{Status: Null} + return nil + } + + dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) + if !ok { + return errors.Errorf("cannot find dimensions of %v for VarcharArray", src) + } + if elementsLength == 0 { + *dst = VarcharArray{Status: Present} + return nil + } + if len(dimensions) == 0 { + if originalSrc, ok := underlyingSliceType(src); ok { + return dst.Set(originalSrc) + } + return errors.Errorf("cannot convert %v to VarcharArray", src) + } + + *dst = VarcharArray{ + Elements: make([]Varchar, elementsLength), + Dimensions: dimensions, + Status: Present, + } + elementCount, err := dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + // Maybe the target was one dimension too far, try again: + if len(dst.Dimensions) > 1 { + dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1] + elementsLength = 0 + for _, dim := range dst.Dimensions { + if elementsLength == 0 { + elementsLength = int(dim.Length) + } else { + elementsLength *= int(dim.Length) + } + } + dst.Elements = make([]Varchar, elementsLength) + elementCount, err = dst.setRecursive(reflectedValue, 0, 0) + if err != nil { + return err + } + } else { return err } - } else { - return err } - } - if elementCount != len(dst.Elements) { - return errors.Errorf("cannot convert %v to VarcharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + if elementCount != len(dst.Elements) { + return errors.Errorf("cannot convert %v to VarcharArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) + } } return nil @@ -95,10 +149,11 @@ func (dst *VarcharArray) setRecursive(value reflect.Value, index, dimension int) break } - if int32(value.Len()) != dst.Dimensions[dimension].Length { + valueLen := value.Len() + if int32(valueLen) != dst.Dimensions[dimension].Length { return 0, errors.Errorf("multidimensional arrays must have array expressions with matching dimensions") } - for i := 0; i < value.Len(); i++ { + for i := 0; i < valueLen; i++ { var err error index, err = dst.setRecursive(value.Index(i), index, dimension+1) if err != nil { @@ -133,6 +188,30 @@ func (dst VarcharArray) Get() interface{} { func (src *VarcharArray) AssignTo(dst interface{}) error { switch src.Status { case Present: + if len(src.Dimensions) == 1 { + 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 + + } + } + value := reflect.ValueOf(dst) if value.Kind() == reflect.Ptr { value = value.Elem() @@ -171,10 +250,12 @@ func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension length := int(src.Dimensions[dimension].Length) if reflect.Array == kind { - if value.Type().Len() != length { - return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, value.Type(), value.Type().Len()) + typ := value.Type() + typLen := typ.Len() + if typLen != length { + return 0, errors.Errorf("expected size %d array, but %s has size %d array", length, typ, typLen) } - value.Set(reflect.New(value.Type()).Elem()) + value.Set(reflect.New(typ).Elem()) } else { value.Set(reflect.MakeSlice(value.Type(), length, length)) } @@ -192,11 +273,14 @@ func (src *VarcharArray) assignToRecursive(value reflect.Value, index, dimension if len(src.Dimensions) != dimension { return 0, errors.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension) } - if !value.CanAddr() || !value.Addr().CanInterface() { + if !value.CanAddr() { return 0, errors.Errorf("cannot assign all values from VarcharArray") } - err := src.Elements[index].AssignTo(value.Addr().Interface()) - if err != nil { + addr := value.Addr() + if !addr.CanInterface() { + return 0, errors.Errorf("cannot assign all values from VarcharArray") + } + if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { return 0, err } index++