mirror of https://github.com/jackc/pgx.git
Convert numeric to Codec
parent
0056156904
commit
b9b5e35d0f
|
@ -901,7 +901,7 @@ func TestDomainType(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("did not find uint64 OID, %v", err)
|
||||
}
|
||||
conn.ConnInfo().RegisterDataType(pgtype.DataType{Value: &pgtype.Numeric{}, Name: "uint64", OID: uint64OID})
|
||||
conn.ConnInfo().RegisterDataType(pgtype.DataType{Name: "uint64", OID: uint64OID, Codec: pgtype.NumericCodec{}})
|
||||
|
||||
// String is still an acceptable argument after registration
|
||||
err = conn.QueryRow(context.Background(), "select $1::uint64", "7").Scan(&n)
|
||||
|
|
|
@ -273,6 +273,20 @@ func (w float32Wrapper) Int64Value() (Int8, error) {
|
|||
return Int8{Int: int64(w), Valid: true}, nil
|
||||
}
|
||||
|
||||
func (w *float32Wrapper) ScanFloat64(v Float8) error {
|
||||
if !v.Valid {
|
||||
return fmt.Errorf("cannot scan NULL into *float32")
|
||||
}
|
||||
|
||||
*w = float32Wrapper(v.Float)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w float32Wrapper) Float64Value() (Float8, error) {
|
||||
return Float8{Float: float64(w), Valid: true}, nil
|
||||
}
|
||||
|
||||
type float64Wrapper float64
|
||||
|
||||
func (w float64Wrapper) SkipUnderlyingTypePlan() {}
|
||||
|
@ -295,6 +309,20 @@ func (w float64Wrapper) Int64Value() (Int8, error) {
|
|||
return Int8{Int: int64(w), Valid: true}, nil
|
||||
}
|
||||
|
||||
func (w *float64Wrapper) ScanFloat64(v Float8) error {
|
||||
if !v.Valid {
|
||||
return fmt.Errorf("cannot scan NULL into *float64")
|
||||
}
|
||||
|
||||
*w = float64Wrapper(v.Float)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w float64Wrapper) Float64Value() (Float8, error) {
|
||||
return Float8{Float: float64(w), Valid: true}, nil
|
||||
}
|
||||
|
||||
type stringWrapper string
|
||||
|
||||
func (w stringWrapper) SkipUnderlyingTypePlan() {}
|
||||
|
|
1103
pgtype/numeric.go
1103
pgtype/numeric.go
File diff suppressed because it is too large
Load Diff
|
@ -1,672 +0,0 @@
|
|||
// Code generated by erb. DO NOT EDIT.
|
||||
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
type NumericArray struct {
|
||||
Elements []Numeric
|
||||
Dimensions []ArrayDimension
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func (dst *NumericArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = NumericArray{}
|
||||
return nil
|
||||
}
|
||||
|
||||
if value, ok := src.(interface{ Get() interface{} }); ok {
|
||||
value2 := value.Get()
|
||||
if value2 != value {
|
||||
return dst.Set(value2)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to match to select common types:
|
||||
switch value := src.(type) {
|
||||
|
||||
case []float32:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []*float32:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []float64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []*float64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []int64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []*int64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []uint64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []*uint64:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} 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}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
case []Numeric:
|
||||
if value == nil {
|
||||
*dst = NumericArray{}
|
||||
} else if len(value) == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
} else {
|
||||
*dst = NumericArray{
|
||||
Elements: value,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
reflectedValue := reflect.ValueOf(src)
|
||||
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
|
||||
*dst = NumericArray{}
|
||||
return nil
|
||||
}
|
||||
|
||||
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot find dimensions of %v for NumericArray", src)
|
||||
}
|
||||
if elementsLength == 0 {
|
||||
*dst = NumericArray{Valid: true}
|
||||
return nil
|
||||
}
|
||||
if len(dimensions) == 0 {
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return fmt.Errorf("cannot convert %v to NumericArray", src)
|
||||
}
|
||||
|
||||
*dst = NumericArray{
|
||||
Elements: make([]Numeric, elementsLength),
|
||||
Dimensions: dimensions,
|
||||
Valid: true,
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
if elementCount != len(dst.Elements) {
|
||||
return fmt.Errorf("cannot convert %v to NumericArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *NumericArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch value.Kind() {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(dst.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
valueLen := value.Len()
|
||||
if int32(valueLen) != dst.Dimensions[dimension].Length {
|
||||
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
|
||||
}
|
||||
for i := 0; i < valueLen; i++ {
|
||||
var err error
|
||||
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if !value.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot convert all values to NumericArray")
|
||||
}
|
||||
if err := dst.Elements[index].Set(value.Interface()); err != nil {
|
||||
return 0, fmt.Errorf("%v in NumericArray", err)
|
||||
}
|
||||
index++
|
||||
|
||||
return index, nil
|
||||
}
|
||||
|
||||
func (dst NumericArray) Get() interface{} {
|
||||
if !dst.Valid {
|
||||
return nil
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func (src *NumericArray) AssignTo(dst interface{}) error {
|
||||
if !src.Valid {
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
if len(src.Dimensions) <= 1 {
|
||||
// Attempt to match to select common types:
|
||||
switch v := dst.(type) {
|
||||
|
||||
case *[]float32:
|
||||
*v = make([]float32, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*float32:
|
||||
*v = make([]*float32, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]float64:
|
||||
*v = make([]float64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*float64:
|
||||
*v = make([]*float64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]int64:
|
||||
*v = make([]int64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*int64:
|
||||
*v = make([]*int64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]uint64:
|
||||
*v = make([]uint64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case *[]*uint64:
|
||||
*v = make([]*uint64, len(src.Elements))
|
||||
for i := range src.Elements {
|
||||
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Try to convert to something AssignTo can use directly.
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
|
||||
// Fallback to reflection if an optimised match was not found.
|
||||
// The reflection is necessary for arrays and multidimensional slices,
|
||||
// but it comes with a 20-50% performance penalty for large arrays/slices
|
||||
value := reflect.ValueOf(dst)
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
switch value.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
default:
|
||||
return fmt.Errorf("cannot assign %T to %T", src, dst)
|
||||
}
|
||||
|
||||
if len(src.Elements) == 0 {
|
||||
if value.Kind() == reflect.Slice {
|
||||
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
elementCount, err := src.assignToRecursive(value, 0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if elementCount != len(src.Elements) {
|
||||
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *NumericArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
if len(src.Dimensions) == dimension {
|
||||
break
|
||||
}
|
||||
|
||||
length := int(src.Dimensions[dimension].Length)
|
||||
if reflect.Array == kind {
|
||||
typ := value.Type()
|
||||
if typ.Len() != length {
|
||||
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
|
||||
}
|
||||
value.Set(reflect.New(typ).Elem())
|
||||
} else {
|
||||
value.Set(reflect.MakeSlice(value.Type(), length, length))
|
||||
}
|
||||
|
||||
var err error
|
||||
for i := 0; i < length; i++ {
|
||||
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
if len(src.Dimensions) != dimension {
|
||||
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
|
||||
}
|
||||
if !value.CanAddr() {
|
||||
return 0, fmt.Errorf("cannot assign all values from NumericArray")
|
||||
}
|
||||
addr := value.Addr()
|
||||
if !addr.CanInterface() {
|
||||
return 0, fmt.Errorf("cannot assign all values from NumericArray")
|
||||
}
|
||||
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
index++
|
||||
return index, nil
|
||||
}
|
||||
|
||||
func (dst *NumericArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = NumericArray{}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Numeric
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Numeric, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Numeric
|
||||
var elemSrc []byte
|
||||
if s != "NULL" || uta.Quoted[i] {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = NumericArray{Elements: elements, Dimensions: uta.Dimensions, Valid: true}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *NumericArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = NumericArray{}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = NumericArray{Dimensions: arrayHeader.Dimensions, Valid: true}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Numeric, elementCount)
|
||||
|
||||
for i := range elements {
|
||||
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
var elemSrc []byte
|
||||
if elemLen >= 0 {
|
||||
elemSrc = src[rp : rp+elemLen]
|
||||
rp += elemLen
|
||||
}
|
||||
err = elements[i].DecodeBinary(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = NumericArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Valid: true}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src NumericArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
if !src.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if len(src.Dimensions) == 0 {
|
||||
return append(buf, '{', '}'), nil
|
||||
}
|
||||
|
||||
buf = EncodeTextArrayDimensions(buf, src.Dimensions)
|
||||
|
||||
// dimElemCounts is the multiples of elements that each array lies on. For
|
||||
// example, a single dimension array of length 4 would have a dimElemCounts of
|
||||
// [4]. A multi-dimensional array of lengths [3,5,2] would have a
|
||||
// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{'
|
||||
// or '}'.
|
||||
dimElemCounts := make([]int, len(src.Dimensions))
|
||||
dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length)
|
||||
for i := len(src.Dimensions) - 2; i > -1; i-- {
|
||||
dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1]
|
||||
}
|
||||
|
||||
inElemBuf := make([]byte, 0, 32)
|
||||
for i, elem := range src.Elements {
|
||||
if i > 0 {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
for _, dec := range dimElemCounts {
|
||||
if i%dec == 0 {
|
||||
buf = append(buf, '{')
|
||||
}
|
||||
}
|
||||
|
||||
elemBuf, err := elem.EncodeText(ci, inElemBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf == nil {
|
||||
buf = append(buf, `NULL`...)
|
||||
} else {
|
||||
buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...)
|
||||
}
|
||||
|
||||
for _, dec := range dimElemCounts {
|
||||
if (i+1)%dec == 0 {
|
||||
buf = append(buf, '}')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src NumericArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
if !src.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("numeric"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, fmt.Errorf("unable to find oid for type name %v", "numeric")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if !src.Elements[i].Valid {
|
||||
arrayHeader.ContainsNull = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
buf = arrayHeader.EncodeBinary(ci, buf)
|
||||
|
||||
for i := range src.Elements {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := src.Elements[i].EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *NumericArray) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
return dst.DecodeText(nil, nil)
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src NumericArray) Value() (driver.Value, error) {
|
||||
buf, err := src.EncodeText(nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return string(buf), nil
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestNumericArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "numeric[]", []interface{}{
|
||||
&pgtype.NumericArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Valid: true,
|
||||
},
|
||||
&pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Valid: true,
|
||||
},
|
||||
&pgtype.NumericArray{},
|
||||
&pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
{},
|
||||
{Int: big.NewInt(6), Valid: true},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Valid: true,
|
||||
},
|
||||
&pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestNumericArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.NumericArray
|
||||
}{
|
||||
{
|
||||
source: []float32{1},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: []float32{float32(math.Copysign(0, -1))},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(0), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: []float64{1},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: []float64{math.Copysign(0, -1)},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(0), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: (([]float32)(nil)),
|
||||
result: pgtype.NumericArray{},
|
||||
},
|
||||
{
|
||||
source: [][]float32{{1}, {2}},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
{Int: big.NewInt(5), Valid: true},
|
||||
{Int: big.NewInt(6), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{LowerBound: 1, Length: 2},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 3}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: [2][1]float32{{1}, {2}},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
},
|
||||
{
|
||||
source: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}},
|
||||
result: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
{Int: big.NewInt(5), Valid: true},
|
||||
{Int: big.NewInt(6), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{LowerBound: 1, Length: 2},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 3}},
|
||||
Valid: true},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.NumericArray
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(r, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericArrayAssignTo(t *testing.T) {
|
||||
var float32Slice []float32
|
||||
var float64Slice []float64
|
||||
var float32SliceDim2 [][]float32
|
||||
var float32SliceDim4 [][][][]float32
|
||||
var float32ArrayDim2 [2][1]float32
|
||||
var float32ArrayDim4 [2][1][1][3]float32
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.NumericArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true,
|
||||
},
|
||||
dst: &float32Slice,
|
||||
expected: []float32{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true,
|
||||
},
|
||||
dst: &float64Slice,
|
||||
expected: []float64{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{},
|
||||
dst: &float32Slice,
|
||||
expected: (([]float32)(nil)),
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{Valid: true},
|
||||
dst: &float32Slice,
|
||||
expected: []float32{},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
dst: &float32SliceDim2,
|
||||
expected: [][]float32{{1}, {2}},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
{Int: big.NewInt(5), Valid: true},
|
||||
{Int: big.NewInt(6), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{LowerBound: 1, Length: 2},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 3}},
|
||||
Valid: true},
|
||||
dst: &float32SliceDim4,
|
||||
expected: [][][][]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
dst: &float32ArrayDim2,
|
||||
expected: [2][1]float32{{1}, {2}},
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{
|
||||
{Int: big.NewInt(1), Valid: true},
|
||||
{Int: big.NewInt(2), Valid: true},
|
||||
{Int: big.NewInt(3), Valid: true},
|
||||
{Int: big.NewInt(4), Valid: true},
|
||||
{Int: big.NewInt(5), Valid: true},
|
||||
{Int: big.NewInt(6), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{LowerBound: 1, Length: 2},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 1},
|
||||
{LowerBound: 1, Length: 3}},
|
||||
Valid: true},
|
||||
dst: &float32ArrayDim4,
|
||||
expected: [2][1][1][3]float32{{{{1, 2, 3}}}, {{{4, 5, 6}}}},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range simpleTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if dst := reflect.ValueOf(tt.dst).Elem().Interface(); !reflect.DeepEqual(dst, tt.expected) {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
errorTests := []struct {
|
||||
src pgtype.NumericArray
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Valid: true,
|
||||
},
|
||||
dst: &float32Slice,
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}},
|
||||
Valid: true},
|
||||
dst: &float32ArrayDim2,
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}, {LowerBound: 1, Length: 2}},
|
||||
Valid: true},
|
||||
dst: &float32Slice,
|
||||
},
|
||||
{
|
||||
src: pgtype.NumericArray{
|
||||
Elements: []pgtype.Numeric{{Int: big.NewInt(1), Valid: true}, {Int: big.NewInt(2), Valid: true}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 2}, {LowerBound: 1, Length: 1}},
|
||||
Valid: true},
|
||||
dst: &float32ArrayDim4,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range errorTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,7 +6,7 @@ import (
|
|||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
|
@ -14,34 +14,6 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// For test purposes only. Note that it does not normalize values. e.g. (Int: 1, Exp: 3) will not equal (Int: 1000, Exp: 0)
|
||||
func numericEqual(left, right *pgtype.Numeric) bool {
|
||||
return left.Valid == right.Valid &&
|
||||
left.Exp == right.Exp &&
|
||||
((left.Int == nil && right.Int == nil) || (left.Int != nil && right.Int != nil && left.Int.Cmp(right.Int) == 0)) &&
|
||||
left.NaN == right.NaN
|
||||
}
|
||||
|
||||
// For test purposes only.
|
||||
func numericNormalizedEqual(left, right *pgtype.Numeric) bool {
|
||||
if left.Valid != right.Valid {
|
||||
return false
|
||||
}
|
||||
|
||||
normLeft := &pgtype.Numeric{Int: (&big.Int{}).Set(left.Int), Valid: left.Valid}
|
||||
normRight := &pgtype.Numeric{Int: (&big.Int{}).Set(right.Int), Valid: right.Valid}
|
||||
|
||||
if left.Exp < right.Exp {
|
||||
mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(right.Exp-left.Exp)), nil)
|
||||
normRight.Int.Mul(normRight.Int, mul)
|
||||
} else if left.Exp > right.Exp {
|
||||
mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(left.Exp-right.Exp)), nil)
|
||||
normLeft.Int.Mul(normLeft.Int, mul)
|
||||
}
|
||||
|
||||
return normLeft.Int.Cmp(normRight.Int) == 0
|
||||
}
|
||||
|
||||
func mustParseBigInt(t *testing.T, src string) *big.Int {
|
||||
i := &big.Int{}
|
||||
if _, ok := i.SetString(src, 10); !ok {
|
||||
|
@ -50,368 +22,122 @@ func mustParseBigInt(t *testing.T, src string) *big.Int {
|
|||
return i
|
||||
}
|
||||
|
||||
func TestNumericNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select '0'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '1'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '10.00'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(1000), Exp: -2, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '1e-3'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: -3, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '-1'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '10000'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(1), Exp: 4, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '3.14'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '1.1'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(11), Exp: -1, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '100010001'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(100010001), Exp: 0, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '100010001.0001'::numeric",
|
||||
Value: &pgtype.Numeric{Int: big.NewInt(1000100010001), Exp: -4, Valid: true},
|
||||
},
|
||||
{
|
||||
SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric",
|
||||
Value: &pgtype.Numeric{
|
||||
Int: mustParseBigInt(t, "423723478923478928934789237432487213832189417894318904389012483210893443219085471578891547854892438945012347981"),
|
||||
Exp: -41,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric",
|
||||
Value: &pgtype.Numeric{
|
||||
Int: mustParseBigInt(t, "8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"),
|
||||
Exp: -196,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric",
|
||||
Value: &pgtype.Numeric{
|
||||
Int: mustParseBigInt(t, "123"),
|
||||
Exp: -186,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
func isExpectedEqNumeric(a interface{}) func(interface{}) bool {
|
||||
return func(v interface{}) bool {
|
||||
aa := a.(pgtype.Numeric)
|
||||
vv := v.(pgtype.Numeric)
|
||||
|
||||
if aa.Valid != vv.Valid {
|
||||
return false
|
||||
}
|
||||
|
||||
// If NULL doesn't matter what the rest of the values are.
|
||||
if !aa.Valid {
|
||||
return true
|
||||
}
|
||||
|
||||
if !(aa.NaN == vv.NaN && aa.InfinityModifier == vv.InfinityModifier) {
|
||||
return false
|
||||
}
|
||||
|
||||
// If NaN or InfinityModifier are set then Int and Exp don't matter.
|
||||
if aa.NaN || aa.InfinityModifier != pgtype.None {
|
||||
return true
|
||||
}
|
||||
|
||||
aaInt := (&big.Int{}).Set(aa.Int)
|
||||
vvInt := (&big.Int{}).Set(vv.Int)
|
||||
|
||||
if aa.Exp < vv.Exp {
|
||||
mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(vv.Exp-aa.Exp)), nil)
|
||||
vvInt.Mul(vvInt, mul)
|
||||
} else if aa.Exp > vv.Exp {
|
||||
mul := (&big.Int{}).Exp(big.NewInt(10), big.NewInt(int64(aa.Exp-vv.Exp)), nil)
|
||||
aaInt.Mul(aaInt, mul)
|
||||
}
|
||||
|
||||
return aaInt.Cmp(vvInt) == 0
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericTranscode(t *testing.T) {
|
||||
func mustParseNumeric(t *testing.T, src string) pgtype.Numeric {
|
||||
var n pgtype.Numeric
|
||||
plan := pgtype.NumericCodec{}.PlanScan(nil, pgtype.NumericOID, pgtype.TextFormatCode, &n, false)
|
||||
require.NotNil(t, plan)
|
||||
err := plan.Scan(nil, pgtype.NumericOID, pgtype.TextFormatCode, []byte(src), &n)
|
||||
require.NoError(t, err)
|
||||
return n
|
||||
}
|
||||
|
||||
func TestNumericCodec(t *testing.T) {
|
||||
max := new(big.Int).Exp(big.NewInt(10), big.NewInt(147454), nil)
|
||||
max.Add(max, big.NewInt(1))
|
||||
longestNumeric := &pgtype.Numeric{Int: max, Exp: -16383, Valid: true}
|
||||
longestNumeric := pgtype.Numeric{Int: max, Exp: -16383, Valid: true}
|
||||
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{
|
||||
&pgtype.Numeric{NaN: true, Valid: true},
|
||||
&pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true},
|
||||
&pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true},
|
||||
|
||||
&pgtype.Numeric{Int: big.NewInt(0), Exp: 0, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(1), Exp: 0, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(-1), Exp: 0, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(1), Exp: 6, Valid: true},
|
||||
|
||||
// preserves significant zeroes
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -1, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -2, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -3, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -4, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -5, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(10000000), Exp: -6, Valid: true},
|
||||
|
||||
&pgtype.Numeric{Int: big.NewInt(314), Exp: -2, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(123), Exp: -7, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(123), Exp: -8, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(123), Exp: -9, Valid: true},
|
||||
&pgtype.Numeric{Int: big.NewInt(123), Exp: -1500, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "3723409723490243842378942378901237502734019231380123"), Exp: 81, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "723409723490243842378942378901237502734019231380123"), Exp: 82, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "23409723490243842378942378901237502734019231380123"), Exp: 83, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "3409723490243842378942378901237502734019231380123"), Exp: 84, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "3423409823409243892349028349023482934092340892390101"), Exp: -91, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "423409823409243892349028349023482934092340892390101"), Exp: -92, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "23409823409243892349028349023482934092340892390101"), Exp: -93, Valid: true},
|
||||
&pgtype.Numeric{Int: mustParseBigInt(t, "3409823409243892349028349023482934092340892390101"), Exp: -94, Valid: true},
|
||||
|
||||
longestNumeric,
|
||||
|
||||
&pgtype.Numeric{},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(pgtype.Numeric)
|
||||
b := bb.(pgtype.Numeric)
|
||||
|
||||
return numericEqual(&a, &b)
|
||||
testPgxCodec(t, "numeric", []PgxTranscodeTestCase{
|
||||
{mustParseNumeric(t, "1"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "1"))},
|
||||
{mustParseNumeric(t, "3.14159"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "3.14159"))},
|
||||
{mustParseNumeric(t, "100010001"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "100010001"))},
|
||||
{mustParseNumeric(t, "100010001.0001"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "100010001.0001"))},
|
||||
{mustParseNumeric(t, "4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981"))},
|
||||
{mustParseNumeric(t, "0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"))},
|
||||
{mustParseNumeric(t, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123"))},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "243723409723490243842378942378901237502734019231380123"), Exp: 23790, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "2437"), Exp: 23790, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 80, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 81, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 81, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 82, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 82, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 83, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 83, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 84, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "43723409723490243842378942378901237502734019231380123"), Exp: 84, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "913423409823409243892349028349023482934092340892390101"), Exp: -14021, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -90, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -91, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -91, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -92, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -92, Valid: true})},
|
||||
{pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -93, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{Int: mustParseBigInt(t, "13423409823409243892349028349023482934092340892390101"), Exp: -93, Valid: true})},
|
||||
{pgtype.Numeric{NaN: true, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{NaN: true, Valid: true})},
|
||||
{pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true})},
|
||||
{pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, new(pgtype.Numeric), isExpectedEqNumeric(pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true})},
|
||||
{longestNumeric, new(pgtype.Numeric), isExpectedEqNumeric(longestNumeric)},
|
||||
{mustParseNumeric(t, "1"), new(int64), isExpectedEq(int64(1))},
|
||||
{math.NaN(), new(float64), func(a interface{}) bool { return math.IsNaN(a.(float64)) }},
|
||||
{float32(math.NaN()), new(float32), func(a interface{}) bool { return math.IsNaN(float64(a.(float32))) }},
|
||||
{math.Inf(1), new(float64), isExpectedEq(math.Inf(1))},
|
||||
{float32(math.Inf(1)), new(float32), isExpectedEq(float32(math.Inf(1)))},
|
||||
{math.Inf(-1), new(float64), isExpectedEq(math.Inf(-1))},
|
||||
{float32(math.Inf(-1)), new(float32), isExpectedEq(float32(math.Inf(-1)))},
|
||||
{int64(-1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "-1"))},
|
||||
{int64(0), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "0"))},
|
||||
{int64(1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "1"))},
|
||||
{int64(math.MinInt64), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64, 10)))},
|
||||
{int64(math.MinInt64 + 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MinInt64+1, 10)))},
|
||||
{int64(math.MaxInt64), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64, 10)))},
|
||||
{int64(math.MaxInt64 - 1), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, strconv.FormatInt(math.MaxInt64-1, 10)))},
|
||||
{pgtype.Numeric{}, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
||||
{nil, new(pgtype.Numeric), isExpectedEq(pgtype.Numeric{})},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestNumericTranscodeFuzz(t *testing.T) {
|
||||
func TestNumericCodecFuzz(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(0))
|
||||
max := &big.Int{}
|
||||
max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
|
||||
|
||||
values := make([]interface{}, 0, 2000)
|
||||
tests := make([]PgxTranscodeTestCase, 0, 2000)
|
||||
for i := 0; i < 10; i++ {
|
||||
for j := -50; j < 50; j++ {
|
||||
num := (&big.Int{}).Rand(r, max)
|
||||
|
||||
n := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}
|
||||
tests = append(tests, PgxTranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
|
||||
|
||||
negNum := &big.Int{}
|
||||
negNum.Neg(num)
|
||||
values = append(values, &pgtype.Numeric{Int: num, Exp: int32(j), Valid: true})
|
||||
values = append(values, &pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true})
|
||||
n = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}
|
||||
tests = append(tests, PgxTranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
|
||||
}
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", values,
|
||||
func(aa, bb interface{}) bool {
|
||||
a := aa.(pgtype.Numeric)
|
||||
b := bb.(pgtype.Numeric)
|
||||
|
||||
return numericNormalizedEqual(&a, &b)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNumericSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result *pgtype.Numeric
|
||||
}{
|
||||
{source: float32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: float32(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Valid: true}},
|
||||
{source: float64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: float64(math.Copysign(0, -1)), result: &pgtype.Numeric{Int: big.NewInt(0), Valid: true}},
|
||||
{source: int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: int16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: int32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: int64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: int8(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}},
|
||||
{source: int16(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}},
|
||||
{source: int32(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}},
|
||||
{source: int64(-1), result: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}},
|
||||
{source: uint8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: uint16(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: uint32(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: uint64(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: "1", result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: _int8(1), result: &pgtype.Numeric{Int: big.NewInt(1), Valid: true}},
|
||||
{source: float64(1000), result: &pgtype.Numeric{Int: big.NewInt(1), Exp: 3, Valid: true}},
|
||||
{source: float64(1234), result: &pgtype.Numeric{Int: big.NewInt(1234), Exp: 0, Valid: true}},
|
||||
{source: float64(12345678900), result: &pgtype.Numeric{Int: big.NewInt(123456789), Exp: 2, Valid: true}},
|
||||
{source: float64(12345.678901), result: &pgtype.Numeric{Int: big.NewInt(12345678901), Exp: -6, Valid: true}},
|
||||
{source: math.NaN(), result: &pgtype.Numeric{Int: nil, Exp: 0, Valid: true, NaN: true}},
|
||||
{source: float32(math.NaN()), result: &pgtype.Numeric{Int: nil, Exp: 0, Valid: true, NaN: true}},
|
||||
{source: pgtype.Infinity, result: &pgtype.Numeric{InfinityModifier: pgtype.Infinity, Valid: true}},
|
||||
{source: math.Inf(1), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}},
|
||||
{source: float32(math.Inf(1)), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}},
|
||||
{source: pgtype.NegativeInfinity, result: &pgtype.Numeric{InfinityModifier: pgtype.NegativeInfinity, Valid: true}},
|
||||
{source: math.Inf(-1), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}},
|
||||
{source: float32(math.Inf(1)), result: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
r := &pgtype.Numeric{}
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !numericEqual(r, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericAssignTo(t *testing.T) {
|
||||
var i8 int8
|
||||
var i16 int16
|
||||
var i32 int32
|
||||
var i64 int64
|
||||
var i int
|
||||
var ui8 uint8
|
||||
var ui16 uint16
|
||||
var ui32 uint32
|
||||
var ui64 uint64
|
||||
var ui uint
|
||||
var pi8 *int8
|
||||
var _i8 _int8
|
||||
var _pi8 *_int8
|
||||
var f32 float32
|
||||
var f64 float64
|
||||
var pf32 *float32
|
||||
var pf64 *float64
|
||||
|
||||
simpleTests := []struct {
|
||||
src *pgtype.Numeric
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &f32, expected: float32(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &f64, expected: float64(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Valid: true}, dst: &f32, expected: float32(4.2)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Exp: -1, Valid: true}, dst: &f64, expected: float64(4.2)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i16, expected: int16(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i32, expected: int32(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i64, expected: int64(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Exp: 3, Valid: true}, dst: &i64, expected: int64(42000)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &i, expected: int(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui8, expected: uint8(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui16, expected: uint16(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui32, expected: uint32(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui64, expected: uint64(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &ui, expected: uint(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &_i8, expected: _int8(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(1006), Exp: -2, Valid: true}, dst: &f64, expected: float64(10.06)}, // https://github.com/jackc/pgx/v5/pgtype/issues/27
|
||||
{src: &pgtype.Numeric{Valid: true, NaN: true}, dst: &f64, expected: math.NaN()},
|
||||
{src: &pgtype.Numeric{Valid: true, NaN: true}, dst: &f32, expected: float32(math.NaN())},
|
||||
{src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}, dst: &f64, expected: math.Inf(1)},
|
||||
{src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.Infinity}, dst: &f32, expected: float32(math.Inf(1))},
|
||||
{src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}, dst: &f64, expected: math.Inf(-1)},
|
||||
{src: &pgtype.Numeric{Valid: true, InfinityModifier: pgtype.NegativeInfinity}, dst: &f32, expected: float32(math.Inf(-1))},
|
||||
}
|
||||
|
||||
for i, tt := range simpleTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
dst := reflect.ValueOf(tt.dst).Elem().Interface()
|
||||
switch dstTyped := dst.(type) {
|
||||
case float32:
|
||||
nanExpected := math.IsNaN(float64(tt.expected.(float32)))
|
||||
if nanExpected && !math.IsNaN(float64(dstTyped)) {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
} else if !nanExpected && dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
case float64:
|
||||
nanExpected := math.IsNaN(tt.expected.(float64))
|
||||
if nanExpected && !math.IsNaN(dstTyped) {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
} else if !nanExpected && dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
default:
|
||||
if dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pointerAllocTests := []struct {
|
||||
src *pgtype.Numeric
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &pf32, expected: float32(42)},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(42), Valid: true}, dst: &pf64, expected: float64(42)},
|
||||
}
|
||||
|
||||
for i, tt := range pointerAllocTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if dst := reflect.ValueOf(tt.dst).Elem().Elem().Interface(); dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
errorTests := []struct {
|
||||
src *pgtype.Numeric
|
||||
dst interface{}
|
||||
}{
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(150), Valid: true}, dst: &i8},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(40000), Valid: true}, dst: &i16},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui8},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui16},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui32},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui64},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(-1), Valid: true}, dst: &ui},
|
||||
{src: &pgtype.Numeric{Int: big.NewInt(0)}, dst: &i32},
|
||||
}
|
||||
|
||||
for i, tt := range errorTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err == nil {
|
||||
t.Errorf("%d: expected error but none was returned (%v -> %v)", i, tt.src, tt.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericEncodeDecodeBinary(t *testing.T) {
|
||||
ci := pgtype.NewConnInfo()
|
||||
tests := []interface{}{
|
||||
123,
|
||||
0.000012345,
|
||||
1.00002345,
|
||||
math.NaN(),
|
||||
float32(math.NaN()),
|
||||
math.Inf(1),
|
||||
float32(math.Inf(1)),
|
||||
math.Inf(-1),
|
||||
float32(math.Inf(-1)),
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
toString := func(n *pgtype.Numeric) string {
|
||||
ci := pgtype.NewConnInfo()
|
||||
text, err := n.EncodeText(ci, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%d (EncodeText): %v", i, err)
|
||||
}
|
||||
return string(text)
|
||||
}
|
||||
numeric := &pgtype.Numeric{}
|
||||
numeric.Set(tt)
|
||||
|
||||
encoded, err := numeric.EncodeBinary(ci, nil)
|
||||
if err != nil {
|
||||
t.Errorf("%d (EncodeBinary): %v", i, err)
|
||||
}
|
||||
decoded := &pgtype.Numeric{}
|
||||
err = decoded.DecodeBinary(ci, encoded)
|
||||
if err != nil {
|
||||
t.Errorf("%d (DecodeBinary): %v", i, err)
|
||||
}
|
||||
|
||||
text0 := toString(numeric)
|
||||
text1 := toString(decoded)
|
||||
|
||||
if text0 != text1 {
|
||||
t.Errorf("%d: expected %v to equal to %v, but doesn't", i, text0, text1)
|
||||
}
|
||||
}
|
||||
testPgxCodec(t, "numeric", tests)
|
||||
}
|
||||
|
||||
func TestNumericMarshalJSON(t *testing.T) {
|
||||
|
|
|
@ -290,7 +290,7 @@ func NewConnInfo() *ConnInfo {
|
|||
ci.RegisterDataType(DataType{Name: "_polygon", OID: PolygonArrayOID, Codec: &ArrayCodec{ElementCodec: PolygonCodec{}, ElementOID: PolygonOID}})
|
||||
ci.RegisterDataType(DataType{Name: "_name", OID: NameArrayOID, Codec: &ArrayCodec{ElementCodec: TextCodec{}, ElementOID: NameOID}})
|
||||
ci.RegisterDataType(DataType{Name: "_char", OID: QCharArrayOID, Codec: &ArrayCodec{ElementCodec: QCharCodec{}, ElementOID: QCharOID}})
|
||||
ci.RegisterDataType(DataType{Value: &NumericArray{}, Name: "_numeric", OID: NumericArrayOID})
|
||||
ci.RegisterDataType(DataType{Name: "_numeric", OID: NumericArrayOID, Codec: &ArrayCodec{ElementCodec: NumericCodec{}, ElementOID: NumericOID}})
|
||||
ci.RegisterDataType(DataType{Name: "_text", OID: TextArrayOID, Codec: &ArrayCodec{ElementCodec: TextCodec{}, ElementOID: TextOID}})
|
||||
ci.RegisterDataType(DataType{Name: "_timestamp", OID: TimestampArrayOID, Codec: &ArrayCodec{ElementCodec: TimestampCodec{}, ElementOID: TimestampOID}})
|
||||
ci.RegisterDataType(DataType{Name: "_timestamptz", OID: TimestamptzArrayOID, Codec: &ArrayCodec{ElementCodec: TimestamptzCodec{}, ElementOID: TimestamptzOID}})
|
||||
|
@ -333,7 +333,7 @@ func NewConnInfo() *ConnInfo {
|
|||
ci.RegisterDataType(DataType{Name: "lseg", OID: LsegOID, Codec: LsegCodec{}})
|
||||
ci.RegisterDataType(DataType{Name: "macaddr", OID: MacaddrOID, Codec: MacaddrCodec{}})
|
||||
ci.RegisterDataType(DataType{Name: "name", OID: NameOID, Codec: TextCodec{}})
|
||||
ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID})
|
||||
ci.RegisterDataType(DataType{Name: "numeric", OID: NumericOID, Codec: NumericCodec{}})
|
||||
// ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID})
|
||||
ci.RegisterDataType(DataType{Name: "oid", OID: OIDOID, Codec: Uint32Codec{}})
|
||||
ci.RegisterDataType(DataType{Name: "path", OID: PathOID, Codec: PathCodec{}})
|
||||
|
|
Loading…
Reference in New Issue