Split pgtype into own repo
This commit is contained in:
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -6,8 +6,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
)
|
||||
|
||||
func TestConnBeginBatch(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -9,8 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
)
|
||||
|
||||
func BenchmarkPointerPointerWithNullValues(b *testing.B) {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgproto3/v2"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-1
@@ -9,8 +9,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/stretchr/testify/require"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
"github.com/jackc/pgproto3"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
func newFastpath(cn *Conn) *fastpath {
|
||||
|
||||
@@ -8,18 +8,15 @@ require (
|
||||
github.com/jackc/pgio v1.0.0
|
||||
github.com/jackc/pgproto3 v1.1.0
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/lib/pq v1.0.0
|
||||
github.com/lib/pq v1.1.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/rs/zerolog v1.13.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
github.com/stretchr/testify v1.3.0
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
||||
@@ -16,6 +16,10 @@ github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
|
||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db h1:UpaKn/gYxzH6/zWyRQH1S260zvKqwJJ4h8+Kf09ooh0=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0 h1:mX93v750WifMD1htCt7vqeolcnpaG1gz8URVGjSzcUM=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b h1:cIcUpcEP55F/QuZWEtXyqHoWk+IV4TBiLjtBkeq/Q1c=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
@@ -27,6 +31,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
// LargeObjects is a structure used to access the large objects API. It is only
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/jackc/pgproto3/v2"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgtype"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// ACLItem is used for PostgreSQL's aclitem data type. A sample aclitem
|
||||
// might look like this:
|
||||
//
|
||||
// postgres=arwdDxt/postgres
|
||||
//
|
||||
// Note, however, that because the user/role name part of an aclitem is
|
||||
// an identifier, it follows all the usual formatting rules for SQL
|
||||
// identifiers: if it contains spaces and other special characters,
|
||||
// it should appear in double-quotes:
|
||||
//
|
||||
// postgres=arwdDxt/"role with spaces"
|
||||
//
|
||||
type ACLItem struct {
|
||||
String string
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *ACLItem) Set(src interface{}) error {
|
||||
switch value := src.(type) {
|
||||
case string:
|
||||
*dst = ACLItem{String: value, Status: Present}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = ACLItem{Status: Null}
|
||||
} else {
|
||||
*dst = ACLItem{String: *value, Status: Present}
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingStringType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to ACLItem", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ACLItem) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.String
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *ACLItem) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *string:
|
||||
*v = src.String
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *ACLItem) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = ACLItem{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
*dst = ACLItem{String: string(src), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *ACLItem) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.String...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *ACLItem) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = ACLItem{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *ACLItem) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return src.String, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ACLItemArray struct {
|
||||
Elements []ACLItem
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *ACLItemArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = ACLItemArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to ACLItemArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ACLItemArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *ACLItemArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *ACLItemArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = ACLItemArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []ACLItem
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]ACLItem, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem ACLItem
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = ACLItemArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *ACLItemArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *ACLItemArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *ACLItemArray) 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,152 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestACLItemArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "aclitem[]", []interface{}{
|
||||
&pgtype.ACLItemArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ACLItemArray{Status: pgtype.Null},
|
||||
&pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
{String: "postgres=arwdDxt/postgres", Status: pgtype.Present},
|
||||
{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present},
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
{String: "postgres=arwdDxt/postgres", Status: pgtype.Present},
|
||||
{String: "=r/postgres", Status: pgtype.Present},
|
||||
{String: "postgres=arwdDxt/postgres", Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestACLItemArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.ACLItemArray
|
||||
}{
|
||||
{
|
||||
source: []string{"=r/postgres"},
|
||||
result: pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]string)(nil)),
|
||||
result: pgtype.ACLItemArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.ACLItemArray
|
||||
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 TestACLItemArrayAssignTo(t *testing.T) {
|
||||
var stringSlice []string
|
||||
type _stringSlice []string
|
||||
var namedStringSlice _stringSlice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.ACLItemArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &stringSlice,
|
||||
expected: []string{"=r/postgres"},
|
||||
},
|
||||
{
|
||||
src: pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{{String: "=r/postgres", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedStringSlice,
|
||||
expected: _stringSlice{"=r/postgres"},
|
||||
},
|
||||
{
|
||||
src: pgtype.ACLItemArray{Status: pgtype.Null},
|
||||
dst: &stringSlice,
|
||||
expected: (([]string)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.ACLItemArray
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.ACLItemArray{
|
||||
Elements: []pgtype.ACLItem{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &stringSlice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestACLItemTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "aclitem", []interface{}{
|
||||
&pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present},
|
||||
&pgtype.ACLItem{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Status: pgtype.Present},
|
||||
&pgtype.ACLItem{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestACLItemSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.ACLItem
|
||||
}{
|
||||
{source: "postgres=arwdDxt/postgres", result: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}},
|
||||
{source: (*string)(nil), result: pgtype.ACLItem{Status: pgtype.Null}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var d pgtype.ACLItem
|
||||
err := d.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if d != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestACLItemAssignTo(t *testing.T) {
|
||||
var s string
|
||||
var ps *string
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.ACLItem
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &s, expected: "postgres=arwdDxt/postgres"},
|
||||
{src: pgtype.ACLItem{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.ACLItem
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.ACLItem{String: "postgres=arwdDxt/postgres", Status: pgtype.Present}, dst: &ps, expected: "postgres=arwdDxt/postgres"},
|
||||
}
|
||||
|
||||
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.ACLItem
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.ACLItem{Status: pgtype.Null}, dst: &s},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-352
@@ -1,352 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Information on the internals of PostgreSQL arrays can be found in
|
||||
// src/include/utils/array.h and src/backend/utils/adt/arrayfuncs.c. Of
|
||||
// particular interest is the array_send function.
|
||||
|
||||
type ArrayHeader struct {
|
||||
ContainsNull bool
|
||||
ElementOID int32
|
||||
Dimensions []ArrayDimension
|
||||
}
|
||||
|
||||
type ArrayDimension struct {
|
||||
Length int32
|
||||
LowerBound int32
|
||||
}
|
||||
|
||||
func (dst *ArrayHeader) DecodeBinary(ci *ConnInfo, src []byte) (int, error) {
|
||||
if len(src) < 12 {
|
||||
return 0, errors.Errorf("array header too short: %d", len(src))
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
numDims := int(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
dst.ContainsNull = binary.BigEndian.Uint32(src[rp:]) == 1
|
||||
rp += 4
|
||||
|
||||
dst.ElementOID = int32(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
if numDims > 0 {
|
||||
dst.Dimensions = make([]ArrayDimension, numDims)
|
||||
}
|
||||
if len(src) < 12+numDims*8 {
|
||||
return 0, errors.Errorf("array header too short for %d dimensions: %d", numDims, len(src))
|
||||
}
|
||||
for i := range dst.Dimensions {
|
||||
dst.Dimensions[i].Length = int32(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
|
||||
dst.Dimensions[i].LowerBound = int32(binary.BigEndian.Uint32(src[rp:]))
|
||||
rp += 4
|
||||
}
|
||||
|
||||
return rp, nil
|
||||
}
|
||||
|
||||
func (src *ArrayHeader) EncodeBinary(ci *ConnInfo, buf []byte) []byte {
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Dimensions)))
|
||||
|
||||
var containsNull int32
|
||||
if src.ContainsNull {
|
||||
containsNull = 1
|
||||
}
|
||||
buf = pgio.AppendInt32(buf, containsNull)
|
||||
|
||||
buf = pgio.AppendInt32(buf, src.ElementOID)
|
||||
|
||||
for i := range src.Dimensions {
|
||||
buf = pgio.AppendInt32(buf, src.Dimensions[i].Length)
|
||||
buf = pgio.AppendInt32(buf, src.Dimensions[i].LowerBound)
|
||||
}
|
||||
|
||||
return buf
|
||||
}
|
||||
|
||||
type UntypedTextArray struct {
|
||||
Elements []string
|
||||
Dimensions []ArrayDimension
|
||||
}
|
||||
|
||||
func ParseUntypedTextArray(src string) (*UntypedTextArray, error) {
|
||||
dst := &UntypedTextArray{}
|
||||
|
||||
buf := bytes.NewBufferString(src)
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
var explicitDimensions []ArrayDimension
|
||||
|
||||
// Array has explicit dimensions
|
||||
if r == '[' {
|
||||
buf.UnreadRune()
|
||||
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r == '=' {
|
||||
break
|
||||
} else if r != '[' {
|
||||
return nil, errors.Errorf("invalid array, expected '[' or '=' got %v", r)
|
||||
}
|
||||
|
||||
lower, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r != ':' {
|
||||
return nil, errors.Errorf("invalid array, expected ':' got %v", r)
|
||||
}
|
||||
|
||||
upper, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r != ']' {
|
||||
return nil, errors.Errorf("invalid array, expected ']' got %v", r)
|
||||
}
|
||||
|
||||
explicitDimensions = append(explicitDimensions, ArrayDimension{LowerBound: lower, Length: upper - lower + 1})
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
return nil, errors.Errorf("invalid array, expected '{': %v", err)
|
||||
}
|
||||
|
||||
implicitDimensions := []ArrayDimension{{LowerBound: 1, Length: 0}}
|
||||
|
||||
// Consume all initial opening brackets. This provides number of dimensions.
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
if r == '{' {
|
||||
implicitDimensions[len(implicitDimensions)-1].Length = 1
|
||||
implicitDimensions = append(implicitDimensions, ArrayDimension{LowerBound: 1})
|
||||
} else {
|
||||
buf.UnreadRune()
|
||||
break
|
||||
}
|
||||
}
|
||||
currentDim := len(implicitDimensions) - 1
|
||||
counterDim := currentDim
|
||||
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array: %v", err)
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '{':
|
||||
if currentDim == counterDim {
|
||||
implicitDimensions[currentDim].Length++
|
||||
}
|
||||
currentDim++
|
||||
case ',':
|
||||
case '}':
|
||||
currentDim--
|
||||
if currentDim < counterDim {
|
||||
counterDim = currentDim
|
||||
}
|
||||
default:
|
||||
buf.UnreadRune()
|
||||
value, err := arrayParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("invalid array value: %v", err)
|
||||
}
|
||||
if currentDim == counterDim {
|
||||
implicitDimensions[currentDim].Length++
|
||||
}
|
||||
dst.Elements = append(dst.Elements, value)
|
||||
}
|
||||
|
||||
if currentDim < 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
skipWhitespace(buf)
|
||||
|
||||
if buf.Len() > 0 {
|
||||
return nil, errors.Errorf("unexpected trailing data: %v", buf.String())
|
||||
}
|
||||
|
||||
if len(dst.Elements) == 0 {
|
||||
dst.Dimensions = nil
|
||||
} else if len(explicitDimensions) > 0 {
|
||||
dst.Dimensions = explicitDimensions
|
||||
} else {
|
||||
dst.Dimensions = implicitDimensions
|
||||
}
|
||||
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
func skipWhitespace(buf *bytes.Buffer) {
|
||||
var r rune
|
||||
var err error
|
||||
for r, _, _ = buf.ReadRune(); unicode.IsSpace(r); r, _, _ = buf.ReadRune() {
|
||||
}
|
||||
|
||||
if err != io.EOF {
|
||||
buf.UnreadRune()
|
||||
}
|
||||
}
|
||||
|
||||
func arrayParseValue(buf *bytes.Buffer) (string, error) {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if r == '"' {
|
||||
return arrayParseQuotedValue(buf)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case ',', '}':
|
||||
buf.UnreadRune()
|
||||
return s.String(), nil
|
||||
}
|
||||
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
func arrayParseQuotedValue(buf *bytes.Buffer) (string, error) {
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch r {
|
||||
case '\\':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
case '"':
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.UnreadRune()
|
||||
return s.String(), nil
|
||||
}
|
||||
s.WriteRune(r)
|
||||
}
|
||||
}
|
||||
|
||||
func arrayParseInteger(buf *bytes.Buffer) (int32, error) {
|
||||
s := &bytes.Buffer{}
|
||||
|
||||
for {
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if '0' <= r && r <= '9' {
|
||||
s.WriteRune(r)
|
||||
} else {
|
||||
buf.UnreadRune()
|
||||
n, err := strconv.ParseInt(s.String(), 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(n), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func EncodeTextArrayDimensions(buf []byte, dimensions []ArrayDimension) []byte {
|
||||
var customDimensions bool
|
||||
for _, dim := range dimensions {
|
||||
if dim.LowerBound != 1 {
|
||||
customDimensions = true
|
||||
}
|
||||
}
|
||||
|
||||
if !customDimensions {
|
||||
return buf
|
||||
}
|
||||
|
||||
for _, dim := range dimensions {
|
||||
buf = append(buf, '[')
|
||||
buf = append(buf, strconv.FormatInt(int64(dim.LowerBound), 10)...)
|
||||
buf = append(buf, ':')
|
||||
buf = append(buf, strconv.FormatInt(int64(dim.LowerBound+dim.Length-1), 10)...)
|
||||
buf = append(buf, ']')
|
||||
}
|
||||
|
||||
return append(buf, '=')
|
||||
}
|
||||
|
||||
var quoteArrayReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)
|
||||
|
||||
func quoteArrayElement(src string) string {
|
||||
return `"` + quoteArrayReplacer.Replace(src) + `"`
|
||||
}
|
||||
|
||||
func QuoteArrayElementIfNeeded(src string) string {
|
||||
if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || src[0] == ' ' || src[len(src)-1] == ' ' || strings.ContainsAny(src, `{},"\`) {
|
||||
return quoteArrayElement(src)
|
||||
}
|
||||
return src
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
)
|
||||
|
||||
func TestParseUntypedTextArray(t *testing.T) {
|
||||
tests := []struct {
|
||||
source string
|
||||
result pgtype.UntypedTextArray
|
||||
}{
|
||||
{
|
||||
source: "{}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "{1}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"1"},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "{a,b}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"a", "b"},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: `{"NULL"}`,
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"NULL"},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: `{""}`,
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{""},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: `{"He said, \"Hello.\""}`,
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{`He said, "Hello."`},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "{{a,b},{c,d},{e,f}}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"a", "b", "c", "d", "e", "f"},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "{{{a,b},{c,d},{e,f}},{{a,b},{c,d},{e,f}}}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"a", "b", "c", "d", "e", "f", "a", "b", "c", "d", "e", "f"},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 1},
|
||||
{Length: 3, LowerBound: 1},
|
||||
{Length: 2, LowerBound: 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "[4:4]={1}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"1"},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 1, LowerBound: 4}},
|
||||
},
|
||||
},
|
||||
{
|
||||
source: "[4:5][2:3]={{a,b},{c,d}}",
|
||||
result: pgtype.UntypedTextArray{
|
||||
Elements: []string{"a", "b", "c", "d"},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
r, err := pgtype.ParseUntypedTextArray(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*r, tt.result) {
|
||||
t.Errorf("%d: expected %+v to be parsed to %+v, but it was %+v", i, tt.source, tt.result, *r)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
type Bit Varbit
|
||||
|
||||
func (dst *Bit) Set(src interface{}) error {
|
||||
return (*Varbit)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *Bit) Get() interface{} {
|
||||
return (*Varbit)(dst).Get()
|
||||
}
|
||||
|
||||
func (src *Bit) AssignTo(dst interface{}) error {
|
||||
return (*Varbit)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *Bit) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return (*Varbit)(dst).DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
func (src *Bit) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Varbit)(src).EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Bit) Scan(src interface{}) error {
|
||||
return (*Varbit)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Bit) Value() (driver.Value, error) {
|
||||
return (*Varbit)(src).Value()
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestBitTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "bit(40)", []interface{}{
|
||||
&pgtype.Varbit{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Status: pgtype.Present},
|
||||
&pgtype.Varbit{Bytes: []byte{0, 1, 128, 254, 255}, Len: 40, Status: pgtype.Present},
|
||||
&pgtype.Varbit{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBitNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select B'111111111'",
|
||||
Value: &pgtype.Bit{Bytes: []byte{255, 128}, Len: 9, Status: pgtype.Present},
|
||||
},
|
||||
})
|
||||
}
|
||||
-165
@@ -1,165 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"strconv"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Bool struct {
|
||||
Bool bool
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Bool) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case bool:
|
||||
*dst = Bool{Bool: value, Status: Present}
|
||||
case string:
|
||||
bb, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Bool{Bool: bb, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingBoolType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Bool", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Bool) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Bool
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Bool) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *bool:
|
||||
*v = src.Bool
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Bool) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 1 {
|
||||
return errors.Errorf("invalid length for bool: %v", len(src))
|
||||
}
|
||||
|
||||
*dst = Bool{Bool: src[0] == 't', Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Bool) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 1 {
|
||||
return errors.Errorf("invalid length for bool: %v", len(src))
|
||||
}
|
||||
|
||||
*dst = Bool{Bool: src[0] == 1, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Bool) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
if src.Bool {
|
||||
buf = append(buf, 't')
|
||||
} else {
|
||||
buf = append(buf, 'f')
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Bool) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
if src.Bool {
|
||||
buf = append(buf, 1)
|
||||
} else {
|
||||
buf = append(buf, 0)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Bool) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bool{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case bool:
|
||||
*dst = Bool{Bool: src, Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Bool) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return src.Bool, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BoolArray struct {
|
||||
Elements []Bool
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *BoolArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = BoolArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to BoolArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BoolArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *BoolArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *BoolArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = BoolArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Bool
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Bool, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Bool
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = BoolArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BoolArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = BoolArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = BoolArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Bool, 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 = BoolArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *BoolArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *BoolArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("bool"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "bool")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *BoolArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *BoolArray) 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,153 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestBoolArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "bool[]", []interface{}{
|
||||
&pgtype.BoolArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BoolArray{Status: pgtype.Null},
|
||||
&pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Bool: false, Status: pgtype.Present},
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Bool: false, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Bool: false, Status: pgtype.Present},
|
||||
{Bool: true, Status: pgtype.Present},
|
||||
{Bool: false, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBoolArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.BoolArray
|
||||
}{
|
||||
{
|
||||
source: []bool{true},
|
||||
result: pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]bool)(nil)),
|
||||
result: pgtype.BoolArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.BoolArray
|
||||
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 TestBoolArrayAssignTo(t *testing.T) {
|
||||
var boolSlice []bool
|
||||
type _boolSlice []bool
|
||||
var namedBoolSlice _boolSlice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.BoolArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &boolSlice,
|
||||
expected: []bool{true},
|
||||
},
|
||||
{
|
||||
src: pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{{Bool: true, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedBoolSlice,
|
||||
expected: _boolSlice{true},
|
||||
},
|
||||
{
|
||||
src: pgtype.BoolArray{Status: pgtype.Null},
|
||||
dst: &boolSlice,
|
||||
expected: (([]bool)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.BoolArray
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.BoolArray{
|
||||
Elements: []pgtype.Bool{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &boolSlice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestBoolTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "bool", []interface{}{
|
||||
&pgtype.Bool{Bool: false, Status: pgtype.Present},
|
||||
&pgtype.Bool{Bool: true, Status: pgtype.Present},
|
||||
&pgtype.Bool{Bool: false, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBoolSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Bool
|
||||
}{
|
||||
{source: true, result: pgtype.Bool{Bool: true, Status: pgtype.Present}},
|
||||
{source: false, result: pgtype.Bool{Bool: false, Status: pgtype.Present}},
|
||||
{source: "true", result: pgtype.Bool{Bool: true, Status: pgtype.Present}},
|
||||
{source: "false", result: pgtype.Bool{Bool: false, Status: pgtype.Present}},
|
||||
{source: "t", result: pgtype.Bool{Bool: true, Status: pgtype.Present}},
|
||||
{source: "f", result: pgtype.Bool{Bool: false, Status: pgtype.Present}},
|
||||
{source: _bool(true), result: pgtype.Bool{Bool: true, Status: pgtype.Present}},
|
||||
{source: _bool(false), result: pgtype.Bool{Bool: false, Status: pgtype.Present}},
|
||||
{source: nil, result: pgtype.Bool{Status: pgtype.Null}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Bool
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoolAssignTo(t *testing.T) {
|
||||
var b bool
|
||||
var _b _bool
|
||||
var pb *bool
|
||||
var _pb *_bool
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Bool
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &b, expected: false},
|
||||
{src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &b, expected: true},
|
||||
{src: pgtype.Bool{Bool: false, Status: pgtype.Present}, dst: &_b, expected: _bool(false)},
|
||||
{src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_b, expected: _bool(true)},
|
||||
{src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &pb, expected: ((*bool)(nil))},
|
||||
{src: pgtype.Bool{Bool: false, Status: pgtype.Null}, dst: &_pb, expected: ((*_bool)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Bool
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &pb, expected: true},
|
||||
{src: pgtype.Bool{Bool: true, Status: pgtype.Present}, dst: &_pb, expected: _bool(true)},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Box struct {
|
||||
P [2]Vec2
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Box) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Box", src)
|
||||
}
|
||||
|
||||
func (dst *Box) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Box) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Box) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Box{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) < 11 {
|
||||
return errors.Errorf("invalid length for Box: %v", len(src))
|
||||
}
|
||||
|
||||
str := string(src[1:])
|
||||
|
||||
var end int
|
||||
end = strings.IndexByte(str, ',')
|
||||
|
||||
x1, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+1:]
|
||||
end = strings.IndexByte(str, ')')
|
||||
|
||||
y1, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+3:]
|
||||
end = strings.IndexByte(str, ',')
|
||||
|
||||
x2, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+1 : len(str)-1]
|
||||
|
||||
y2, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Box{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Box) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Box{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 32 {
|
||||
return errors.Errorf("invalid length for Box: %v", len(src))
|
||||
}
|
||||
|
||||
x1 := binary.BigEndian.Uint64(src)
|
||||
y1 := binary.BigEndian.Uint64(src[8:])
|
||||
x2 := binary.BigEndian.Uint64(src[16:])
|
||||
y2 := binary.BigEndian.Uint64(src[24:])
|
||||
|
||||
*dst = Box{
|
||||
P: [2]Vec2{
|
||||
{math.Float64frombits(x1), math.Float64frombits(y1)},
|
||||
{math.Float64frombits(x2), math.Float64frombits(y2)},
|
||||
},
|
||||
Status: Present,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Box) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`,
|
||||
strconv.FormatFloat(src.P[0].X, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[0].Y, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[1].X, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[1].Y, 'f', -1, 64),
|
||||
)...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Box) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].Y))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].X))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].Y))
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Box) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Box{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Box) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestBoxTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "box", []interface{}{
|
||||
&pgtype.Box{
|
||||
P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Box{
|
||||
P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Box{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestBoxNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select '3.14, 1.678, 7.1, 5.234'::box",
|
||||
Value: &pgtype.Box{
|
||||
P: [2]pgtype.Vec2{{7.1, 5.234}, {3.14, 1.678}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// BPChar is fixed-length, blank padded char type
|
||||
// character(n), char(n)
|
||||
type BPChar Text
|
||||
|
||||
// Set converts from src to dst.
|
||||
func (dst *BPChar) Set(src interface{}) error {
|
||||
return (*Text)(dst).Set(src)
|
||||
}
|
||||
|
||||
// Get returns underlying value
|
||||
func (dst *BPChar) Get() interface{} {
|
||||
return (*Text)(dst).Get()
|
||||
}
|
||||
|
||||
// AssignTo assigns from src to dst.
|
||||
func (src *BPChar) AssignTo(dst interface{}) error {
|
||||
if src.Status == Present {
|
||||
switch v := dst.(type) {
|
||||
case *rune:
|
||||
runes := []rune(src.String)
|
||||
if len(runes) == 1 {
|
||||
*v = runes[0]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return (*Text)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *BPChar) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*Text)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (dst *BPChar) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return (*Text)(dst).DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
func (src *BPChar) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Text)(src).EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
func (src *BPChar) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Text)(src).EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *BPChar) Scan(src interface{}) error {
|
||||
return (*Text)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *BPChar) Value() (driver.Value, error) {
|
||||
return (*Text)(src).Value()
|
||||
}
|
||||
|
||||
func (src *BPChar) MarshalJSON() ([]byte, error) {
|
||||
return (*Text)(src).MarshalJSON()
|
||||
}
|
||||
|
||||
func (dst *BPChar) UnmarshalJSON(b []byte) error {
|
||||
return (*Text)(dst).UnmarshalJSON(b)
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type BPCharArray struct {
|
||||
Elements []BPChar
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *BPCharArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = BPCharArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to BPCharArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BPCharArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *BPCharArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *BPCharArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = BPCharArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []BPChar
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]BPChar, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem BPChar
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = BPCharArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *BPCharArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = BPCharArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = BPCharArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]BPChar, 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 = BPCharArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *BPCharArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *BPCharArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("bpchar"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "bpchar")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *BPCharArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *BPCharArray) 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,55 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestBPCharArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "char(8)[]", []interface{}{
|
||||
&pgtype.BPCharArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BPCharArray{
|
||||
Elements: []pgtype.BPChar{
|
||||
pgtype.BPChar{String: "foo ", Status: pgtype.Present},
|
||||
pgtype.BPChar{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BPCharArray{Status: pgtype.Null},
|
||||
&pgtype.BPCharArray{
|
||||
Elements: []pgtype.BPChar{
|
||||
pgtype.BPChar{String: "bar ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: "NuLL ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: `wow"quz\`, Status: pgtype.Present},
|
||||
pgtype.BPChar{String: "1 ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: "1 ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: "null ", Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 3, LowerBound: 1},
|
||||
{Length: 2, LowerBound: 1},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.BPCharArray{
|
||||
Elements: []pgtype.BPChar{
|
||||
pgtype.BPChar{String: " bar ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: " baz ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: " quz ", Status: pgtype.Present},
|
||||
pgtype.BPChar{String: "foo ", Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestChar3Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "char(3)", []interface{}{
|
||||
&pgtype.BPChar{String: "a ", Status: pgtype.Present},
|
||||
&pgtype.BPChar{String: " a ", Status: pgtype.Present},
|
||||
&pgtype.BPChar{String: "嗨 ", Status: pgtype.Present},
|
||||
&pgtype.BPChar{String: " ", Status: pgtype.Present},
|
||||
&pgtype.BPChar{Status: pgtype.Null},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(pgtype.BPChar)
|
||||
b := bb.(pgtype.BPChar)
|
||||
|
||||
return a.Status == b.Status && a.String == b.String
|
||||
})
|
||||
}
|
||||
|
||||
func TestBPCharAssignTo(t *testing.T) {
|
||||
var (
|
||||
str string
|
||||
run rune
|
||||
)
|
||||
simpleTests := []struct {
|
||||
src pgtype.BPChar
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.BPChar{String: "simple", Status: pgtype.Present}, dst: &str, expected: "simple"},
|
||||
{src: pgtype.BPChar{String: "嗨", Status: pgtype.Present}, dst: &run, expected: '嗨'},
|
||||
}
|
||||
|
||||
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(); dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/hex"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Bytea struct {
|
||||
Bytes []byte
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Bytea) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bytea{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case []byte:
|
||||
if value != nil {
|
||||
*dst = Bytea{Bytes: value, Status: Present}
|
||||
} else {
|
||||
*dst = Bytea{Status: Null}
|
||||
}
|
||||
default:
|
||||
if originalSrc, ok := underlyingBytesType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Bytea", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Bytea) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Bytes
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Bytea) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *[]byte:
|
||||
buf := make([]byte, len(src.Bytes))
|
||||
copy(buf, src.Bytes)
|
||||
*v = buf
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
// DecodeText only supports the hex format. This has been the default since
|
||||
// PostgreSQL 9.0.
|
||||
func (dst *Bytea) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Bytea{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) < 2 || src[0] != '\\' || src[1] != 'x' {
|
||||
return errors.Errorf("invalid hex format")
|
||||
}
|
||||
|
||||
buf := make([]byte, (len(src)-2)/2)
|
||||
_, err := hex.Decode(buf, src[2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Bytea{Bytes: buf, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Bytea) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Bytea{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
*dst = Bytea{Bytes: src, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Bytea) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, hex.EncodeToString(src.Bytes)...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Bytea) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.Bytes...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Bytea) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Bytea{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
buf := make([]byte, len(src))
|
||||
copy(buf, src)
|
||||
*dst = Bytea{Bytes: buf, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Bytea) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return src.Bytes, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type ByteaArray struct {
|
||||
Elements []Bytea
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *ByteaArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = ByteaArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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 = ByteaArray{
|
||||
Elements: elements,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to ByteaArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ByteaArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *ByteaArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *ByteaArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = ByteaArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Bytea
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Bytea, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Bytea
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = ByteaArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *ByteaArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = ByteaArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = ByteaArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Bytea, 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 = ByteaArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *ByteaArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *ByteaArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("bytea"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "bytea")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *ByteaArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *ByteaArray) 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,120 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestByteaArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "bytea[]", []interface{}{
|
||||
&pgtype.ByteaArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ByteaArray{
|
||||
Elements: []pgtype.Bytea{
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ByteaArray{Status: pgtype.Null},
|
||||
&pgtype.ByteaArray{
|
||||
Elements: []pgtype.Bytea{
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Bytes: []byte{}, Status: pgtype.Present},
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Bytes: []byte{1}, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.ByteaArray{
|
||||
Elements: []pgtype.Bytea{
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Bytes: []byte{}, Status: pgtype.Present},
|
||||
{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
{Bytes: []byte{1}, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestByteaArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.ByteaArray
|
||||
}{
|
||||
{
|
||||
source: [][]byte{{1, 2, 3}},
|
||||
result: pgtype.ByteaArray{
|
||||
Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([][]byte)(nil)),
|
||||
result: pgtype.ByteaArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.ByteaArray
|
||||
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 TestByteaArrayAssignTo(t *testing.T) {
|
||||
var byteByteSlice [][]byte
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.ByteaArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.ByteaArray{
|
||||
Elements: []pgtype.Bytea{{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &byteByteSlice,
|
||||
expected: [][]byte{{1, 2, 3}},
|
||||
},
|
||||
{
|
||||
src: pgtype.ByteaArray{Status: pgtype.Null},
|
||||
dst: &byteByteSlice,
|
||||
expected: (([][]byte)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestByteaTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "bytea", []interface{}{
|
||||
&pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present},
|
||||
&pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present},
|
||||
&pgtype.Bytea{Bytes: nil, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestByteaSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Bytea
|
||||
}{
|
||||
{source: []byte{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}},
|
||||
{source: []byte{}, result: pgtype.Bytea{Bytes: []byte{}, Status: pgtype.Present}},
|
||||
{source: []byte(nil), result: pgtype.Bytea{Status: pgtype.Null}},
|
||||
{source: _byteSlice{1, 2, 3}, result: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}},
|
||||
{source: _byteSlice(nil), result: pgtype.Bytea{Status: pgtype.Null}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Bytea
|
||||
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 TestByteaAssignTo(t *testing.T) {
|
||||
var buf []byte
|
||||
var _buf _byteSlice
|
||||
var pbuf *[]byte
|
||||
var _pbuf *_byteSlice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Bytea
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &buf, expected: []byte{1, 2, 3}},
|
||||
{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_buf, expected: _byteSlice{1, 2, 3}},
|
||||
{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &pbuf, expected: &[]byte{1, 2, 3}},
|
||||
{src: pgtype.Bytea{Bytes: []byte{1, 2, 3}, Status: pgtype.Present}, dst: &_pbuf, expected: &_byteSlice{1, 2, 3}},
|
||||
{src: pgtype.Bytea{Status: pgtype.Null}, dst: &pbuf, expected: ((*[]byte)(nil))},
|
||||
{src: pgtype.Bytea{Status: pgtype.Null}, dst: &_pbuf, expected: ((*_byteSlice)(nil))},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// CID is PostgreSQL's Command Identifier type.
|
||||
//
|
||||
// When one does
|
||||
//
|
||||
// select cmin, cmax, * from some_table;
|
||||
//
|
||||
// it is the data type of the cmin and cmax hidden system columns.
|
||||
//
|
||||
// It is currently implemented as an unsigned four byte integer.
|
||||
// Its definition can be found in src/include/c.h as CommandId
|
||||
// in the PostgreSQL sources.
|
||||
type CID pguint32
|
||||
|
||||
// Set converts from src to dst. Note that as CID is not a general
|
||||
// number type Set does not do automatic type conversion as other number
|
||||
// types do.
|
||||
func (dst *CID) Set(src interface{}) error {
|
||||
return (*pguint32)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *CID) Get() interface{} {
|
||||
return (*pguint32)(dst).Get()
|
||||
}
|
||||
|
||||
// AssignTo assigns from src to dst. Note that as CID is not a general number
|
||||
// type AssignTo does not do automatic type conversion as other number types do.
|
||||
func (src *CID) AssignTo(dst interface{}) error {
|
||||
return (*pguint32)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *CID) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*pguint32)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (dst *CID) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return (*pguint32)(dst).DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
func (src *CID) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*pguint32)(src).EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
func (src *CID) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*pguint32)(src).EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *CID) Scan(src interface{}) error {
|
||||
return (*pguint32)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *CID) Value() (driver.Value, error) {
|
||||
return (*pguint32)(src).Value()
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestCIDTranscode(t *testing.T) {
|
||||
pgTypeName := "cid"
|
||||
values := []interface{}{
|
||||
&pgtype.CID{Uint: 42, Status: pgtype.Present},
|
||||
&pgtype.CID{Status: pgtype.Null},
|
||||
}
|
||||
eqFunc := func(a, b interface{}) bool {
|
||||
return reflect.DeepEqual(a, b)
|
||||
}
|
||||
|
||||
testutil.TestPgxSuccessfulTranscodeEqFunc(t, pgTypeName, values, eqFunc)
|
||||
|
||||
for _, driverName := range []string{"github.com/lib/pq", "github.com/jackc/pgx/stdlib"} {
|
||||
testutil.TestDatabaseSQLSuccessfulTranscodeEqFunc(t, driverName, pgTypeName, values, eqFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.CID
|
||||
}{
|
||||
{source: uint32(1), result: pgtype.CID{Uint: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.CID
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIDAssignTo(t *testing.T) {
|
||||
var ui32 uint32
|
||||
var pui32 *uint32
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.CID
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.CID{Status: pgtype.Null}, dst: &pui32, expected: ((*uint32)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.CID
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.CID{Uint: 42, Status: pgtype.Present}, dst: &pui32, expected: uint32(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.CID
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.CID{Status: pgtype.Null}, dst: &ui32},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
type CIDR Inet
|
||||
|
||||
func (dst *CIDR) Set(src interface{}) error {
|
||||
return (*Inet)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *CIDR) Get() interface{} {
|
||||
return (*Inet)(dst).Get()
|
||||
}
|
||||
|
||||
func (src *CIDR) AssignTo(dst interface{}) error {
|
||||
return (*Inet)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *CIDR) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*Inet)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (dst *CIDR) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return (*Inet)(dst).DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
func (src *CIDR) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Inet)(src).EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
func (src *CIDR) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Inet)(src).EncodeBinary(ci, buf)
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type CIDRArray struct {
|
||||
Elements []CIDR
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *CIDRArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = CIDRArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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 = 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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to CIDRArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *CIDRArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *CIDRArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *CIDRArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = CIDRArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []CIDR
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]CIDR, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem CIDR
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = CIDRArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *CIDRArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = CIDRArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = CIDRArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]CIDR, 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 = CIDRArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *CIDRArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *CIDRArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("cidr"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "cidr")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *CIDRArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *CIDRArray) 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,165 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestCIDRArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "cidr[]", []interface{}{
|
||||
&pgtype.CIDRArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.CIDRArray{Status: pgtype.Null},
|
||||
&pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{
|
||||
{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{
|
||||
{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestCIDRArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.CIDRArray
|
||||
}{
|
||||
{
|
||||
source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")},
|
||||
result: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]*net.IPNet)(nil)),
|
||||
result: pgtype.CIDRArray{Status: pgtype.Null},
|
||||
},
|
||||
{
|
||||
source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
result: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]net.IP)(nil)),
|
||||
result: pgtype.CIDRArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.CIDRArray
|
||||
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 TestCIDRArrayAssignTo(t *testing.T) {
|
||||
var ipnetSlice []*net.IPNet
|
||||
var ipSlice []net.IP
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.CIDRArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipnetSlice,
|
||||
expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")},
|
||||
},
|
||||
{
|
||||
src: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipnetSlice,
|
||||
expected: []*net.IPNet{nil},
|
||||
},
|
||||
{
|
||||
src: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipSlice,
|
||||
expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
},
|
||||
{
|
||||
src: pgtype.CIDRArray{
|
||||
Elements: []pgtype.CIDR{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipSlice,
|
||||
expected: []net.IP{nil},
|
||||
},
|
||||
{
|
||||
src: pgtype.CIDRArray{Status: pgtype.Null},
|
||||
dst: &ipnetSlice,
|
||||
expected: (([]*net.IPNet)(nil)),
|
||||
},
|
||||
{
|
||||
src: pgtype.CIDRArray{Status: pgtype.Null},
|
||||
dst: &ipSlice,
|
||||
expected: (([]net.IP)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Circle struct {
|
||||
P Vec2
|
||||
R float64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Circle) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Circle", src)
|
||||
}
|
||||
|
||||
func (dst *Circle) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Circle) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Circle) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Circle{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) < 9 {
|
||||
return errors.Errorf("invalid length for Circle: %v", len(src))
|
||||
}
|
||||
|
||||
str := string(src[2:])
|
||||
end := strings.IndexByte(str, ',')
|
||||
x, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+1:]
|
||||
end = strings.IndexByte(str, ')')
|
||||
|
||||
y, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+2 : len(str)-1]
|
||||
|
||||
r, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Circle{P: Vec2{x, y}, R: r, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Circle) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Circle{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 24 {
|
||||
return errors.Errorf("invalid length for Circle: %v", len(src))
|
||||
}
|
||||
|
||||
x := binary.BigEndian.Uint64(src)
|
||||
y := binary.BigEndian.Uint64(src[8:])
|
||||
r := binary.BigEndian.Uint64(src[16:])
|
||||
|
||||
*dst = Circle{
|
||||
P: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
|
||||
R: math.Float64frombits(r),
|
||||
Status: Present,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Circle) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, fmt.Sprintf(`<(%s,%s),%s>`,
|
||||
strconv.FormatFloat(src.P.X, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P.Y, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.R, 'f', -1, 64),
|
||||
)...)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Circle) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P.X))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P.Y))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.R))
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Circle) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Circle{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Circle) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestCircleTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "circle", []interface{}{
|
||||
&pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Status: pgtype.Present},
|
||||
&pgtype.Circle{P: pgtype.Vec2{-1.234, -5.6789}, R: 12.9, Status: pgtype.Present},
|
||||
&pgtype.Circle{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
@@ -1,424 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const maxUint = ^uint(0)
|
||||
const maxInt = int(maxUint >> 1)
|
||||
const minInt = -maxInt - 1
|
||||
|
||||
// underlyingNumberType gets the underlying type that can be converted to Int2, Int4, Int8, Float4, or Float8
|
||||
func underlyingNumberType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
case reflect.Int:
|
||||
convVal := int(refVal.Int())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Int8:
|
||||
convVal := int8(refVal.Int())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Int16:
|
||||
convVal := int16(refVal.Int())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Int32:
|
||||
convVal := int32(refVal.Int())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Int64:
|
||||
convVal := int64(refVal.Int())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Uint:
|
||||
convVal := uint(refVal.Uint())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Uint8:
|
||||
convVal := uint8(refVal.Uint())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Uint16:
|
||||
convVal := uint16(refVal.Uint())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Uint32:
|
||||
convVal := uint32(refVal.Uint())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Uint64:
|
||||
convVal := uint64(refVal.Uint())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Float32:
|
||||
convVal := float32(refVal.Float())
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.Float64:
|
||||
convVal := refVal.Float()
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
case reflect.String:
|
||||
convVal := refVal.String()
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// underlyingBoolType gets the underlying type that can be converted to Bool
|
||||
func underlyingBoolType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
case reflect.Bool:
|
||||
convVal := refVal.Bool()
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// underlyingBytesType gets the underlying type that can be converted to []byte
|
||||
func underlyingBytesType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
case reflect.Slice:
|
||||
if refVal.Type().Elem().Kind() == reflect.Uint8 {
|
||||
convVal := refVal.Bytes()
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// underlyingStringType gets the underlying type that can be converted to String
|
||||
func underlyingStringType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
case reflect.String:
|
||||
convVal := refVal.String()
|
||||
return convVal, reflect.TypeOf(convVal) != refVal.Type()
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// underlyingPtrType dereferences a pointer
|
||||
func underlyingPtrType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// underlyingTimeType gets the underlying type that can be converted to time.Time
|
||||
func underlyingTimeType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return time.Time{}, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
}
|
||||
|
||||
timeType := reflect.TypeOf(time.Time{})
|
||||
if refVal.Type().ConvertibleTo(timeType) {
|
||||
return refVal.Convert(timeType).Interface(), true
|
||||
}
|
||||
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// underlyingSliceType gets the underlying slice type
|
||||
func underlyingSliceType(val interface{}) (interface{}, bool) {
|
||||
refVal := reflect.ValueOf(val)
|
||||
|
||||
switch refVal.Kind() {
|
||||
case reflect.Ptr:
|
||||
if refVal.IsNil() {
|
||||
return nil, false
|
||||
}
|
||||
convVal := refVal.Elem().Interface()
|
||||
return convVal, true
|
||||
case reflect.Slice:
|
||||
baseSliceType := reflect.SliceOf(refVal.Type().Elem())
|
||||
if refVal.Type().ConvertibleTo(baseSliceType) {
|
||||
convVal := refVal.Convert(baseSliceType)
|
||||
return convVal.Interface(), reflect.TypeOf(convVal.Interface()) != refVal.Type()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func int64AssignTo(srcVal int64, srcStatus Status, dst interface{}) error {
|
||||
if srcStatus == Present {
|
||||
switch v := dst.(type) {
|
||||
case *int:
|
||||
if srcVal < int64(minInt) {
|
||||
return errors.Errorf("%d is less than minimum value for int", srcVal)
|
||||
} else if srcVal > int64(maxInt) {
|
||||
return errors.Errorf("%d is greater than maximum value for int", srcVal)
|
||||
}
|
||||
*v = int(srcVal)
|
||||
case *int8:
|
||||
if srcVal < math.MinInt8 {
|
||||
return errors.Errorf("%d is less than minimum value for int8", srcVal)
|
||||
} else if srcVal > math.MaxInt8 {
|
||||
return errors.Errorf("%d is greater than maximum value for int8", srcVal)
|
||||
}
|
||||
*v = int8(srcVal)
|
||||
case *int16:
|
||||
if srcVal < math.MinInt16 {
|
||||
return errors.Errorf("%d is less than minimum value for int16", srcVal)
|
||||
} else if srcVal > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for int16", srcVal)
|
||||
}
|
||||
*v = int16(srcVal)
|
||||
case *int32:
|
||||
if srcVal < math.MinInt32 {
|
||||
return errors.Errorf("%d is less than minimum value for int32", srcVal)
|
||||
} else if srcVal > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for int32", srcVal)
|
||||
}
|
||||
*v = int32(srcVal)
|
||||
case *int64:
|
||||
if srcVal < math.MinInt64 {
|
||||
return errors.Errorf("%d is less than minimum value for int64", srcVal)
|
||||
} else if srcVal > math.MaxInt64 {
|
||||
return errors.Errorf("%d is greater than maximum value for int64", srcVal)
|
||||
}
|
||||
*v = int64(srcVal)
|
||||
case *uint:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for uint", srcVal)
|
||||
} else if uint64(srcVal) > uint64(maxUint) {
|
||||
return errors.Errorf("%d is greater than maximum value for uint", srcVal)
|
||||
}
|
||||
*v = uint(srcVal)
|
||||
case *uint8:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for uint8", srcVal)
|
||||
} else if srcVal > math.MaxUint8 {
|
||||
return errors.Errorf("%d is greater than maximum value for uint8", srcVal)
|
||||
}
|
||||
*v = uint8(srcVal)
|
||||
case *uint16:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for uint32", srcVal)
|
||||
} else if srcVal > math.MaxUint16 {
|
||||
return errors.Errorf("%d is greater than maximum value for uint16", srcVal)
|
||||
}
|
||||
*v = uint16(srcVal)
|
||||
case *uint32:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for uint32", srcVal)
|
||||
} else if srcVal > math.MaxUint32 {
|
||||
return errors.Errorf("%d is greater than maximum value for uint32", srcVal)
|
||||
}
|
||||
*v = uint32(srcVal)
|
||||
case *uint64:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for uint64", srcVal)
|
||||
}
|
||||
*v = uint64(srcVal)
|
||||
default:
|
||||
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||
el := v.Elem()
|
||||
switch el.Kind() {
|
||||
// if dst is a pointer to pointer, strip the pointer and try again
|
||||
case reflect.Ptr:
|
||||
if el.IsNil() {
|
||||
// allocate destination
|
||||
el.Set(reflect.New(el.Type().Elem()))
|
||||
}
|
||||
return int64AssignTo(srcVal, srcStatus, el.Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if el.OverflowInt(int64(srcVal)) {
|
||||
return errors.Errorf("cannot put %d into %T", srcVal, dst)
|
||||
}
|
||||
el.SetInt(int64(srcVal))
|
||||
return nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if srcVal < 0 {
|
||||
return errors.Errorf("%d is less than zero for %T", srcVal, dst)
|
||||
}
|
||||
if el.OverflowUint(uint64(srcVal)) {
|
||||
return errors.Errorf("cannot put %d into %T", srcVal, dst)
|
||||
}
|
||||
el.SetUint(uint64(srcVal))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.Errorf("cannot assign %v into %T", srcVal, dst)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// if dst is a pointer to pointer and srcStatus is not Present, nil it out
|
||||
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||
el := v.Elem()
|
||||
if el.Kind() == reflect.Ptr {
|
||||
el.Set(reflect.Zero(el.Type()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst)
|
||||
}
|
||||
|
||||
func float64AssignTo(srcVal float64, srcStatus Status, dst interface{}) error {
|
||||
if srcStatus == Present {
|
||||
switch v := dst.(type) {
|
||||
case *float32:
|
||||
*v = float32(srcVal)
|
||||
case *float64:
|
||||
*v = srcVal
|
||||
default:
|
||||
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||
el := v.Elem()
|
||||
switch el.Kind() {
|
||||
// if dst is a pointer to pointer, strip the pointer and try again
|
||||
case reflect.Ptr:
|
||||
if el.IsNil() {
|
||||
// allocate destination
|
||||
el.Set(reflect.New(el.Type().Elem()))
|
||||
}
|
||||
return float64AssignTo(srcVal, srcStatus, el.Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
i64 := int64(srcVal)
|
||||
if float64(i64) == srcVal {
|
||||
return int64AssignTo(i64, srcStatus, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.Errorf("cannot assign %v into %T", srcVal, dst)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// if dst is a pointer to pointer and srcStatus is not Present, nil it out
|
||||
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||
el := v.Elem()
|
||||
if el.Kind() == reflect.Ptr {
|
||||
el.Set(reflect.Zero(el.Type()))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot assign %v %v into %T", srcVal, srcStatus, dst)
|
||||
}
|
||||
|
||||
func NullAssignTo(dst interface{}) error {
|
||||
dstPtr := reflect.ValueOf(dst)
|
||||
|
||||
// AssignTo dst must always be a pointer
|
||||
if dstPtr.Kind() != reflect.Ptr {
|
||||
return errors.Errorf("cannot assign NULL to %T", dst)
|
||||
}
|
||||
|
||||
dstVal := dstPtr.Elem()
|
||||
|
||||
switch dstVal.Kind() {
|
||||
case reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
dstVal.Set(reflect.Zero(dstVal.Type()))
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot assign NULL to %T", dst)
|
||||
}
|
||||
|
||||
var kindTypes map[reflect.Kind]reflect.Type
|
||||
|
||||
// GetAssignToDstType attempts to convert dst to something AssignTo can assign
|
||||
// to. If dst is a pointer to pointer it allocates a value and returns the
|
||||
// dereferences pointer. If dst is a named type such as *Foo where Foo is type
|
||||
// Foo int16, it converts dst to *int16.
|
||||
//
|
||||
// GetAssignToDstType returns the converted dst and a bool representing if any
|
||||
// change was made.
|
||||
func GetAssignToDstType(dst interface{}) (interface{}, bool) {
|
||||
dstPtr := reflect.ValueOf(dst)
|
||||
|
||||
// AssignTo dst must always be a pointer
|
||||
if dstPtr.Kind() != reflect.Ptr {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
dstVal := dstPtr.Elem()
|
||||
|
||||
// if dst is a pointer to pointer, allocate space try again with the dereferenced pointer
|
||||
if dstVal.Kind() == reflect.Ptr {
|
||||
dstVal.Set(reflect.New(dstVal.Type().Elem()))
|
||||
return dstVal.Interface(), true
|
||||
}
|
||||
|
||||
// if dst is pointer to a base type that has been renamed
|
||||
if baseValType, ok := kindTypes[dstVal.Kind()]; ok {
|
||||
nextDst := dstPtr.Convert(reflect.PtrTo(baseValType))
|
||||
return nextDst.Interface(), dstPtr.Type() != nextDst.Type()
|
||||
}
|
||||
|
||||
if dstVal.Kind() == reflect.Slice {
|
||||
if baseElemType, ok := kindTypes[dstVal.Type().Elem().Kind()]; ok {
|
||||
baseSliceType := reflect.PtrTo(reflect.SliceOf(baseElemType))
|
||||
nextDst := dstPtr.Convert(baseSliceType)
|
||||
return nextDst.Interface(), dstPtr.Type() != nextDst.Type()
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func init() {
|
||||
kindTypes = map[reflect.Kind]reflect.Type{
|
||||
reflect.Bool: reflect.TypeOf(false),
|
||||
reflect.Float32: reflect.TypeOf(float32(0)),
|
||||
reflect.Float64: reflect.TypeOf(float64(0)),
|
||||
reflect.Int: reflect.TypeOf(int(0)),
|
||||
reflect.Int8: reflect.TypeOf(int8(0)),
|
||||
reflect.Int16: reflect.TypeOf(int16(0)),
|
||||
reflect.Int32: reflect.TypeOf(int32(0)),
|
||||
reflect.Int64: reflect.TypeOf(int64(0)),
|
||||
reflect.Uint: reflect.TypeOf(uint(0)),
|
||||
reflect.Uint8: reflect.TypeOf(uint8(0)),
|
||||
reflect.Uint16: reflect.TypeOf(uint16(0)),
|
||||
reflect.Uint32: reflect.TypeOf(uint32(0)),
|
||||
reflect.Uint64: reflect.TypeOf(uint64(0)),
|
||||
reflect.String: reflect.TypeOf(""),
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
func DatabaseSQLValue(ci *ConnInfo, src Value) (interface{}, error) {
|
||||
if valuer, ok := src.(driver.Valuer); ok {
|
||||
return valuer.Value()
|
||||
}
|
||||
|
||||
if textEncoder, ok := src.(TextEncoder); ok {
|
||||
buf, err := textEncoder.EncodeText(ci, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
if binaryEncoder, ok := src.(BinaryEncoder); ok {
|
||||
buf, err := binaryEncoder.EncodeBinary(ci, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("cannot convert to database/sql compatible value")
|
||||
}
|
||||
|
||||
func EncodeValueText(src TextEncoder) (interface{}, error) {
|
||||
buf, err := src.EncodeText(nil, make([]byte, 0, 32))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return string(buf), err
|
||||
}
|
||||
-210
@@ -1,210 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Date struct {
|
||||
Time time.Time
|
||||
Status Status
|
||||
InfinityModifier InfinityModifier
|
||||
}
|
||||
|
||||
const (
|
||||
negativeInfinityDayOffset = -2147483648
|
||||
infinityDayOffset = 2147483647
|
||||
)
|
||||
|
||||
func (dst *Date) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case time.Time:
|
||||
*dst = Date{Time: value, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingTimeType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Date", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Date) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
if dst.InfinityModifier != None {
|
||||
return dst.InfinityModifier
|
||||
}
|
||||
return dst.Time
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Date) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *time.Time:
|
||||
if src.InfinityModifier != None {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
*v = src.Time
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Date) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
sbuf := string(src)
|
||||
switch sbuf {
|
||||
case "infinity":
|
||||
*dst = Date{Status: Present, InfinityModifier: Infinity}
|
||||
case "-infinity":
|
||||
*dst = Date{Status: Present, InfinityModifier: -Infinity}
|
||||
default:
|
||||
t, err := time.ParseInLocation("2006-01-02", sbuf, time.UTC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Date{Time: t, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Date) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Date{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 4 {
|
||||
return errors.Errorf("invalid length for date: %v", len(src))
|
||||
}
|
||||
|
||||
dayOffset := int32(binary.BigEndian.Uint32(src))
|
||||
|
||||
switch dayOffset {
|
||||
case infinityDayOffset:
|
||||
*dst = Date{Status: Present, InfinityModifier: Infinity}
|
||||
case negativeInfinityDayOffset:
|
||||
*dst = Date{Status: Present, InfinityModifier: -Infinity}
|
||||
default:
|
||||
t := time.Date(2000, 1, int(1+dayOffset), 0, 0, 0, 0, time.UTC)
|
||||
*dst = Date{Time: t, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Date) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var s string
|
||||
|
||||
switch src.InfinityModifier {
|
||||
case None:
|
||||
s = src.Time.Format("2006-01-02")
|
||||
case Infinity:
|
||||
s = "infinity"
|
||||
case NegativeInfinity:
|
||||
s = "-infinity"
|
||||
}
|
||||
|
||||
return append(buf, s...), nil
|
||||
}
|
||||
|
||||
func (src *Date) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var daysSinceDateEpoch int32
|
||||
switch src.InfinityModifier {
|
||||
case None:
|
||||
tUnix := time.Date(src.Time.Year(), src.Time.Month(), src.Time.Day(), 0, 0, 0, 0, time.UTC).Unix()
|
||||
dateEpoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC).Unix()
|
||||
|
||||
secSinceDateEpoch := tUnix - dateEpoch
|
||||
daysSinceDateEpoch = int32(secSinceDateEpoch / 86400)
|
||||
case Infinity:
|
||||
daysSinceDateEpoch = infinityDayOffset
|
||||
case NegativeInfinity:
|
||||
daysSinceDateEpoch = negativeInfinityDayOffset
|
||||
}
|
||||
|
||||
return pgio.AppendInt32(buf, daysSinceDateEpoch), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Date) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Date{Status: Null}
|
||||
return 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)
|
||||
case time.Time:
|
||||
*dst = Date{Time: src, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Date) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
if src.InfinityModifier != None {
|
||||
return src.InfinityModifier.String(), nil
|
||||
}
|
||||
return src.Time, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,302 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type DateArray struct {
|
||||
Elements []Date
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *DateArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = DateArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to DateArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *DateArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *DateArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *DateArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = DateArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Date
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Date, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Date
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = DateArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *DateArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = DateArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = DateArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Date, 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 = DateArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *DateArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *DateArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("date"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "date")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *DateArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *DateArray) 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,143 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestDateArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "date[]", []interface{}{
|
||||
&pgtype.DateArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.DateArray{
|
||||
Elements: []pgtype.Date{
|
||||
{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.DateArray{Status: pgtype.Null},
|
||||
&pgtype.DateArray{
|
||||
Elements: []pgtype.Date{
|
||||
{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2016, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2017, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.DateArray{
|
||||
Elements: []pgtype.Date{
|
||||
{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2015, 2, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2015, 2, 3, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
{Time: time.Date(2015, 2, 4, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestDateArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.DateArray
|
||||
}{
|
||||
{
|
||||
source: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)},
|
||||
result: pgtype.DateArray{
|
||||
Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]time.Time)(nil)),
|
||||
result: pgtype.DateArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.DateArray
|
||||
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 TestDateArrayAssignTo(t *testing.T) {
|
||||
var timeSlice []time.Time
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.DateArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.DateArray{
|
||||
Elements: []pgtype.Date{{Time: time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &timeSlice,
|
||||
expected: []time.Time{time.Date(2015, 2, 1, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
{
|
||||
src: pgtype.DateArray{Status: pgtype.Null},
|
||||
dst: &timeSlice,
|
||||
expected: (([]time.Time)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.DateArray
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.DateArray{
|
||||
Elements: []pgtype.Date{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &timeSlice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestDateTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "date", []interface{}{
|
||||
&pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
&pgtype.Date{Status: pgtype.Null},
|
||||
&pgtype.Date{Status: pgtype.Present, InfinityModifier: pgtype.Infinity},
|
||||
&pgtype.Date{Status: pgtype.Present, InfinityModifier: -pgtype.Infinity},
|
||||
}, func(a, b interface{}) bool {
|
||||
at := a.(pgtype.Date)
|
||||
bt := b.(pgtype.Date)
|
||||
|
||||
return at.Time.Equal(bt.Time) && at.Status == bt.Status && at.InfinityModifier == bt.InfinityModifier
|
||||
})
|
||||
}
|
||||
|
||||
func TestDateSet(t *testing.T) {
|
||||
type _time time.Time
|
||||
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Date
|
||||
}{
|
||||
{source: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2000, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), result: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
{source: _time(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)), result: pgtype.Date{Time: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var d pgtype.Date
|
||||
err := d.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if d != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDateAssignTo(t *testing.T) {
|
||||
var tim time.Time
|
||||
var ptim *time.Time
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Date
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &tim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)},
|
||||
{src: pgtype.Date{Time: time.Time{}, Status: pgtype.Null}, dst: &ptim, expected: ((*time.Time)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Date
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Present}, dst: &ptim, expected: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local)},
|
||||
}
|
||||
|
||||
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.Date
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.Infinity, Status: pgtype.Present}, dst: &tim},
|
||||
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), InfinityModifier: pgtype.NegativeInfinity, Status: pgtype.Present}, dst: &tim},
|
||||
{src: pgtype.Date{Time: time.Date(2015, 1, 1, 0, 0, 0, 0, time.Local), Status: pgtype.Null}, dst: &tim},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Daterange struct {
|
||||
Lower Date
|
||||
Upper Date
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Daterange) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Daterange", src)
|
||||
}
|
||||
|
||||
func (dst *Daterange) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Daterange) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Daterange) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Daterange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utr, err := ParseUntypedTextRange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Daterange{Status: Present}
|
||||
|
||||
dst.LowerType = utr.LowerType
|
||||
dst.UpperType = utr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Daterange) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Daterange{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
ubr, err := ParseUntypedBinaryRange(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Daterange{Status: Present}
|
||||
|
||||
dst.LowerType = ubr.LowerType
|
||||
dst.UpperType = ubr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Daterange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
switch src.LowerType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, '(')
|
||||
case Inclusive:
|
||||
buf = append(buf, '[')
|
||||
case Empty:
|
||||
return append(buf, "empty"...), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown lower bound type %v", src.LowerType)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
buf, err = src.Lower.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
buf = append(buf, ',')
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
buf, err = src.Upper.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, ')')
|
||||
case Inclusive:
|
||||
buf = append(buf, ']')
|
||||
default:
|
||||
return nil, errors.Errorf("unknown upper bound type %v", src.UpperType)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Daterange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var rangeType byte
|
||||
switch src.LowerType {
|
||||
case Inclusive:
|
||||
rangeType |= lowerInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= lowerUnboundedMask
|
||||
case Exclusive:
|
||||
case Empty:
|
||||
return append(buf, emptyMask), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown LowerType: %v", src.LowerType)
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Inclusive:
|
||||
rangeType |= upperInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= upperUnboundedMask
|
||||
case Exclusive:
|
||||
default:
|
||||
return nil, errors.Errorf("unknown UpperType: %v", src.UpperType)
|
||||
}
|
||||
|
||||
buf = append(buf, rangeType)
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Lower.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Upper.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Daterange) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Daterange{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Daterange) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestDaterangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "daterange", []interface{}{
|
||||
&pgtype.Daterange{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present},
|
||||
&pgtype.Daterange{
|
||||
Lower: pgtype.Date{Time: time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
Upper: pgtype.Date{Time: time.Date(2028, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Daterange{
|
||||
Lower: pgtype.Date{Time: time.Date(1800, 12, 31, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
Upper: pgtype.Date{Time: time.Date(2200, 1, 1, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Daterange{Status: pgtype.Null},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(pgtype.Daterange)
|
||||
b := bb.(pgtype.Daterange)
|
||||
|
||||
return a.Status == b.Status &&
|
||||
a.Lower.Time.Equal(b.Lower.Time) &&
|
||||
a.Lower.Status == b.Lower.Status &&
|
||||
a.Lower.InfinityModifier == b.Lower.InfinityModifier &&
|
||||
a.Upper.Time.Equal(b.Upper.Time) &&
|
||||
a.Upper.Status == b.Upper.Status &&
|
||||
a.Upper.InfinityModifier == b.Upper.InfinityModifier
|
||||
})
|
||||
}
|
||||
|
||||
func TestDaterangeNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalizeEqFunc(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select daterange('2010-01-01', '2010-01-11', '(]')",
|
||||
Value: pgtype.Daterange{
|
||||
Lower: pgtype.Date{Time: time.Date(2010, 1, 2, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
Upper: pgtype.Date{Time: time.Date(2010, 1, 12, 0, 0, 0, 0, time.UTC), Status: pgtype.Present},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(pgtype.Daterange)
|
||||
b := bb.(pgtype.Daterange)
|
||||
|
||||
return a.Status == b.Status &&
|
||||
a.Lower.Time.Equal(b.Lower.Time) &&
|
||||
a.Lower.Status == b.Lower.Status &&
|
||||
a.Lower.InfinityModifier == b.Lower.InfinityModifier &&
|
||||
a.Upper.Time.Equal(b.Upper.Time) &&
|
||||
a.Upper.Status == b.Upper.Status &&
|
||||
a.Upper.InfinityModifier == b.Upper.InfinityModifier
|
||||
})
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type EnumArray struct {
|
||||
Elements []GenericText
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *EnumArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = EnumArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to EnumArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *EnumArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *EnumArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *EnumArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = EnumArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []GenericText
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]GenericText, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem GenericText
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = EnumArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *EnumArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *EnumArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *EnumArray) 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,151 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestEnumArrayTranscode(t *testing.T) {
|
||||
setupConn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, setupConn)
|
||||
|
||||
if _, err := setupConn.Exec(context.Background(), "drop type if exists color"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := setupConn.Exec(context.Background(), "create type color as enum ('red', 'green', 'blue')"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscode(t, "color[]", []interface{}{
|
||||
&pgtype.EnumArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{
|
||||
{String: "red", Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.EnumArray{Status: pgtype.Null},
|
||||
&pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{
|
||||
{String: "red", Status: pgtype.Present},
|
||||
{String: "green", Status: pgtype.Present},
|
||||
{String: "blue", Status: pgtype.Present},
|
||||
{String: "red", Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnumArrayArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.EnumArray
|
||||
}{
|
||||
{
|
||||
source: []string{"foo"},
|
||||
result: pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]string)(nil)),
|
||||
result: pgtype.EnumArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.EnumArray
|
||||
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 TestEnumArrayArrayAssignTo(t *testing.T) {
|
||||
var stringSlice []string
|
||||
type _stringSlice []string
|
||||
var namedStringSlice _stringSlice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.EnumArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{{String: "foo", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &stringSlice,
|
||||
expected: []string{"foo"},
|
||||
},
|
||||
{
|
||||
src: pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{{String: "bar", Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedStringSlice,
|
||||
expected: _stringSlice{"bar"},
|
||||
},
|
||||
{
|
||||
src: pgtype.EnumArray{Status: pgtype.Null},
|
||||
dst: &stringSlice,
|
||||
expected: (([]string)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.EnumArray
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.EnumArray{
|
||||
Elements: []pgtype.GenericText{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &stringSlice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var errUndefined = errors.New("cannot encode status undefined")
|
||||
|
||||
type UUID struct {
|
||||
UUID uuid.UUID
|
||||
Status pgtype.Status
|
||||
}
|
||||
|
||||
func (dst *UUID) Set(src interface{}) error {
|
||||
switch value := src.(type) {
|
||||
case uuid.UUID:
|
||||
*dst = UUID{UUID: value, Status: pgtype.Present}
|
||||
case [16]byte:
|
||||
*dst = UUID{UUID: uuid.UUID(value), Status: pgtype.Present}
|
||||
case []byte:
|
||||
if len(value) != 16 {
|
||||
return errors.Errorf("[]byte must be 16 bytes to convert to UUID: %d", len(value))
|
||||
}
|
||||
*dst = UUID{Status: pgtype.Present}
|
||||
copy(dst.UUID[:], value)
|
||||
case string:
|
||||
uuid, err := uuid.FromString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = UUID{UUID: uuid, Status: pgtype.Present}
|
||||
default:
|
||||
// If all else fails see if pgtype.UUID can handle it. If so, translate through that.
|
||||
pgUUID := &pgtype.UUID{}
|
||||
if err := pgUUID.Set(value); err != nil {
|
||||
return errors.Errorf("cannot convert %v to UUID", value)
|
||||
}
|
||||
|
||||
*dst = UUID{UUID: uuid.UUID(pgUUID.Bytes), Status: pgUUID.Status}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *UUID) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case pgtype.Present:
|
||||
return dst.UUID
|
||||
case pgtype.Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *UUID) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case pgtype.Present:
|
||||
switch v := dst.(type) {
|
||||
case *uuid.UUID:
|
||||
*v = src.UUID
|
||||
case *[16]byte:
|
||||
*v = [16]byte(src.UUID)
|
||||
return nil
|
||||
case *[]byte:
|
||||
*v = make([]byte, 16)
|
||||
copy(*v, src.UUID[:])
|
||||
return nil
|
||||
case *string:
|
||||
*v = src.UUID.String()
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := pgtype.GetAssignToDstType(v); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case pgtype.Null:
|
||||
return pgtype.NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot assign %v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *UUID) DecodeText(ci *pgtype.ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = UUID{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
u, err := uuid.FromString(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = UUID{UUID: u, Status: pgtype.Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *UUID) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = UUID{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 16 {
|
||||
return errors.Errorf("invalid length for UUID: %v", len(src))
|
||||
}
|
||||
|
||||
*dst = UUID{Status: pgtype.Present}
|
||||
copy(dst.UUID[:], src)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *UUID) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case pgtype.Null:
|
||||
return nil, nil
|
||||
case pgtype.Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.UUID.String()...), nil
|
||||
}
|
||||
|
||||
func (src *UUID) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case pgtype.Null:
|
||||
return nil, nil
|
||||
case pgtype.Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.UUID[:]...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *UUID) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = UUID{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
return dst.DecodeText(nil, src)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *UUID) Value() (driver.Value, error) {
|
||||
return pgtype.EncodeValueText(src)
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package uuid_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
satori "github.com/jackc/pgx/v4/pgtype/ext/satori-uuid"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestUUIDTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{
|
||||
&satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present},
|
||||
&satori.UUID{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestUUIDSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result satori.UUID
|
||||
}{
|
||||
{
|
||||
source: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: "00010203-0405-0607-0809-0a0b0c0d0e0f",
|
||||
result: satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r satori.UUID
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUIDAssignTo(t *testing.T) {
|
||||
{
|
||||
src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}
|
||||
var dst [16]byte
|
||||
expected := [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
err := src.AssignTo(&dst)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if dst != expected {
|
||||
t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}
|
||||
var dst []byte
|
||||
expected := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
err := src.AssignTo(&dst)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if bytes.Compare(dst, expected) != 0 {
|
||||
t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
src := satori.UUID{UUID: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Status: pgtype.Present}
|
||||
var dst string
|
||||
expected := "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
|
||||
err := src.AssignTo(&dst)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if dst != expected {
|
||||
t.Errorf("expected %v to assign %v, but result was %v", src, expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
package numeric
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"strconv"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
var errUndefined = errors.New("cannot encode status undefined")
|
||||
|
||||
type Numeric struct {
|
||||
Decimal decimal.Decimal
|
||||
Status pgtype.Status
|
||||
}
|
||||
|
||||
func (dst *Numeric) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Numeric{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case decimal.Decimal:
|
||||
*dst = Numeric{Decimal: value, Status: pgtype.Present}
|
||||
case float32:
|
||||
*dst = Numeric{Decimal: decimal.NewFromFloat(float64(value)), Status: pgtype.Present}
|
||||
case float64:
|
||||
*dst = Numeric{Decimal: decimal.NewFromFloat(value), Status: pgtype.Present}
|
||||
case int8:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case uint8:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case int16:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case uint16:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case int32:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case uint32:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case int64:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case uint64:
|
||||
// uint64 could be greater than int64 so convert to string then to decimal
|
||||
dec, err := decimal.NewFromString(strconv.FormatUint(value, 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
case int:
|
||||
*dst = Numeric{Decimal: decimal.New(int64(value), 0), Status: pgtype.Present}
|
||||
case uint:
|
||||
// uint could be greater than int64 so convert to string then to decimal
|
||||
dec, err := decimal.NewFromString(strconv.FormatUint(uint64(value), 10))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
case string:
|
||||
dec, err := decimal.NewFromString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
default:
|
||||
// If all else fails see if pgtype.Numeric can handle it. If so, translate through that.
|
||||
num := &pgtype.Numeric{}
|
||||
if err := num.Set(value); err != nil {
|
||||
return errors.Errorf("cannot convert %v to Numeric", value)
|
||||
}
|
||||
|
||||
buf, err := num.EncodeText(nil, nil)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to Numeric", value)
|
||||
}
|
||||
|
||||
dec, err := decimal.NewFromString(string(buf))
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to Numeric", value)
|
||||
}
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Numeric) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case pgtype.Present:
|
||||
return dst.Decimal
|
||||
case pgtype.Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Numeric) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case pgtype.Present:
|
||||
switch v := dst.(type) {
|
||||
case *decimal.Decimal:
|
||||
*v = src.Decimal
|
||||
case *float32:
|
||||
f, _ := src.Decimal.Float64()
|
||||
*v = float32(f)
|
||||
case *float64:
|
||||
f, _ := src.Decimal.Float64()
|
||||
*v = f
|
||||
case *int:
|
||||
if src.Decimal.Exponent() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseInt(src.Decimal.String(), 10, strconv.IntSize)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = int(n)
|
||||
case *int8:
|
||||
if src.Decimal.Exponent() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseInt(src.Decimal.String(), 10, 8)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = int8(n)
|
||||
case *int16:
|
||||
if src.Decimal.Exponent() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseInt(src.Decimal.String(), 10, 16)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = int16(n)
|
||||
case *int32:
|
||||
if src.Decimal.Exponent() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseInt(src.Decimal.String(), 10, 32)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = int32(n)
|
||||
case *int64:
|
||||
if src.Decimal.Exponent() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseInt(src.Decimal.String(), 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = int64(n)
|
||||
case *uint:
|
||||
if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseUint(src.Decimal.String(), 10, strconv.IntSize)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = uint(n)
|
||||
case *uint8:
|
||||
if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseUint(src.Decimal.String(), 10, 8)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = uint8(n)
|
||||
case *uint16:
|
||||
if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseUint(src.Decimal.String(), 10, 16)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = uint16(n)
|
||||
case *uint32:
|
||||
if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseUint(src.Decimal.String(), 10, 32)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = uint32(n)
|
||||
case *uint64:
|
||||
if src.Decimal.Exponent() < 0 || src.Decimal.Sign() < 0 {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
n, err := strconv.ParseUint(src.Decimal.String(), 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("cannot convert %v to %T", dst, *v)
|
||||
}
|
||||
*v = uint64(n)
|
||||
default:
|
||||
if nextDst, retry := pgtype.GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case pgtype.Null:
|
||||
return pgtype.NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Numeric) DecodeText(ci *pgtype.ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Numeric{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
dec, err := decimal.NewFromString(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Numeric) DecodeBinary(ci *pgtype.ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Numeric{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
// For now at least, implement this in terms of pgtype.Numeric
|
||||
|
||||
num := &pgtype.Numeric{}
|
||||
if err := num.DecodeBinary(ci, src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
buf, err := num.EncodeText(ci, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dec, err := decimal.NewFromString(string(buf))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Numeric{Decimal: dec, Status: pgtype.Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Numeric) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case pgtype.Null:
|
||||
return nil, nil
|
||||
case pgtype.Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.Decimal.String()...), nil
|
||||
}
|
||||
|
||||
func (src *Numeric) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case pgtype.Null:
|
||||
return nil, nil
|
||||
case pgtype.Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
// For now at least, implement this in terms of pgtype.Numeric
|
||||
num := &pgtype.Numeric{}
|
||||
if err := num.DecodeText(ci, []byte(src.Decimal.String())); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return num.EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Numeric) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Numeric{Status: pgtype.Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case float64:
|
||||
*dst = Numeric{Decimal: decimal.NewFromFloat(src), Status: pgtype.Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
return dst.DecodeText(nil, src)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Numeric) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case pgtype.Present:
|
||||
return src.Decimal.Value()
|
||||
case pgtype.Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,286 +0,0 @@
|
||||
package numeric_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
shopspring "github.com/jackc/pgx/v4/pgtype/ext/shopspring-numeric"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
func mustParseDecimal(t *testing.T, src string) decimal.Decimal {
|
||||
dec, err := decimal.NewFromString(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
func TestNumericNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalizeEqFunc(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select '0'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '10.00'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10.00"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1e-3'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '-1'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '10000'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "10000"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '3.14'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1.1'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.1"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '100010001'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '100010001.0001'::numeric",
|
||||
Value: &shopspring.Numeric{Decimal: mustParseDecimal(t, "100010001.0001"), Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981'::numeric",
|
||||
Value: &shopspring.Numeric{
|
||||
Decimal: mustParseDecimal(t, "4237234789234789289347892374324872138321894178943189043890124832108934.43219085471578891547854892438945012347981"),
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
{
|
||||
SQL: "select '0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234'::numeric",
|
||||
Value: &shopspring.Numeric{
|
||||
Decimal: mustParseDecimal(t, "0.8925092023480223478923478978978937897879595901237890234789243679037419057877231734823098432903527585734549035904590854890345905434578345789347890402348952348905890489054234237489234987723894789234"),
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
{
|
||||
SQL: "select '0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123'::numeric",
|
||||
Value: &shopspring.Numeric{
|
||||
Decimal: mustParseDecimal(t, "0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123"),
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(shopspring.Numeric)
|
||||
b := bb.(shopspring.Numeric)
|
||||
|
||||
return a.Status == b.Status && a.Decimal.Equal(b.Decimal)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNumericTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", []interface{}{
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "100000"), Status: pgtype.Present},
|
||||
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.1"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.01"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.001"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0001"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00001"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000001"), Status: pgtype.Present},
|
||||
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "3.14"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000123"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.000000123"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.0000000123"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000123"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001234567890123456789"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Decimal: mustParseDecimal(t, "4309132809320932980457137401234890237489238912983572189348951289375283573984571892758234678903467889512893489128589347891272139.8489235871258912789347891235879148795891238915678189467128957812395781238579189025891238901583915890128973578957912385798125789012378905238905471598123758923478294374327894237892234"), Status: pgtype.Present},
|
||||
&shopspring.Numeric{Status: pgtype.Null},
|
||||
}, func(aa, bb interface{}) bool {
|
||||
a := aa.(shopspring.Numeric)
|
||||
b := bb.(shopspring.Numeric)
|
||||
|
||||
return a.Status == b.Status && a.Decimal.Equal(b.Decimal)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestNumericTranscodeFuzz(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(0))
|
||||
max := &big.Int{}
|
||||
max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
|
||||
|
||||
values := make([]interface{}, 0, 2000)
|
||||
for i := 0; i < 500; i++ {
|
||||
num := fmt.Sprintf("%s.%s", (&big.Int{}).Rand(r, max).String(), (&big.Int{}).Rand(r, max).String())
|
||||
negNum := "-" + num
|
||||
values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, num), Status: pgtype.Present})
|
||||
values = append(values, &shopspring.Numeric{Decimal: mustParseDecimal(t, negNum), Status: pgtype.Present})
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "numeric", values,
|
||||
func(aa, bb interface{}) bool {
|
||||
a := aa.(shopspring.Numeric)
|
||||
b := bb.(shopspring.Numeric)
|
||||
|
||||
return a.Status == b.Status && a.Decimal.Equal(b.Decimal)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNumericSet(t *testing.T) {
|
||||
type _int8 int8
|
||||
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result *shopspring.Numeric
|
||||
}{
|
||||
{source: float32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: float64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: int16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: int32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: int64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: int8(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}},
|
||||
{source: int16(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}},
|
||||
{source: int32(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}},
|
||||
{source: int64(-1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}},
|
||||
{source: uint8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: uint16(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: uint32(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: uint64(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: "1", result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: _int8(1), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1"), Status: pgtype.Present}},
|
||||
{source: float64(1000), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1000"), Status: pgtype.Present}},
|
||||
{source: float64(1234), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1234"), Status: pgtype.Present}},
|
||||
{source: float64(12345678900), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "12345678900"), Status: pgtype.Present}},
|
||||
{source: float64(1.25), result: &shopspring.Numeric{Decimal: mustParseDecimal(t, "1.25"), Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
r := &shopspring.Numeric{}
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !(r.Status == tt.result.Status && r.Decimal.Equal(tt.result.Decimal)) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumericAssignTo(t *testing.T) {
|
||||
type _int8 int8
|
||||
|
||||
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 *shopspring.Numeric
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f32, expected: float32(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &f64, expected: float64(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f32, expected: float32(4.2)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "4.2"), Status: pgtype.Present}, dst: &f64, expected: float64(4.2)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42000"), Status: pgtype.Present}, dst: &i64, expected: int64(42000)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: &shopspring.Numeric{Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: &shopspring.Numeric{Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, dst)
|
||||
}
|
||||
}
|
||||
|
||||
pointerAllocTests := []struct {
|
||||
src *shopspring.Numeric
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf32, expected: float32(42)},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "42"), Status: pgtype.Present}, dst: &pf64, expected: float64(42)},
|
||||
}
|
||||
|
||||
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 *shopspring.Numeric
|
||||
dst interface{}
|
||||
}{
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "150"), Status: pgtype.Present}, dst: &i8},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "40000"), Status: pgtype.Present}, dst: &i16},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui8},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui16},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui32},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui64},
|
||||
{src: &shopspring.Numeric{Decimal: mustParseDecimal(t, "-1"), Status: pgtype.Present}, dst: &ui},
|
||||
{src: &shopspring.Numeric{Status: pgtype.Null}, dst: &i32},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Float4 struct {
|
||||
Float float32
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float4) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case float32:
|
||||
*dst = Float4{Float: value, Status: Present}
|
||||
case float64:
|
||||
*dst = Float4{Float: float32(value), Status: Present}
|
||||
case int8:
|
||||
*dst = Float4{Float: float32(value), Status: Present}
|
||||
case uint8:
|
||||
*dst = Float4{Float: float32(value), Status: Present}
|
||||
case int16:
|
||||
*dst = Float4{Float: float32(value), Status: Present}
|
||||
case uint16:
|
||||
*dst = Float4{Float: float32(value), Status: Present}
|
||||
case int32:
|
||||
f32 := float32(value)
|
||||
if int32(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case uint32:
|
||||
f32 := float32(value)
|
||||
if uint32(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case int64:
|
||||
f32 := float32(value)
|
||||
if int64(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case uint64:
|
||||
f32 := float32(value)
|
||||
if uint64(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case int:
|
||||
f32 := float32(value)
|
||||
if int(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case uint:
|
||||
f32 := float32(value)
|
||||
if uint(f32) == value {
|
||||
*dst = Float4{Float: f32, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float32", value)
|
||||
}
|
||||
case string:
|
||||
num, err := strconv.ParseFloat(value, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Float4{Float: float32(num), Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Float8", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float4) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Float
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Float4) AssignTo(dst interface{}) error {
|
||||
return float64AssignTo(float64(src.Float), src.Status, dst)
|
||||
}
|
||||
|
||||
func (dst *Float4) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.ParseFloat(string(src), 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Float4{Float: float32(n), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float4) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 4 {
|
||||
return errors.Errorf("invalid length for float4: %v", len(src))
|
||||
}
|
||||
|
||||
n := int32(binary.BigEndian.Uint32(src))
|
||||
|
||||
*dst = Float4{Float: math.Float32frombits(uint32(n)), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Float4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 32)...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Float4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint32(buf, math.Float32bits(src.Float))
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Float4) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case float64:
|
||||
*dst = Float4{Float: float32(src), Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Float4) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return float64(src.Float), nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Float4Array struct {
|
||||
Elements []Float4
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float4Array) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Float4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Float4Array", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float4Array) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Float4Array) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Float4Array) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Float4
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Float4, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Float4
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Float4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float4Array) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = Float4Array{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Float4, 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 = Float4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Float4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *Float4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("float4"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "float4")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *Float4Array) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Float4Array) 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,152 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestFloat4ArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "float4[]", []interface{}{
|
||||
&pgtype.Float4Array{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float4Array{Status: pgtype.Null},
|
||||
&pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Float: 2, Status: pgtype.Present},
|
||||
{Float: 3, Status: pgtype.Present},
|
||||
{Float: 4, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Float: 6, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Float: 2, Status: pgtype.Present},
|
||||
{Float: 3, Status: pgtype.Present},
|
||||
{Float: 4, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat4ArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Float4Array
|
||||
}{
|
||||
{
|
||||
source: []float32{1},
|
||||
result: pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{{Float: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]float32)(nil)),
|
||||
result: pgtype.Float4Array{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Float4Array
|
||||
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 TestFloat4ArrayAssignTo(t *testing.T) {
|
||||
var float32Slice []float32
|
||||
var namedFloat32Slice _float32Slice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Float4Array
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &float32Slice,
|
||||
expected: []float32{1.23},
|
||||
},
|
||||
{
|
||||
src: pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{{Float: 1.23, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedFloat32Slice,
|
||||
expected: _float32Slice{1.23},
|
||||
},
|
||||
{
|
||||
src: pgtype.Float4Array{Status: pgtype.Null},
|
||||
dst: &float32Slice,
|
||||
expected: (([]float32)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.Float4Array
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Float4Array{
|
||||
Elements: []pgtype.Float4{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &float32Slice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestFloat4Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "float4", []interface{}{
|
||||
&pgtype.Float4{Float: -1, Status: pgtype.Present},
|
||||
&pgtype.Float4{Float: 0, Status: pgtype.Present},
|
||||
&pgtype.Float4{Float: 0.00001, Status: pgtype.Present},
|
||||
&pgtype.Float4{Float: 1, Status: pgtype.Present},
|
||||
&pgtype.Float4{Float: 9999.99, Status: pgtype.Present},
|
||||
&pgtype.Float4{Float: 0, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat4Set(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Float4
|
||||
}{
|
||||
{source: float32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: float64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: int16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: int32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: int64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: int8(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}},
|
||||
{source: int16(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}},
|
||||
{source: int32(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}},
|
||||
{source: int64(-1), result: pgtype.Float4{Float: -1, Status: pgtype.Present}},
|
||||
{source: uint8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint16(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint32(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint64(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: "1", result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
{source: _int8(1), result: pgtype.Float4{Float: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Float4
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat4AssignTo(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.Float4
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Float4
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)},
|
||||
{src: pgtype.Float4{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)},
|
||||
}
|
||||
|
||||
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.Float4
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Float4{Float: 150, Status: pgtype.Present}, dst: &i8},
|
||||
{src: pgtype.Float4{Float: 40000, Status: pgtype.Present}, dst: &i16},
|
||||
{src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui8},
|
||||
{src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui16},
|
||||
{src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui32},
|
||||
{src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui64},
|
||||
{src: pgtype.Float4{Float: -1, Status: pgtype.Present}, dst: &ui},
|
||||
{src: pgtype.Float4{Float: 0, Status: pgtype.Null}, dst: &i32},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Float8 struct {
|
||||
Float float64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float8) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case float32:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case float64:
|
||||
*dst = Float8{Float: value, Status: Present}
|
||||
case int8:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case uint8:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case int16:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case uint16:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case int32:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case uint32:
|
||||
*dst = Float8{Float: float64(value), Status: Present}
|
||||
case int64:
|
||||
f64 := float64(value)
|
||||
if int64(f64) == value {
|
||||
*dst = Float8{Float: f64, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float64", value)
|
||||
}
|
||||
case uint64:
|
||||
f64 := float64(value)
|
||||
if uint64(f64) == value {
|
||||
*dst = Float8{Float: f64, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float64", value)
|
||||
}
|
||||
case int:
|
||||
f64 := float64(value)
|
||||
if int(f64) == value {
|
||||
*dst = Float8{Float: f64, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float64", value)
|
||||
}
|
||||
case uint:
|
||||
f64 := float64(value)
|
||||
if uint(f64) == value {
|
||||
*dst = Float8{Float: f64, Status: Present}
|
||||
} else {
|
||||
return errors.Errorf("%v cannot be exactly represented as float64", value)
|
||||
}
|
||||
case string:
|
||||
num, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Float8{Float: float64(num), Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Float8", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float8) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Float
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Float8) AssignTo(dst interface{}) error {
|
||||
return float64AssignTo(src.Float, src.Status, dst)
|
||||
}
|
||||
|
||||
func (dst *Float8) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.ParseFloat(string(src), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Float8{Float: n, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float8) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 8 {
|
||||
return errors.Errorf("invalid length for float4: %v", len(src))
|
||||
}
|
||||
|
||||
n := int64(binary.BigEndian.Uint64(src))
|
||||
|
||||
*dst = Float8{Float: math.Float64frombits(uint64(n)), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Float8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, strconv.FormatFloat(float64(src.Float), 'f', -1, 64)...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Float8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.Float))
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Float8) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Float8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case float64:
|
||||
*dst = Float8{Float: src, Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Float8) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return src.Float, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Float8Array struct {
|
||||
Elements []Float8
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Float8Array) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Float8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Float8Array", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float8Array) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Float8Array) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Float8Array) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Float8
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Float8, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Float8
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Float8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Float8Array) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Float8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = Float8Array{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Float8, 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 = Float8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Float8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *Float8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("float8"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "float8")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *Float8Array) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Float8Array) 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,152 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestFloat8ArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "float8[]", []interface{}{
|
||||
&pgtype.Float8Array{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float8Array{Status: pgtype.Null},
|
||||
&pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Float: 2, Status: pgtype.Present},
|
||||
{Float: 3, Status: pgtype.Present},
|
||||
{Float: 4, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Float: 6, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{
|
||||
{Float: 1, Status: pgtype.Present},
|
||||
{Float: 2, Status: pgtype.Present},
|
||||
{Float: 3, Status: pgtype.Present},
|
||||
{Float: 4, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat8ArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Float8Array
|
||||
}{
|
||||
{
|
||||
source: []float64{1},
|
||||
result: pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{{Float: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]float64)(nil)),
|
||||
result: pgtype.Float8Array{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Float8Array
|
||||
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 TestFloat8ArrayAssignTo(t *testing.T) {
|
||||
var float64Slice []float64
|
||||
var namedFloat64Slice _float64Slice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Float8Array
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &float64Slice,
|
||||
expected: []float64{1.23},
|
||||
},
|
||||
{
|
||||
src: pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{{Float: 1.23, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedFloat64Slice,
|
||||
expected: _float64Slice{1.23},
|
||||
},
|
||||
{
|
||||
src: pgtype.Float8Array{Status: pgtype.Null},
|
||||
dst: &float64Slice,
|
||||
expected: (([]float64)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.Float8Array
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Float8Array{
|
||||
Elements: []pgtype.Float8{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &float64Slice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestFloat8Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "float8", []interface{}{
|
||||
&pgtype.Float8{Float: -1, Status: pgtype.Present},
|
||||
&pgtype.Float8{Float: 0, Status: pgtype.Present},
|
||||
&pgtype.Float8{Float: 0.00001, Status: pgtype.Present},
|
||||
&pgtype.Float8{Float: 1, Status: pgtype.Present},
|
||||
&pgtype.Float8{Float: 9999.99, Status: pgtype.Present},
|
||||
&pgtype.Float8{Float: 0, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestFloat8Set(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Float8
|
||||
}{
|
||||
{source: float32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: float64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: int16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: int32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: int64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: int8(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}},
|
||||
{source: int16(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}},
|
||||
{source: int32(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}},
|
||||
{source: int64(-1), result: pgtype.Float8{Float: -1, Status: pgtype.Present}},
|
||||
{source: uint8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint16(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint32(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: uint64(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: "1", result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
{source: _int8(1), result: pgtype.Float8{Float: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Float8
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat8AssignTo(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.Float8
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f32, expected: float32(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &f64, expected: float64(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Float8
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf32, expected: float32(42)},
|
||||
{src: pgtype.Float8{Float: 42, Status: pgtype.Present}, dst: &pf64, expected: float64(42)},
|
||||
}
|
||||
|
||||
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.Float8
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Float8{Float: 150, Status: pgtype.Present}, dst: &i8},
|
||||
{src: pgtype.Float8{Float: 40000, Status: pgtype.Present}, dst: &i16},
|
||||
{src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui8},
|
||||
{src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui16},
|
||||
{src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui32},
|
||||
{src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui64},
|
||||
{src: pgtype.Float8{Float: -1, Status: pgtype.Present}, dst: &ui},
|
||||
{src: pgtype.Float8{Float: 0, Status: pgtype.Null}, dst: &i32},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// GenericBinary is a placeholder for binary format values that no other type exists
|
||||
// to handle.
|
||||
type GenericBinary Bytea
|
||||
|
||||
func (dst *GenericBinary) Set(src interface{}) error {
|
||||
return (*Bytea)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *GenericBinary) Get() interface{} {
|
||||
return (*Bytea)(dst).Get()
|
||||
}
|
||||
|
||||
func (src *GenericBinary) AssignTo(dst interface{}) error {
|
||||
return (*Bytea)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *GenericBinary) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return (*Bytea)(dst).DecodeBinary(ci, src)
|
||||
}
|
||||
|
||||
func (src *GenericBinary) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Bytea)(src).EncodeBinary(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *GenericBinary) Scan(src interface{}) error {
|
||||
return (*Bytea)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *GenericBinary) Value() (driver.Value, error) {
|
||||
return (*Bytea)(src).Value()
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// GenericText is a placeholder for text format values that no other type exists
|
||||
// to handle.
|
||||
type GenericText Text
|
||||
|
||||
func (dst *GenericText) Set(src interface{}) error {
|
||||
return (*Text)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *GenericText) Get() interface{} {
|
||||
return (*Text)(dst).Get()
|
||||
}
|
||||
|
||||
func (src *GenericText) AssignTo(dst interface{}) error {
|
||||
return (*Text)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *GenericText) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*Text)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (src *GenericText) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*Text)(src).EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *GenericText) Scan(src interface{}) error {
|
||||
return (*Text)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *GenericText) Value() (driver.Value, error) {
|
||||
return (*Text)(src).Value()
|
||||
}
|
||||
@@ -1,435 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
)
|
||||
|
||||
// Hstore represents an hstore column that can be null or have null values
|
||||
// associated with its keys.
|
||||
type Hstore struct {
|
||||
Map map[string]Text
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Hstore) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Hstore{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case map[string]string:
|
||||
m := make(map[string]Text, len(value))
|
||||
for k, v := range value {
|
||||
m[k] = Text{String: v, Status: Present}
|
||||
}
|
||||
*dst = Hstore{Map: m, Status: Present}
|
||||
default:
|
||||
return errors.Errorf("cannot convert %v to Hstore", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Hstore) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Map
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Hstore) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *map[string]string:
|
||||
*v = make(map[string]string, len(src.Map))
|
||||
for k, val := range src.Map {
|
||||
if val.Status != Present {
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
(*v)[k] = val.String
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Hstore) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Hstore{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
keys, values, err := parseHstore(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := make(map[string]Text, len(keys))
|
||||
for i := range keys {
|
||||
m[keys[i]] = values[i]
|
||||
}
|
||||
|
||||
*dst = Hstore{Map: m, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Hstore) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Hstore{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
rp := 0
|
||||
|
||||
if len(src[rp:]) < 4 {
|
||||
return errors.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
pairCount := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
m := make(map[string]Text, pairCount)
|
||||
|
||||
for i := 0; i < pairCount; i++ {
|
||||
if len(src[rp:]) < 4 {
|
||||
return errors.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
keyLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
if len(src[rp:]) < keyLen {
|
||||
return errors.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
key := string(src[rp : rp+keyLen])
|
||||
rp += keyLen
|
||||
|
||||
if len(src[rp:]) < 4 {
|
||||
return errors.Errorf("hstore incomplete %v", src)
|
||||
}
|
||||
valueLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
|
||||
rp += 4
|
||||
|
||||
var valueBuf []byte
|
||||
if valueLen >= 0 {
|
||||
valueBuf = src[rp : rp+valueLen]
|
||||
}
|
||||
rp += valueLen
|
||||
|
||||
var value Text
|
||||
err := value.DecodeBinary(ci, valueBuf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m[key] = value
|
||||
}
|
||||
|
||||
*dst = Hstore{Map: m, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Hstore) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
firstPair := true
|
||||
|
||||
for k, v := range src.Map {
|
||||
if firstPair {
|
||||
firstPair = false
|
||||
} else {
|
||||
buf = append(buf, ',')
|
||||
}
|
||||
|
||||
buf = append(buf, quoteHstoreElementIfNeeded(k)...)
|
||||
buf = append(buf, "=>"...)
|
||||
|
||||
elemBuf, err := v.EncodeText(ci, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if elemBuf == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
} else {
|
||||
buf = append(buf, quoteHstoreElementIfNeeded(string(elemBuf))...)
|
||||
}
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Hstore) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt32(buf, int32(len(src.Map)))
|
||||
|
||||
var err error
|
||||
for k, v := range src.Map {
|
||||
buf = pgio.AppendInt32(buf, int32(len(k)))
|
||||
buf = append(buf, k...)
|
||||
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
elemBuf, err := v.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elemBuf != nil {
|
||||
buf = elemBuf
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
}
|
||||
|
||||
return buf, err
|
||||
}
|
||||
|
||||
var quoteHstoreReplacer = strings.NewReplacer(`\`, `\\`, `"`, `\"`)
|
||||
|
||||
func quoteHstoreElement(src string) string {
|
||||
return `"` + quoteArrayReplacer.Replace(src) + `"`
|
||||
}
|
||||
|
||||
func quoteHstoreElementIfNeeded(src string) string {
|
||||
if src == "" || (len(src) == 4 && strings.ToLower(src) == "null") || strings.ContainsAny(src, ` {},"\=>`) {
|
||||
return quoteArrayElement(src)
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
const (
|
||||
hsPre = iota
|
||||
hsKey
|
||||
hsSep
|
||||
hsVal
|
||||
hsNul
|
||||
hsNext
|
||||
)
|
||||
|
||||
type hstoreParser struct {
|
||||
str string
|
||||
pos int
|
||||
}
|
||||
|
||||
func newHSP(in string) *hstoreParser {
|
||||
return &hstoreParser{
|
||||
pos: 0,
|
||||
str: in,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *hstoreParser) Consume() (r rune, end bool) {
|
||||
if p.pos >= len(p.str) {
|
||||
end = true
|
||||
return
|
||||
}
|
||||
r, w := utf8.DecodeRuneInString(p.str[p.pos:])
|
||||
p.pos += w
|
||||
return
|
||||
}
|
||||
|
||||
func (p *hstoreParser) Peek() (r rune, end bool) {
|
||||
if p.pos >= len(p.str) {
|
||||
end = true
|
||||
return
|
||||
}
|
||||
r, _ = utf8.DecodeRuneInString(p.str[p.pos:])
|
||||
return
|
||||
}
|
||||
|
||||
// parseHstore parses the string representation of an hstore column (the same
|
||||
// you would get from an ordinary SELECT) into two slices of keys and values. it
|
||||
// is used internally in the default parsing of hstores.
|
||||
func parseHstore(s string) (k []string, v []Text, err error) {
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
keys := []string{}
|
||||
values := []Text{}
|
||||
p := newHSP(s)
|
||||
|
||||
r, end := p.Consume()
|
||||
state := hsPre
|
||||
|
||||
for !end {
|
||||
switch state {
|
||||
case hsPre:
|
||||
if r == '"' {
|
||||
state = hsKey
|
||||
} else {
|
||||
err = errors.New("String does not begin with \"")
|
||||
}
|
||||
case hsKey:
|
||||
switch r {
|
||||
case '"': //End of the key
|
||||
if buf.Len() == 0 {
|
||||
err = errors.New("Empty Key is invalid")
|
||||
} else {
|
||||
keys = append(keys, buf.String())
|
||||
buf = bytes.Buffer{}
|
||||
state = hsSep
|
||||
}
|
||||
case '\\': //Potential escaped character
|
||||
n, end := p.Consume()
|
||||
switch {
|
||||
case end:
|
||||
err = errors.New("Found EOS in key, expecting character or \"")
|
||||
case n == '"', n == '\\':
|
||||
buf.WriteRune(n)
|
||||
default:
|
||||
buf.WriteRune(r)
|
||||
buf.WriteRune(n)
|
||||
}
|
||||
default: //Any other character
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
case hsSep:
|
||||
if r == '=' {
|
||||
r, end = p.Consume()
|
||||
switch {
|
||||
case end:
|
||||
err = errors.New("Found EOS after '=', expecting '>'")
|
||||
case r == '>':
|
||||
r, end = p.Consume()
|
||||
switch {
|
||||
case end:
|
||||
err = errors.New("Found EOS after '=>', expecting '\"' or 'NULL'")
|
||||
case r == '"':
|
||||
state = hsVal
|
||||
case r == 'N':
|
||||
state = hsNul
|
||||
default:
|
||||
err = errors.Errorf("Invalid character '%c' after '=>', expecting '\"' or 'NULL'", r)
|
||||
}
|
||||
default:
|
||||
err = errors.Errorf("Invalid character after '=', expecting '>'")
|
||||
}
|
||||
} else {
|
||||
err = errors.Errorf("Invalid character '%c' after value, expecting '='", r)
|
||||
}
|
||||
case hsVal:
|
||||
switch r {
|
||||
case '"': //End of the value
|
||||
values = append(values, Text{String: buf.String(), Status: Present})
|
||||
buf = bytes.Buffer{}
|
||||
state = hsNext
|
||||
case '\\': //Potential escaped character
|
||||
n, end := p.Consume()
|
||||
switch {
|
||||
case end:
|
||||
err = errors.New("Found EOS in key, expecting character or \"")
|
||||
case n == '"', n == '\\':
|
||||
buf.WriteRune(n)
|
||||
default:
|
||||
buf.WriteRune(r)
|
||||
buf.WriteRune(n)
|
||||
}
|
||||
default: //Any other character
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
case hsNul:
|
||||
nulBuf := make([]rune, 3)
|
||||
nulBuf[0] = r
|
||||
for i := 1; i < 3; i++ {
|
||||
r, end = p.Consume()
|
||||
if end {
|
||||
err = errors.New("Found EOS in NULL value")
|
||||
return
|
||||
}
|
||||
nulBuf[i] = r
|
||||
}
|
||||
if nulBuf[0] == 'U' && nulBuf[1] == 'L' && nulBuf[2] == 'L' {
|
||||
values = append(values, Text{Status: Null})
|
||||
state = hsNext
|
||||
} else {
|
||||
err = errors.Errorf("Invalid NULL value: 'N%s'", string(nulBuf))
|
||||
}
|
||||
case hsNext:
|
||||
if r == ',' {
|
||||
r, end = p.Consume()
|
||||
switch {
|
||||
case end:
|
||||
err = errors.New("Found EOS after ',', expcting space")
|
||||
case (unicode.IsSpace(r)):
|
||||
r, end = p.Consume()
|
||||
state = hsKey
|
||||
default:
|
||||
err = errors.Errorf("Invalid character '%c' after ', ', expecting \"", r)
|
||||
}
|
||||
} else {
|
||||
err = errors.Errorf("Invalid character '%c' after value, expecting ','", r)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r, end = p.Consume()
|
||||
}
|
||||
if state != hsNext {
|
||||
err = errors.New("Improperly formatted hstore")
|
||||
return
|
||||
}
|
||||
k = keys
|
||||
v = values
|
||||
return
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Hstore) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Hstore{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Hstore) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,301 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type HstoreArray struct {
|
||||
Elements []Hstore
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *HstoreArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = HstoreArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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 = HstoreArray{
|
||||
Elements: elements,
|
||||
Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
|
||||
Status: Present,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to HstoreArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *HstoreArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *HstoreArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *HstoreArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = HstoreArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Hstore
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Hstore, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Hstore
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = HstoreArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *HstoreArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = HstoreArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = HstoreArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Hstore, 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 = HstoreArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *HstoreArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *HstoreArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("hstore"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "hstore")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *HstoreArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *HstoreArray) 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,199 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4"
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestHstoreArrayTranscode(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
var hstoreOID pgtype.OID
|
||||
err := conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='hstore';").Scan(&hstoreOID)
|
||||
if err != nil {
|
||||
t.Fatalf("did not find hstore OID, %v", err)
|
||||
}
|
||||
conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.Hstore{}, Name: "hstore", OID: hstoreOID})
|
||||
|
||||
var hstoreArrayOID pgtype.OID
|
||||
err = conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='_hstore';").Scan(&hstoreArrayOID)
|
||||
if err != nil {
|
||||
t.Fatalf("did not find _hstore OID, %v", err)
|
||||
}
|
||||
conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.HstoreArray{}, Name: "_hstore", OID: hstoreArrayOID})
|
||||
|
||||
text := func(s string) pgtype.Text {
|
||||
return pgtype.Text{String: s, Status: pgtype.Present}
|
||||
}
|
||||
|
||||
values := []pgtype.Hstore{
|
||||
{Map: map[string]pgtype.Text{}, Status: pgtype.Present},
|
||||
{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present},
|
||||
{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present},
|
||||
{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present},
|
||||
{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
}
|
||||
|
||||
specialStrings := []string{
|
||||
`"`,
|
||||
`'`,
|
||||
`\`,
|
||||
`\\`,
|
||||
`=>`,
|
||||
` `,
|
||||
`\ / / \\ => " ' " '`,
|
||||
}
|
||||
for _, s := range specialStrings {
|
||||
// Special key values
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key
|
||||
|
||||
// Special value values
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end
|
||||
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key
|
||||
}
|
||||
|
||||
src := &pgtype.HstoreArray{
|
||||
Elements: values,
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(values)), LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
}
|
||||
|
||||
ps, err := conn.Prepare(context.Background(), "test", "select $1::hstore[]")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
formats := []struct {
|
||||
name string
|
||||
formatCode int16
|
||||
}{
|
||||
{name: "TextFormat", formatCode: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
|
||||
}
|
||||
|
||||
for _, fc := range formats {
|
||||
ps.FieldDescriptions[0].FormatCode = fc.formatCode
|
||||
vEncoder := testutil.ForceEncoder(src, fc.formatCode)
|
||||
if vEncoder == nil {
|
||||
t.Logf("%#v does not implement %v", src, fc.name)
|
||||
continue
|
||||
}
|
||||
|
||||
var result pgtype.HstoreArray
|
||||
err := conn.QueryRow(context.Background(), "test", vEncoder).Scan(&result)
|
||||
if err != nil {
|
||||
t.Errorf("%v: %v", fc.name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if result.Status != src.Status {
|
||||
t.Errorf("%v: expected Status %v, got %v", fc.formatCode, src.Status, result.Status)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(result.Elements) != len(src.Elements) {
|
||||
t.Errorf("%v: expected %v elements, got %v", fc.formatCode, len(src.Elements), len(result.Elements))
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range result.Elements {
|
||||
a := src.Elements[i]
|
||||
b := result.Elements[i]
|
||||
|
||||
if a.Status != b.Status {
|
||||
t.Errorf("%v element idx %d: expected status %v, got %v", fc.formatCode, i, a.Status, b.Status)
|
||||
}
|
||||
|
||||
if len(a.Map) != len(b.Map) {
|
||||
t.Errorf("%v element idx %d: expected %v pairs, got %v", fc.formatCode, i, len(a.Map), len(b.Map))
|
||||
}
|
||||
|
||||
for k := range a.Map {
|
||||
if a.Map[k] != b.Map[k] {
|
||||
t.Errorf("%v element idx %d: expected key %v to be %v, got %v", fc.formatCode, i, k, a.Map[k], b.Map[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHstoreArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
src []map[string]string
|
||||
result pgtype.HstoreArray
|
||||
}{
|
||||
{
|
||||
src: []map[string]string{{"foo": "bar"}},
|
||||
result: pgtype.HstoreArray{
|
||||
Elements: []pgtype.Hstore{
|
||||
{
|
||||
Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var dst pgtype.HstoreArray
|
||||
err := dst.Set(tt.src)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(dst, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHstoreArrayAssignTo(t *testing.T) {
|
||||
var m []map[string]string
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.HstoreArray
|
||||
dst *[]map[string]string
|
||||
expected []map[string]string
|
||||
}{
|
||||
{
|
||||
src: pgtype.HstoreArray{
|
||||
Elements: []pgtype.Hstore{
|
||||
{
|
||||
Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &m,
|
||||
expected: []map[string]string{{"foo": "bar"}}},
|
||||
{src: pgtype.HstoreArray{Status: pgtype.Null}, dst: &m, expected: (([]map[string]string)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range simpleTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*tt.dst, tt.expected) {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestHstoreTranscode(t *testing.T) {
|
||||
text := func(s string) pgtype.Text {
|
||||
return pgtype.Text{String: s, Status: pgtype.Present}
|
||||
}
|
||||
|
||||
values := []interface{}{
|
||||
&pgtype.Hstore{Map: map[string]pgtype.Text{}, Status: pgtype.Present},
|
||||
&pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present},
|
||||
&pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present},
|
||||
&pgtype.Hstore{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present},
|
||||
&pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present},
|
||||
&pgtype.Hstore{Status: pgtype.Null},
|
||||
}
|
||||
|
||||
specialStrings := []string{
|
||||
`"`,
|
||||
`'`,
|
||||
`\`,
|
||||
`\\`,
|
||||
`=>`,
|
||||
` `,
|
||||
`\ / / \\ => " ' " '`,
|
||||
}
|
||||
for _, s := range specialStrings {
|
||||
// Special key values
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key
|
||||
|
||||
// Special value values
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end
|
||||
values = append(values, &pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscodeEqFunc(t, "hstore", values, func(ai, bi interface{}) bool {
|
||||
a := ai.(pgtype.Hstore)
|
||||
b := bi.(pgtype.Hstore)
|
||||
|
||||
if len(a.Map) != len(b.Map) || a.Status != b.Status {
|
||||
return false
|
||||
}
|
||||
|
||||
for k := range a.Map {
|
||||
if a.Map[k] != b.Map[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func TestHstoreSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
src map[string]string
|
||||
result pgtype.Hstore
|
||||
}{
|
||||
{src: map[string]string{"foo": "bar"}, result: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var dst pgtype.Hstore
|
||||
err := dst.Set(tt.src)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(dst, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHstoreAssignTo(t *testing.T) {
|
||||
var m map[string]string
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Hstore
|
||||
dst *map[string]string
|
||||
expected map[string]string
|
||||
}{
|
||||
{src: pgtype.Hstore{Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}}, Status: pgtype.Present}, dst: &m, expected: map[string]string{"foo": "bar"}},
|
||||
{src: pgtype.Hstore{Status: pgtype.Null}, dst: &m, expected: ((map[string]string)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range simpleTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*tt.dst, tt.expected) {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
-216
@@ -1,216 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"net"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// Network address family is dependent on server socket.h value for AF_INET.
|
||||
// In practice, all platforms appear to have the same value. See
|
||||
// src/include/utils/inet.h for more information.
|
||||
const (
|
||||
defaultAFInet = 2
|
||||
defaultAFInet6 = 3
|
||||
)
|
||||
|
||||
// Inet represents both inet and cidr PostgreSQL types.
|
||||
type Inet struct {
|
||||
IPNet *net.IPNet
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Inet) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case net.IPNet:
|
||||
*dst = Inet{IPNet: &value, Status: Present}
|
||||
case *net.IPNet:
|
||||
*dst = Inet{IPNet: value, Status: Present}
|
||||
case net.IP:
|
||||
bitCount := len(value) * 8
|
||||
mask := net.CIDRMask(bitCount, bitCount)
|
||||
*dst = Inet{IPNet: &net.IPNet{Mask: mask, IP: value}, Status: Present}
|
||||
case string:
|
||||
_, ipnet, err := net.ParseCIDR(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Inet{IPNet: ipnet, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingPtrType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Inet", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Inet) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.IPNet
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Inet) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *net.IPNet:
|
||||
*v = net.IPNet{
|
||||
IP: make(net.IP, len(src.IPNet.IP)),
|
||||
Mask: make(net.IPMask, len(src.IPNet.Mask)),
|
||||
}
|
||||
copy(v.IP, src.IPNet.IP)
|
||||
copy(v.Mask, src.IPNet.Mask)
|
||||
return nil
|
||||
case *net.IP:
|
||||
if oneCount, bitCount := src.IPNet.Mask.Size(); oneCount != bitCount {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
*v = make(net.IP, len(src.IPNet.IP))
|
||||
copy(*v, src.IPNet.IP)
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Inet) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipnet *net.IPNet
|
||||
var err error
|
||||
|
||||
if ip := net.ParseIP(string(src)); ip != nil {
|
||||
ipv4 := ip.To4()
|
||||
if ipv4 != nil {
|
||||
ip = ipv4
|
||||
}
|
||||
bitCount := len(ip) * 8
|
||||
mask := net.CIDRMask(bitCount, bitCount)
|
||||
ipnet = &net.IPNet{Mask: mask, IP: ip}
|
||||
} else {
|
||||
_, ipnet, err = net.ParseCIDR(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Inet{IPNet: ipnet, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 8 && len(src) != 20 {
|
||||
return errors.Errorf("Received an invalid size for a inet: %d", len(src))
|
||||
}
|
||||
|
||||
// ignore family
|
||||
bits := src[1]
|
||||
// ignore is_cidr
|
||||
addressLength := src[3]
|
||||
|
||||
var ipnet net.IPNet
|
||||
ipnet.IP = make(net.IP, int(addressLength))
|
||||
copy(ipnet.IP, src[4:])
|
||||
ipnet.Mask = net.CIDRMask(int(bits), int(addressLength)*8)
|
||||
|
||||
*dst = Inet{IPNet: &ipnet, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Inet) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.IPNet.String()...), nil
|
||||
}
|
||||
|
||||
// EncodeBinary encodes src into w.
|
||||
func (src *Inet) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var family byte
|
||||
switch len(src.IPNet.IP) {
|
||||
case net.IPv4len:
|
||||
family = defaultAFInet
|
||||
case net.IPv6len:
|
||||
family = defaultAFInet6
|
||||
default:
|
||||
return nil, errors.Errorf("Unexpected IP length: %v", len(src.IPNet.IP))
|
||||
}
|
||||
|
||||
buf = append(buf, family)
|
||||
|
||||
ones, _ := src.IPNet.Mask.Size()
|
||||
buf = append(buf, byte(ones))
|
||||
|
||||
// is_cidr is ignored on server
|
||||
buf = append(buf, 0)
|
||||
|
||||
buf = append(buf, byte(len(src.IPNet.IP)))
|
||||
|
||||
return append(buf, src.IPNet.IP...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Inet) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Inet{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Inet) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"net"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type InetArray struct {
|
||||
Elements []Inet
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *InetArray) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = InetArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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 = 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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to InetArray", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *InetArray) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *InetArray) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *InetArray) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = InetArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Inet
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Inet, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Inet
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = InetArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *InetArray) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = InetArray{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = InetArray{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Inet, 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 = InetArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *InetArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *InetArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("inet"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "inet")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *InetArray) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *InetArray) 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,165 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInetArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "inet[]", []interface{}{
|
||||
&pgtype.InetArray{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.InetArray{Status: pgtype.Null},
|
||||
&pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{
|
||||
{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{
|
||||
{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "192.168.0.1/32"), Status: pgtype.Present},
|
||||
{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInetArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.InetArray
|
||||
}{
|
||||
{
|
||||
source: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")},
|
||||
result: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]*net.IPNet)(nil)),
|
||||
result: pgtype.InetArray{Status: pgtype.Null},
|
||||
},
|
||||
{
|
||||
source: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
result: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]net.IP)(nil)),
|
||||
result: pgtype.InetArray{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.InetArray
|
||||
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 TestInetArrayAssignTo(t *testing.T) {
|
||||
var ipnetSlice []*net.IPNet
|
||||
var ipSlice []net.IP
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.InetArray
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipnetSlice,
|
||||
expected: []*net.IPNet{mustParseCIDR(t, "127.0.0.1/32")},
|
||||
},
|
||||
{
|
||||
src: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipnetSlice,
|
||||
expected: []*net.IPNet{nil},
|
||||
},
|
||||
{
|
||||
src: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipSlice,
|
||||
expected: []net.IP{mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
},
|
||||
{
|
||||
src: pgtype.InetArray{
|
||||
Elements: []pgtype.Inet{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &ipSlice,
|
||||
expected: []net.IP{nil},
|
||||
},
|
||||
{
|
||||
src: pgtype.InetArray{Status: pgtype.Null},
|
||||
dst: &ipnetSlice,
|
||||
expected: (([]*net.IPNet)(nil)),
|
||||
},
|
||||
{
|
||||
src: pgtype.InetArray{Status: pgtype.Null},
|
||||
dst: &ipSlice,
|
||||
expected: (([]net.IP)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInetTranscode(t *testing.T) {
|
||||
for _, pgTypeName := range []string{"inet", "cidr"} {
|
||||
testutil.TestSuccessfulTranscode(t, pgTypeName, []interface{}{
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "0.0.0.0/32"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "12.34.56.0/32"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.1.0/24"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "255.0.0.0/8"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "255.255.255.255/32"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "::/128"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "::/0"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "::1/128"), Status: pgtype.Present},
|
||||
&pgtype.Inet{IPNet: mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"), Status: pgtype.Present},
|
||||
&pgtype.Inet{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInetSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Inet
|
||||
}{
|
||||
{source: mustParseCIDR(t, "127.0.0.1/32"), result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
{source: mustParseCIDR(t, "127.0.0.1/32").IP, result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
{source: "127.0.0.1/32", result: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Inet
|
||||
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 TestInetAssignTo(t *testing.T) {
|
||||
var ipnet net.IPNet
|
||||
var pipnet *net.IPNet
|
||||
var ip net.IP
|
||||
var pip *net.IP
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Inet
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")},
|
||||
{src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &ip, expected: mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
{src: pgtype.Inet{Status: pgtype.Null}, dst: &pipnet, expected: ((*net.IPNet)(nil))},
|
||||
{src: pgtype.Inet{Status: pgtype.Null}, dst: &pip, expected: ((*net.IP)(nil))},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pointerAllocTests := []struct {
|
||||
src pgtype.Inet
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pipnet, expected: *mustParseCIDR(t, "127.0.0.1/32")},
|
||||
{src: pgtype.Inet{IPNet: mustParseCIDR(t, "127.0.0.1/32"), Status: pgtype.Present}, dst: &pip, expected: mustParseCIDR(t, "127.0.0.1/32").IP},
|
||||
}
|
||||
|
||||
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(); !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.Inet
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Inet{IPNet: mustParseCIDR(t, "192.168.0.0/16"), Status: pgtype.Present}, dst: &ip},
|
||||
{src: pgtype.Inet{Status: pgtype.Null}, dst: &ipnet},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-209
@@ -1,209 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int2 struct {
|
||||
Int int16
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int2) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case int8:
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint8:
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int16:
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint16:
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int32:
|
||||
if value < math.MinInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint32:
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int64:
|
||||
if value < math.MinInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint64:
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case int:
|
||||
if value < math.MinInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case uint:
|
||||
if value > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", value)
|
||||
}
|
||||
*dst = Int2{Int: int16(value), Status: Present}
|
||||
case string:
|
||||
num, err := strconv.ParseInt(value, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Int2{Int: int16(num), Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int2", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int2) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Int
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int2) AssignTo(dst interface{}) error {
|
||||
return int64AssignTo(int64(src.Int), src.Status, dst)
|
||||
}
|
||||
|
||||
func (dst *Int2) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.ParseInt(string(src), 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int2{Int: int16(n), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int2) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 2 {
|
||||
return errors.Errorf("invalid length for int2: %v", len(src))
|
||||
}
|
||||
|
||||
n := int16(binary.BigEndian.Uint16(src))
|
||||
*dst = Int2{Int: n, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int2) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil
|
||||
}
|
||||
|
||||
func (src *Int2) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return pgio.AppendInt16(buf, src.Int), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int2) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int2{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case int64:
|
||||
if src < math.MinInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", src)
|
||||
}
|
||||
if src > math.MaxInt16 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int2", src)
|
||||
}
|
||||
*dst = Int2{Int: int16(src), Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int2) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return int64(src.Int), nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int2) MarshalJSON() ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return []byte(strconv.FormatInt(int64(src.Int), 10)), nil
|
||||
case Null:
|
||||
return []byte("null"), nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return nil, errBadStatus
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int2Array struct {
|
||||
Elements []Int2
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int2Array) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int2Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int2Array", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int2Array) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int2Array) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
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 *[]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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int2Array) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int2Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int2
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Int2, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Int2
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int2Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int2Array) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int2Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = Int2Array{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Int2, 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 = Int2Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int2Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *Int2Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("int2"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "int2")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *Int2Array) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int2Array) 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,177 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt2ArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int2[]", []interface{}{
|
||||
&pgtype.Int2Array{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int2Array{Status: pgtype.Null},
|
||||
&pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Int: 6, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt2ArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int2Array
|
||||
}{
|
||||
{
|
||||
source: []int16{1},
|
||||
result: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: []uint16{1},
|
||||
result: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]int16)(nil)),
|
||||
result: pgtype.Int2Array{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int2Array
|
||||
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 TestInt2ArrayAssignTo(t *testing.T) {
|
||||
var int16Slice []int16
|
||||
var uint16Slice []uint16
|
||||
var namedInt16Slice _int16Slice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int2Array
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int16Slice,
|
||||
expected: []int16{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint16Slice,
|
||||
expected: []uint16{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedInt16Slice,
|
||||
expected: _int16Slice{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int2Array{Status: pgtype.Null},
|
||||
dst: &int16Slice,
|
||||
expected: (([]int16)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.Int2Array
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int16Slice,
|
||||
},
|
||||
{
|
||||
src: pgtype.Int2Array{
|
||||
Elements: []pgtype.Int2{{Int: -1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint16Slice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt2Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int2", []interface{}{
|
||||
&pgtype.Int2{Int: math.MinInt16, Status: pgtype.Present},
|
||||
&pgtype.Int2{Int: -1, Status: pgtype.Present},
|
||||
&pgtype.Int2{Int: 0, Status: pgtype.Present},
|
||||
&pgtype.Int2{Int: 1, Status: pgtype.Present},
|
||||
&pgtype.Int2{Int: math.MaxInt16, Status: pgtype.Present},
|
||||
&pgtype.Int2{Int: 0, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt2Set(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int2
|
||||
}{
|
||||
{source: int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: int16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: int32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: int64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: int8(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}},
|
||||
{source: int16(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}},
|
||||
{source: int32(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}},
|
||||
{source: int64(-1), result: pgtype.Int2{Int: -1, Status: pgtype.Present}},
|
||||
{source: uint8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint16(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint32(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint64(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: "1", result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
{source: _int8(1), result: pgtype.Int2{Int: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int2
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt2AssignTo(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
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int2
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Int2
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)},
|
||||
{src: pgtype.Int2{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)},
|
||||
}
|
||||
|
||||
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.Int2
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Int2{Int: 150, Status: pgtype.Present}, dst: &i8},
|
||||
{src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui8},
|
||||
{src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui16},
|
||||
{src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui32},
|
||||
{src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui64},
|
||||
{src: pgtype.Int2{Int: -1, Status: pgtype.Present}, dst: &ui},
|
||||
{src: pgtype.Int2{Int: 0, Status: pgtype.Null}, dst: &i16},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-213
@@ -1,213 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int4 struct {
|
||||
Int int32
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case int8:
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint8:
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int16:
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint16:
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int32:
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint32:
|
||||
if value > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int64:
|
||||
if value < math.MinInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
if value > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint64:
|
||||
if value > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case int:
|
||||
if value < math.MinInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
if value > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case uint:
|
||||
if value > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", value)
|
||||
}
|
||||
*dst = Int4{Int: int32(value), Status: Present}
|
||||
case string:
|
||||
num, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Int4{Int: int32(num), Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int4", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Int
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4) AssignTo(dst interface{}) error {
|
||||
return int64AssignTo(int64(src.Int), src.Status, dst)
|
||||
}
|
||||
|
||||
func (dst *Int4) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.ParseInt(string(src), 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int4{Int: int32(n), Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 4 {
|
||||
return errors.Errorf("invalid length for int4: %v", len(src))
|
||||
}
|
||||
|
||||
n := int32(binary.BigEndian.Uint32(src))
|
||||
*dst = Int4{Int: n, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int4) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, strconv.FormatInt(int64(src.Int), 10)...), nil
|
||||
}
|
||||
|
||||
func (src *Int4) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return pgio.AppendInt32(buf, src.Int), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int4) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int4{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case int64:
|
||||
if src < math.MinInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", src)
|
||||
}
|
||||
if src > math.MaxInt32 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int4", src)
|
||||
}
|
||||
*dst = Int4{Int: int32(src), Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int4) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return int64(src.Int), nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4) MarshalJSON() ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return []byte(strconv.FormatInt(int64(src.Int), 10)), nil
|
||||
case Null:
|
||||
return []byte("null"), nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return nil, errBadStatus
|
||||
}
|
||||
|
||||
func (dst *Int4) UnmarshalJSON(b []byte) error {
|
||||
var n int32
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int4{Int: n, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,348 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int4Array struct {
|
||||
Elements []Int4
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4Array) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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 []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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int4Array", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4Array) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4Array) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int4Array) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int4
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Int4, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Int4
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int4Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4Array) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = Int4Array{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Int4, 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 = Int4Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int4Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *Int4Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("int4"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "int4")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *Int4Array) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int4Array) 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,198 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt4ArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int4[]", []interface{}{
|
||||
&pgtype.Int4Array{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int4Array{Status: pgtype.Null},
|
||||
&pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Int: 6, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt4ArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int4Array
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
source: []int32{1},
|
||||
result: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: []int{1},
|
||||
result: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: []int{1, math.MaxInt32 + 1, 2},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
source: []uint32{1},
|
||||
result: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]int32)(nil)),
|
||||
result: pgtype.Int4Array{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int4Array
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
if tt.expectedError {
|
||||
continue
|
||||
}
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if tt.expectedError {
|
||||
t.Errorf("%d: an error was expected, %v", i, tt)
|
||||
continue
|
||||
}
|
||||
|
||||
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 TestInt4ArrayAssignTo(t *testing.T) {
|
||||
var int32Slice []int32
|
||||
var uint32Slice []uint32
|
||||
var namedInt32Slice _int32Slice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int4Array
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int32Slice,
|
||||
expected: []int32{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint32Slice,
|
||||
expected: []uint32{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedInt32Slice,
|
||||
expected: _int32Slice{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int4Array{Status: pgtype.Null},
|
||||
dst: &int32Slice,
|
||||
expected: (([]int32)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.Int4Array
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int32Slice,
|
||||
},
|
||||
{
|
||||
src: pgtype.Int4Array{
|
||||
Elements: []pgtype.Int4{{Int: -1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint32Slice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt4Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int4", []interface{}{
|
||||
&pgtype.Int4{Int: math.MinInt32, Status: pgtype.Present},
|
||||
&pgtype.Int4{Int: -1, Status: pgtype.Present},
|
||||
&pgtype.Int4{Int: 0, Status: pgtype.Present},
|
||||
&pgtype.Int4{Int: 1, Status: pgtype.Present},
|
||||
&pgtype.Int4{Int: math.MaxInt32, Status: pgtype.Present},
|
||||
&pgtype.Int4{Int: 0, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt4Set(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int4
|
||||
}{
|
||||
{source: int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: int16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: int32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: int64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: int8(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}},
|
||||
{source: int16(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}},
|
||||
{source: int32(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}},
|
||||
{source: int64(-1), result: pgtype.Int4{Int: -1, Status: pgtype.Present}},
|
||||
{source: uint8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint16(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint32(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint64(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: "1", result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
{source: _int8(1), result: pgtype.Int4{Int: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int4
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt4AssignTo(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
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int4
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Int4
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)},
|
||||
{src: pgtype.Int4{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)},
|
||||
}
|
||||
|
||||
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.Int4
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Int4{Int: 150, Status: pgtype.Present}, dst: &i8},
|
||||
{src: pgtype.Int4{Int: 40000, Status: pgtype.Present}, dst: &i16},
|
||||
{src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui8},
|
||||
{src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui16},
|
||||
{src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui32},
|
||||
{src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui64},
|
||||
{src: pgtype.Int4{Int: -1, Status: pgtype.Present}, dst: &ui},
|
||||
{src: pgtype.Int4{Int: 0, Status: pgtype.Null}, dst: &i32},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int4range struct {
|
||||
Lower Int4
|
||||
Upper Int4
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int4range) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Int4range", src)
|
||||
}
|
||||
|
||||
func (dst *Int4range) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int4range) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int4range) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4range{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utr, err := ParseUntypedTextRange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int4range{Status: Present}
|
||||
|
||||
dst.LowerType = utr.LowerType
|
||||
dst.UpperType = utr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int4range) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int4range{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
ubr, err := ParseUntypedBinaryRange(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int4range{Status: Present}
|
||||
|
||||
dst.LowerType = ubr.LowerType
|
||||
dst.UpperType = ubr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Int4range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
switch src.LowerType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, '(')
|
||||
case Inclusive:
|
||||
buf = append(buf, '[')
|
||||
case Empty:
|
||||
return append(buf, "empty"...), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown lower bound type %v", src.LowerType)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
buf, err = src.Lower.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
buf = append(buf, ',')
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
buf, err = src.Upper.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, ')')
|
||||
case Inclusive:
|
||||
buf = append(buf, ']')
|
||||
default:
|
||||
return nil, errors.Errorf("unknown upper bound type %v", src.UpperType)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Int4range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var rangeType byte
|
||||
switch src.LowerType {
|
||||
case Inclusive:
|
||||
rangeType |= lowerInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= lowerUnboundedMask
|
||||
case Exclusive:
|
||||
case Empty:
|
||||
return append(buf, emptyMask), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown LowerType: %v", src.LowerType)
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Inclusive:
|
||||
rangeType |= upperInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= upperUnboundedMask
|
||||
case Exclusive:
|
||||
default:
|
||||
return nil, errors.Errorf("unknown UpperType: %v", src.UpperType)
|
||||
}
|
||||
|
||||
buf = append(buf, rangeType)
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Lower.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Upper.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int4range) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int4range{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int4range) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt4rangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int4range", []interface{}{
|
||||
&pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present},
|
||||
&pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int4range{Lower: pgtype.Int4{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int4{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int4range{Lower: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present},
|
||||
&pgtype.Int4range{Upper: pgtype.Int4{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int4range{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt4rangeNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select int4range(1, 10, '(]')",
|
||||
Value: pgtype.Int4range{Lower: pgtype.Int4{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int4{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
},
|
||||
})
|
||||
}
|
||||
-199
@@ -1,199 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int8 struct {
|
||||
Int int64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int8) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case int8:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case uint8:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case int16:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case uint16:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case int32:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case uint32:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case int64:
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case uint64:
|
||||
if value > math.MaxInt64 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int8", value)
|
||||
}
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case int:
|
||||
if int64(value) < math.MinInt64 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int8", value)
|
||||
}
|
||||
if int64(value) > math.MaxInt64 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int8", value)
|
||||
}
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case uint:
|
||||
if uint64(value) > math.MaxInt64 {
|
||||
return errors.Errorf("%d is greater than maximum value for Int8", value)
|
||||
}
|
||||
*dst = Int8{Int: int64(value), Status: Present}
|
||||
case string:
|
||||
num, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Int8{Int: num, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingNumberType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int8", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Int
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int8) AssignTo(dst interface{}) error {
|
||||
return int64AssignTo(int64(src.Int), src.Status, dst)
|
||||
}
|
||||
|
||||
func (dst *Int8) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
n, err := strconv.ParseInt(string(src), 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int8{Int: n, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 8 {
|
||||
return errors.Errorf("invalid length for int8: %v", len(src))
|
||||
}
|
||||
|
||||
n := int64(binary.BigEndian.Uint64(src))
|
||||
|
||||
*dst = Int8{Int: n, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int8) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, strconv.FormatInt(src.Int, 10)...), nil
|
||||
}
|
||||
|
||||
func (src *Int8) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return pgio.AppendInt64(buf, src.Int), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int8) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int8{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch src := src.(type) {
|
||||
case int64:
|
||||
*dst = Int8{Int: src, Status: Present}
|
||||
return nil
|
||||
case string:
|
||||
return dst.DecodeText(nil, []byte(src))
|
||||
case []byte:
|
||||
srcCopy := make([]byte, len(src))
|
||||
copy(srcCopy, src)
|
||||
return dst.DecodeText(nil, srcCopy)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int8) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return int64(src.Int), nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int8) MarshalJSON() ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return []byte(strconv.FormatInt(src.Int, 10)), nil
|
||||
case Null:
|
||||
return []byte("null"), nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return nil, errBadStatus
|
||||
}
|
||||
|
||||
func (dst *Int8) UnmarshalJSON(b []byte) error {
|
||||
var n int64
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int8{Int: n, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int8Array struct {
|
||||
Elements []Int8
|
||||
Dimensions []ArrayDimension
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int8Array) Set(src interface{}) error {
|
||||
// untyped nil and typed nil interfaces are different
|
||||
if src == nil {
|
||||
*dst = Int8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if originalSrc, ok := underlyingSliceType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Int8Array", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8Array) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int8Array) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
|
||||
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
|
||||
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int8Array) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
uta, err := ParseUntypedTextArray(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var elements []Int8
|
||||
|
||||
if len(uta.Elements) > 0 {
|
||||
elements = make([]Int8, len(uta.Elements))
|
||||
|
||||
for i, s := range uta.Elements {
|
||||
var elem Int8
|
||||
var elemSrc []byte
|
||||
if s != "NULL" {
|
||||
elemSrc = []byte(s)
|
||||
}
|
||||
err = elem.DecodeText(ci, elemSrc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
elements[i] = elem
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Int8Array{Elements: elements, Dimensions: uta.Dimensions, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8Array) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8Array{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var arrayHeader ArrayHeader
|
||||
rp, err := arrayHeader.DecodeBinary(ci, src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arrayHeader.Dimensions) == 0 {
|
||||
*dst = Int8Array{Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
elementCount := arrayHeader.Dimensions[0].Length
|
||||
for _, d := range arrayHeader.Dimensions[1:] {
|
||||
elementCount *= d.Length
|
||||
}
|
||||
|
||||
elements := make([]Int8, 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 = Int8Array{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Int8Array) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
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 *Int8Array) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
arrayHeader := ArrayHeader{
|
||||
Dimensions: src.Dimensions,
|
||||
}
|
||||
|
||||
if dt, ok := ci.DataTypeForName("int8"); ok {
|
||||
arrayHeader.ElementOID = int32(dt.OID)
|
||||
} else {
|
||||
return nil, errors.Errorf("unable to find oid for type name %v", "int8")
|
||||
}
|
||||
|
||||
for i := range src.Elements {
|
||||
if src.Elements[i].Status == Null {
|
||||
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 *Int8Array) 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Int8Array) 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,177 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt8ArrayTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int8[]", []interface{}{
|
||||
&pgtype.Int8Array{
|
||||
Elements: nil,
|
||||
Dimensions: nil,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int8Array{Status: pgtype.Null},
|
||||
&pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
{Status: pgtype.Null},
|
||||
{Int: 6, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{{Length: 3, LowerBound: 1}, {Length: 2, LowerBound: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{
|
||||
{Int: 1, Status: pgtype.Present},
|
||||
{Int: 2, Status: pgtype.Present},
|
||||
{Int: 3, Status: pgtype.Present},
|
||||
{Int: 4, Status: pgtype.Present},
|
||||
},
|
||||
Dimensions: []pgtype.ArrayDimension{
|
||||
{Length: 2, LowerBound: 4},
|
||||
{Length: 2, LowerBound: 2},
|
||||
},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt8ArraySet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int8Array
|
||||
}{
|
||||
{
|
||||
source: []int64{1},
|
||||
result: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: []uint64{1},
|
||||
result: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
source: (([]int64)(nil)),
|
||||
result: pgtype.Int8Array{Status: pgtype.Null},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int8Array
|
||||
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 TestInt8ArrayAssignTo(t *testing.T) {
|
||||
var int64Slice []int64
|
||||
var uint64Slice []uint64
|
||||
var namedInt64Slice _int64Slice
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int8Array
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int64Slice,
|
||||
expected: []int64{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint64Slice,
|
||||
expected: []uint64{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: 1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &namedInt64Slice,
|
||||
expected: _int64Slice{1},
|
||||
},
|
||||
{
|
||||
src: pgtype.Int8Array{Status: pgtype.Null},
|
||||
dst: &int64Slice,
|
||||
expected: (([]int64)(nil)),
|
||||
},
|
||||
}
|
||||
|
||||
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.Int8Array
|
||||
dst interface{}
|
||||
}{
|
||||
{
|
||||
src: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Status: pgtype.Null}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &int64Slice,
|
||||
},
|
||||
{
|
||||
src: pgtype.Int8Array{
|
||||
Elements: []pgtype.Int8{{Int: -1, Status: pgtype.Present}},
|
||||
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
dst: &uint64Slice,
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt8Transcode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "int8", []interface{}{
|
||||
&pgtype.Int8{Int: math.MinInt64, Status: pgtype.Present},
|
||||
&pgtype.Int8{Int: -1, Status: pgtype.Present},
|
||||
&pgtype.Int8{Int: 0, Status: pgtype.Present},
|
||||
&pgtype.Int8{Int: 1, Status: pgtype.Present},
|
||||
&pgtype.Int8{Int: math.MaxInt64, Status: pgtype.Present},
|
||||
&pgtype.Int8{Int: 0, Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt8Set(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.Int8
|
||||
}{
|
||||
{source: int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: int16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: int32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: int64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: int8(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}},
|
||||
{source: int16(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}},
|
||||
{source: int32(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}},
|
||||
{source: int64(-1), result: pgtype.Int8{Int: -1, Status: pgtype.Present}},
|
||||
{source: uint8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint16(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint32(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: uint64(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: "1", result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
{source: _int8(1), result: pgtype.Int8{Int: 1, Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var r pgtype.Int8
|
||||
err := r.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if r != tt.result {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt8AssignTo(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
|
||||
|
||||
simpleTests := []struct {
|
||||
src pgtype.Int8
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i8, expected: int8(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i16, expected: int16(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i32, expected: int32(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i64, expected: int64(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &i, expected: int(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui8, expected: uint8(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui16, expected: uint16(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui32, expected: uint32(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui64, expected: uint64(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &ui, expected: uint(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_i8, expected: _int8(42)},
|
||||
{src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &pi8, expected: ((*int8)(nil))},
|
||||
{src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &_pi8, expected: ((*_int8)(nil))},
|
||||
}
|
||||
|
||||
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(); 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.Int8
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &pi8, expected: int8(42)},
|
||||
{src: pgtype.Int8{Int: 42, Status: pgtype.Present}, dst: &_pi8, expected: _int8(42)},
|
||||
}
|
||||
|
||||
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.Int8
|
||||
dst interface{}
|
||||
}{
|
||||
{src: pgtype.Int8{Int: 150, Status: pgtype.Present}, dst: &i8},
|
||||
{src: pgtype.Int8{Int: 40000, Status: pgtype.Present}, dst: &i16},
|
||||
{src: pgtype.Int8{Int: 5000000000, Status: pgtype.Present}, dst: &i32},
|
||||
{src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui8},
|
||||
{src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui16},
|
||||
{src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui32},
|
||||
{src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui64},
|
||||
{src: pgtype.Int8{Int: -1, Status: pgtype.Present}, dst: &ui},
|
||||
{src: pgtype.Int8{Int: 0, Status: pgtype.Null}, dst: &i64},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Int8range struct {
|
||||
Lower Int8
|
||||
Upper Int8
|
||||
LowerType BoundType
|
||||
UpperType BoundType
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Int8range) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Int8range", src)
|
||||
}
|
||||
|
||||
func (dst *Int8range) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Int8range) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Int8range) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8range{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
utr, err := ParseUntypedTextRange(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int8range{Status: Present}
|
||||
|
||||
dst.LowerType = utr.LowerType
|
||||
dst.UpperType = utr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeText(ci, []byte(utr.Lower)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeText(ci, []byte(utr.Upper)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Int8range) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Int8range{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
ubr, err := ParseUntypedBinaryRange(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Int8range{Status: Present}
|
||||
|
||||
dst.LowerType = ubr.LowerType
|
||||
dst.UpperType = ubr.UpperType
|
||||
|
||||
if dst.LowerType == Empty {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dst.LowerType == Inclusive || dst.LowerType == Exclusive {
|
||||
if err := dst.Lower.DecodeBinary(ci, ubr.Lower); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.UpperType == Inclusive || dst.UpperType == Exclusive {
|
||||
if err := dst.Upper.DecodeBinary(ci, ubr.Upper); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src Int8range) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
switch src.LowerType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, '(')
|
||||
case Inclusive:
|
||||
buf = append(buf, '[')
|
||||
case Empty:
|
||||
return append(buf, "empty"...), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown lower bound type %v", src.LowerType)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
buf, err = src.Lower.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
buf = append(buf, ',')
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
buf, err = src.Upper.EncodeText(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Exclusive, Unbounded:
|
||||
buf = append(buf, ')')
|
||||
case Inclusive:
|
||||
buf = append(buf, ']')
|
||||
default:
|
||||
return nil, errors.Errorf("unknown upper bound type %v", src.UpperType)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src Int8range) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
var rangeType byte
|
||||
switch src.LowerType {
|
||||
case Inclusive:
|
||||
rangeType |= lowerInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= lowerUnboundedMask
|
||||
case Exclusive:
|
||||
case Empty:
|
||||
return append(buf, emptyMask), nil
|
||||
default:
|
||||
return nil, errors.Errorf("unknown LowerType: %v", src.LowerType)
|
||||
}
|
||||
|
||||
switch src.UpperType {
|
||||
case Inclusive:
|
||||
rangeType |= upperInclusiveMask
|
||||
case Unbounded:
|
||||
rangeType |= upperUnboundedMask
|
||||
case Exclusive:
|
||||
default:
|
||||
return nil, errors.Errorf("unknown UpperType: %v", src.UpperType)
|
||||
}
|
||||
|
||||
buf = append(buf, rangeType)
|
||||
|
||||
var err error
|
||||
|
||||
if src.LowerType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Lower.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Lower cannot be null unless LowerType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
if src.UpperType != Unbounded {
|
||||
sp := len(buf)
|
||||
buf = pgio.AppendInt32(buf, -1)
|
||||
|
||||
buf, err = src.Upper.EncodeBinary(ci, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, errors.Errorf("Upper cannot be null unless UpperType is Unbounded")
|
||||
}
|
||||
|
||||
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Int8range) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Int8range{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src Int8range) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestInt8rangeTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "Int8range", []interface{}{
|
||||
&pgtype.Int8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Status: pgtype.Present},
|
||||
&pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 10, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int8range{Lower: pgtype.Int8{Int: -42, Status: pgtype.Present}, Upper: pgtype.Int8{Int: -5, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int8range{Lower: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Unbounded, Status: pgtype.Present},
|
||||
&pgtype.Int8range{Upper: pgtype.Int8{Int: 1, Status: pgtype.Present}, LowerType: pgtype.Unbounded, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
&pgtype.Int8range{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestInt8rangeNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select Int8range(1, 10, '(]')",
|
||||
Value: pgtype.Int8range{Lower: pgtype.Int8{Int: 2, Status: pgtype.Present}, Upper: pgtype.Int8{Int: 11, Status: pgtype.Present}, LowerType: pgtype.Inclusive, UpperType: pgtype.Exclusive, Status: pgtype.Present},
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
const (
|
||||
microsecondsPerSecond = 1000000
|
||||
microsecondsPerMinute = 60 * microsecondsPerSecond
|
||||
microsecondsPerHour = 60 * microsecondsPerMinute
|
||||
)
|
||||
|
||||
type Interval struct {
|
||||
Microseconds int64
|
||||
Days int32
|
||||
Months int32
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Interval) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Interval{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case time.Duration:
|
||||
*dst = Interval{Microseconds: int64(value) / 1000, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingPtrType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Interval", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Interval) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Interval) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *time.Duration:
|
||||
if src.Days > 0 || src.Months > 0 {
|
||||
return errors.Errorf("interval with months or days cannot be decoded into %T", dst)
|
||||
}
|
||||
*v = time.Duration(src.Microseconds) * time.Microsecond
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Interval) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Interval{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
var microseconds int64
|
||||
var days int32
|
||||
var months int32
|
||||
|
||||
parts := strings.Split(string(src), " ")
|
||||
|
||||
for i := 0; i < len(parts)-1; i += 2 {
|
||||
scalar, err := strconv.ParseInt(parts[i], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("bad interval format")
|
||||
}
|
||||
|
||||
switch parts[i+1] {
|
||||
case "year", "years":
|
||||
months += int32(scalar * 12)
|
||||
case "mon", "mons":
|
||||
months += int32(scalar)
|
||||
case "day", "days":
|
||||
days = int32(scalar)
|
||||
}
|
||||
}
|
||||
|
||||
if len(parts)%2 == 1 {
|
||||
timeParts := strings.SplitN(parts[len(parts)-1], ":", 3)
|
||||
if len(timeParts) != 3 {
|
||||
return errors.Errorf("bad interval format")
|
||||
}
|
||||
|
||||
var negative bool
|
||||
if timeParts[0][0] == '-' {
|
||||
negative = true
|
||||
timeParts[0] = timeParts[0][1:]
|
||||
}
|
||||
|
||||
hours, err := strconv.ParseInt(timeParts[0], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("bad interval hour format: %s", timeParts[0])
|
||||
}
|
||||
|
||||
minutes, err := strconv.ParseInt(timeParts[1], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("bad interval minute format: %s", timeParts[1])
|
||||
}
|
||||
|
||||
secondParts := strings.SplitN(timeParts[2], ".", 2)
|
||||
|
||||
seconds, err := strconv.ParseInt(secondParts[0], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("bad interval second format: %s", secondParts[0])
|
||||
}
|
||||
|
||||
var uSeconds int64
|
||||
if len(secondParts) == 2 {
|
||||
uSeconds, err = strconv.ParseInt(secondParts[1], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("bad interval decimal format: %s", secondParts[1])
|
||||
}
|
||||
|
||||
for i := 0; i < 6-len(secondParts[1]); i++ {
|
||||
uSeconds *= 10
|
||||
}
|
||||
}
|
||||
|
||||
microseconds = hours * microsecondsPerHour
|
||||
microseconds += minutes * microsecondsPerMinute
|
||||
microseconds += seconds * microsecondsPerSecond
|
||||
microseconds += uSeconds
|
||||
|
||||
if negative {
|
||||
microseconds = -microseconds
|
||||
}
|
||||
}
|
||||
|
||||
*dst = Interval{Months: months, Days: days, Microseconds: microseconds, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Interval{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 16 {
|
||||
return errors.Errorf("Received an invalid size for a interval: %d", len(src))
|
||||
}
|
||||
|
||||
microseconds := int64(binary.BigEndian.Uint64(src))
|
||||
days := int32(binary.BigEndian.Uint32(src[8:]))
|
||||
months := int32(binary.BigEndian.Uint32(src[12:]))
|
||||
|
||||
*dst = Interval{Microseconds: microseconds, Days: days, Months: months, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Interval) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
if src.Months != 0 {
|
||||
buf = append(buf, strconv.FormatInt(int64(src.Months), 10)...)
|
||||
buf = append(buf, " mon "...)
|
||||
}
|
||||
|
||||
if src.Days != 0 {
|
||||
buf = append(buf, strconv.FormatInt(int64(src.Days), 10)...)
|
||||
buf = append(buf, " day "...)
|
||||
}
|
||||
|
||||
absMicroseconds := src.Microseconds
|
||||
if absMicroseconds < 0 {
|
||||
absMicroseconds = -absMicroseconds
|
||||
buf = append(buf, '-')
|
||||
}
|
||||
|
||||
hours := absMicroseconds / microsecondsPerHour
|
||||
minutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute
|
||||
seconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond
|
||||
microseconds := absMicroseconds % microsecondsPerSecond
|
||||
|
||||
timeStr := fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, microseconds)
|
||||
return append(buf, timeStr...), nil
|
||||
}
|
||||
|
||||
// EncodeBinary encodes src into w.
|
||||
func (src *Interval) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendInt64(buf, src.Microseconds)
|
||||
buf = pgio.AppendInt32(buf, src.Days)
|
||||
return pgio.AppendInt32(buf, src.Months), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Interval) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Interval{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Interval) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestIntervalTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "interval", []interface{}{
|
||||
&pgtype.Interval{Microseconds: 1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present},
|
||||
&pgtype.Interval{Days: 1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: 1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: 12, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: 13, Days: 15, Microseconds: 1000001, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: -1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: -1000000, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: -1000001, Status: pgtype.Present},
|
||||
&pgtype.Interval{Microseconds: -123202800000000, Status: pgtype.Present},
|
||||
&pgtype.Interval{Days: -1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: -1, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: -12, Status: pgtype.Present},
|
||||
&pgtype.Interval{Months: -13, Days: -15, Microseconds: -1000001, Status: pgtype.Present},
|
||||
&pgtype.Interval{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntervalNormalize(t *testing.T) {
|
||||
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
|
||||
{
|
||||
SQL: "select '1 second'::interval",
|
||||
Value: &pgtype.Interval{Microseconds: 1000000, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1.000001 second'::interval",
|
||||
Value: &pgtype.Interval{Microseconds: 1000001, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '34223 hours'::interval",
|
||||
Value: &pgtype.Interval{Microseconds: 123202800000000, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1 day'::interval",
|
||||
Value: &pgtype.Interval{Days: 1, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1 month'::interval",
|
||||
Value: &pgtype.Interval{Months: 1, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '1 year'::interval",
|
||||
Value: &pgtype.Interval{Months: 12, Status: pgtype.Present},
|
||||
},
|
||||
{
|
||||
SQL: "select '-13 mon'::interval",
|
||||
Value: &pgtype.Interval{Months: -13, Status: pgtype.Present},
|
||||
},
|
||||
})
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type JSON struct {
|
||||
Bytes []byte
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *JSON) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = JSON{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case string:
|
||||
*dst = JSON{Bytes: []byte(value), Status: Present}
|
||||
case *string:
|
||||
if value == nil {
|
||||
*dst = JSON{Status: Null}
|
||||
} else {
|
||||
*dst = JSON{Bytes: []byte(*value), Status: Present}
|
||||
}
|
||||
case []byte:
|
||||
if value == nil {
|
||||
*dst = JSON{Status: Null}
|
||||
} else {
|
||||
*dst = JSON{Bytes: value, Status: Present}
|
||||
}
|
||||
// Encode* methods are defined on *JSON. If JSON is passed directly then the
|
||||
// struct itself would be encoded instead of Bytes. This is clearly a footgun
|
||||
// so detect and return an error. See https://github.com/jackc/pgx/issues/350.
|
||||
case JSON:
|
||||
return errors.New("use pointer to pgtype.JSON instead of value")
|
||||
// Same as above but for JSONB (because they share implementation)
|
||||
case JSONB:
|
||||
return errors.New("use pointer to pgtype.JSONB instead of value")
|
||||
|
||||
default:
|
||||
buf, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = JSON{Bytes: buf, Status: Present}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *JSON) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
var i interface{}
|
||||
err := json.Unmarshal(dst.Bytes, &i)
|
||||
if err != nil {
|
||||
return dst
|
||||
}
|
||||
return i
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *JSON) AssignTo(dst interface{}) error {
|
||||
switch v := dst.(type) {
|
||||
case *string:
|
||||
if src.Status == Present {
|
||||
*v = string(src.Bytes)
|
||||
} else {
|
||||
return errors.Errorf("cannot assign non-present status to %T", dst)
|
||||
}
|
||||
case **string:
|
||||
if src.Status == Present {
|
||||
s := string(src.Bytes)
|
||||
*v = &s
|
||||
return nil
|
||||
} else {
|
||||
*v = nil
|
||||
return nil
|
||||
}
|
||||
case *[]byte:
|
||||
if src.Status != Present {
|
||||
*v = nil
|
||||
} else {
|
||||
buf := make([]byte, len(src.Bytes))
|
||||
copy(buf, src.Bytes)
|
||||
*v = buf
|
||||
}
|
||||
default:
|
||||
data := src.Bytes
|
||||
if data == nil || src.Status != Present {
|
||||
data = []byte("null")
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, dst)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *JSON) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = JSON{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
*dst = JSON{Bytes: src, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *JSON) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
return dst.DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (src *JSON) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.Bytes...), nil
|
||||
}
|
||||
|
||||
func (src *JSON) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return src.EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *JSON) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = JSON{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *JSON) Value() (driver.Value, error) {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
return src.Bytes, nil
|
||||
case Null:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, errUndefined
|
||||
}
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestJSONTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "json", []interface{}{
|
||||
&pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present},
|
||||
&pgtype.JSON{Bytes: []byte("null"), Status: pgtype.Present},
|
||||
&pgtype.JSON{Bytes: []byte("42"), Status: pgtype.Present},
|
||||
&pgtype.JSON{Bytes: []byte(`"hello"`), Status: pgtype.Present},
|
||||
&pgtype.JSON{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestJSONSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.JSON
|
||||
}{
|
||||
{source: "{}", result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}},
|
||||
{source: []byte("{}"), result: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}},
|
||||
{source: ([]byte)(nil), result: pgtype.JSON{Status: pgtype.Null}},
|
||||
{source: (*string)(nil), result: pgtype.JSON{Status: pgtype.Null}},
|
||||
{source: []int{1, 2, 3}, result: pgtype.JSON{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}},
|
||||
{source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var d pgtype.JSON
|
||||
err := d.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONAssignTo(t *testing.T) {
|
||||
var s string
|
||||
var ps *string
|
||||
var b []byte
|
||||
|
||||
rawStringTests := []struct {
|
||||
src pgtype.JSON
|
||||
dst *string
|
||||
expected string
|
||||
}{
|
||||
{src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"},
|
||||
}
|
||||
|
||||
for i, tt := range rawStringTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if *tt.dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
|
||||
rawBytesTests := []struct {
|
||||
src pgtype.JSON
|
||||
dst *[]byte
|
||||
expected []byte
|
||||
}{
|
||||
{src: pgtype.JSON{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")},
|
||||
{src: pgtype.JSON{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range rawBytesTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if bytes.Compare(tt.expected, *tt.dst) != 0 {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
|
||||
var mapDst map[string]interface{}
|
||||
type structDst struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
var strDst structDst
|
||||
|
||||
unmarshalTests := []struct {
|
||||
src pgtype.JSON
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.JSON{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}},
|
||||
{src: pgtype.JSON{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}},
|
||||
}
|
||||
for i, tt := range unmarshalTests {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pointerAllocTests := []struct {
|
||||
src pgtype.JSON
|
||||
dst **string
|
||||
expected *string
|
||||
}{
|
||||
{src: pgtype.JSON{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range pointerAllocTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if *tt.dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type JSONB JSON
|
||||
|
||||
func (dst *JSONB) Set(src interface{}) error {
|
||||
return (*JSON)(dst).Set(src)
|
||||
}
|
||||
|
||||
func (dst *JSONB) Get() interface{} {
|
||||
return (*JSON)(dst).Get()
|
||||
}
|
||||
|
||||
func (src *JSONB) AssignTo(dst interface{}) error {
|
||||
return (*JSON)(src).AssignTo(dst)
|
||||
}
|
||||
|
||||
func (dst *JSONB) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
return (*JSON)(dst).DecodeText(ci, src)
|
||||
}
|
||||
|
||||
func (dst *JSONB) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = JSONB{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) == 0 {
|
||||
return errors.Errorf("jsonb too short")
|
||||
}
|
||||
|
||||
if src[0] != 1 {
|
||||
return errors.Errorf("unknown jsonb version number %d", src[0])
|
||||
}
|
||||
|
||||
*dst = JSONB{Bytes: src[1:], Status: Present}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (src *JSONB) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
return (*JSON)(src).EncodeText(ci, buf)
|
||||
}
|
||||
|
||||
func (src *JSONB) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, 1)
|
||||
return append(buf, src.Bytes...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *JSONB) Scan(src interface{}) error {
|
||||
return (*JSON)(dst).Scan(src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *JSONB) Value() (driver.Value, error) {
|
||||
return (*JSON)(src).Value()
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestJSONBTranscode(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
if _, ok := conn.ConnInfo.DataTypeForName("jsonb"); !ok {
|
||||
t.Skip("Skipping due to no jsonb type")
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscode(t, "jsonb", []interface{}{
|
||||
&pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present},
|
||||
&pgtype.JSONB{Bytes: []byte("null"), Status: pgtype.Present},
|
||||
&pgtype.JSONB{Bytes: []byte("42"), Status: pgtype.Present},
|
||||
&pgtype.JSONB{Bytes: []byte(`"hello"`), Status: pgtype.Present},
|
||||
&pgtype.JSONB{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
|
||||
func TestJSONBSet(t *testing.T) {
|
||||
successfulTests := []struct {
|
||||
source interface{}
|
||||
result pgtype.JSONB
|
||||
}{
|
||||
{source: "{}", result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}},
|
||||
{source: []byte("{}"), result: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}},
|
||||
{source: ([]byte)(nil), result: pgtype.JSONB{Status: pgtype.Null}},
|
||||
{source: (*string)(nil), result: pgtype.JSONB{Status: pgtype.Null}},
|
||||
{source: []int{1, 2, 3}, result: pgtype.JSONB{Bytes: []byte("[1,2,3]"), Status: pgtype.Present}},
|
||||
{source: map[string]interface{}{"foo": "bar"}, result: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}},
|
||||
}
|
||||
|
||||
for i, tt := range successfulTests {
|
||||
var d pgtype.JSONB
|
||||
err := d.Set(tt.source)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(d, tt.result) {
|
||||
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONBAssignTo(t *testing.T) {
|
||||
var s string
|
||||
var ps *string
|
||||
var b []byte
|
||||
|
||||
rawStringTests := []struct {
|
||||
src pgtype.JSONB
|
||||
dst *string
|
||||
expected string
|
||||
}{
|
||||
{src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &s, expected: "{}"},
|
||||
}
|
||||
|
||||
for i, tt := range rawStringTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if *tt.dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
|
||||
rawBytesTests := []struct {
|
||||
src pgtype.JSONB
|
||||
dst *[]byte
|
||||
expected []byte
|
||||
}{
|
||||
{src: pgtype.JSONB{Bytes: []byte("{}"), Status: pgtype.Present}, dst: &b, expected: []byte("{}")},
|
||||
{src: pgtype.JSONB{Status: pgtype.Null}, dst: &b, expected: (([]byte)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range rawBytesTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if bytes.Compare(tt.expected, *tt.dst) != 0 {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
|
||||
var mapDst map[string]interface{}
|
||||
type structDst struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
var strDst structDst
|
||||
|
||||
unmarshalTests := []struct {
|
||||
src pgtype.JSONB
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
}{
|
||||
{src: pgtype.JSONB{Bytes: []byte(`{"foo":"bar"}`), Status: pgtype.Present}, dst: &mapDst, expected: map[string]interface{}{"foo": "bar"}},
|
||||
{src: pgtype.JSONB{Bytes: []byte(`{"name":"John","age":42}`), Status: pgtype.Present}, dst: &strDst, expected: structDst{Name: "John", Age: 42}},
|
||||
}
|
||||
for i, tt := range unmarshalTests {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
pointerAllocTests := []struct {
|
||||
src pgtype.JSONB
|
||||
dst **string
|
||||
expected *string
|
||||
}{
|
||||
{src: pgtype.JSONB{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))},
|
||||
}
|
||||
|
||||
for i, tt := range pointerAllocTests {
|
||||
err := tt.src.AssignTo(tt.dst)
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", i, err)
|
||||
}
|
||||
|
||||
if *tt.dst != tt.expected {
|
||||
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
-149
@@ -1,149 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Line struct {
|
||||
A, B, C float64
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Line) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Line", src)
|
||||
}
|
||||
|
||||
func (dst *Line) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Line) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Line) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Line{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) < 7 {
|
||||
return errors.Errorf("invalid length for Line: %v", len(src))
|
||||
}
|
||||
|
||||
parts := strings.SplitN(string(src[1:len(src)-1]), ",", 3)
|
||||
if len(parts) < 3 {
|
||||
return errors.Errorf("invalid format for line")
|
||||
}
|
||||
|
||||
a, err := strconv.ParseFloat(parts[0], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := strconv.ParseFloat(parts[1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := strconv.ParseFloat(parts[2], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Line{A: a, B: b, C: c, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Line) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Line{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 24 {
|
||||
return errors.Errorf("invalid length for Line: %v", len(src))
|
||||
}
|
||||
|
||||
a := binary.BigEndian.Uint64(src)
|
||||
b := binary.BigEndian.Uint64(src[8:])
|
||||
c := binary.BigEndian.Uint64(src[16:])
|
||||
|
||||
*dst = Line{
|
||||
A: math.Float64frombits(a),
|
||||
B: math.Float64frombits(b),
|
||||
C: math.Float64frombits(c),
|
||||
Status: Present,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Line) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, fmt.Sprintf(`{%s,%s,%s}`,
|
||||
strconv.FormatFloat(src.A, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.B, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.C, 'f', -1, 64),
|
||||
)...)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Line) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.A))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.B))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.C))
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Line) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Line{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Line) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestLineTranscode(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
if _, ok := conn.ConnInfo.DataTypeForName("line"); !ok {
|
||||
t.Skip("Skipping due to no line type")
|
||||
}
|
||||
|
||||
// line may exist but not be usable on 9.3 :(
|
||||
var isPG93 bool
|
||||
err := conn.QueryRow(context.Background(), "select version() ~ '9.3'").Scan(&isPG93)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if isPG93 {
|
||||
t.Skip("Skipping due to unimplemented line type in PG 9.3")
|
||||
}
|
||||
|
||||
testutil.TestSuccessfulTranscode(t, "line", []interface{}{
|
||||
&pgtype.Line{
|
||||
A: 1.23, B: 4.56, C: 7.89012345,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Line{
|
||||
A: -1.23, B: -4.56, C: -7.89,
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Line{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
-166
@@ -1,166 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jackc/pgio"
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Lseg struct {
|
||||
P [2]Vec2
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Lseg) Set(src interface{}) error {
|
||||
return errors.Errorf("cannot convert %v to Lseg", src)
|
||||
}
|
||||
|
||||
func (dst *Lseg) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Lseg) AssignTo(dst interface{}) error {
|
||||
return errors.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Lseg) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Lseg{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) < 11 {
|
||||
return errors.Errorf("invalid length for Lseg: %v", len(src))
|
||||
}
|
||||
|
||||
str := string(src[2:])
|
||||
|
||||
var end int
|
||||
end = strings.IndexByte(str, ',')
|
||||
|
||||
x1, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+1:]
|
||||
end = strings.IndexByte(str, ')')
|
||||
|
||||
y1, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+3:]
|
||||
end = strings.IndexByte(str, ',')
|
||||
|
||||
x2, err := strconv.ParseFloat(str[:end], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
str = str[end+1 : len(str)-2]
|
||||
|
||||
y2, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Lseg{P: [2]Vec2{{x1, y1}, {x2, y2}}, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Lseg) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Lseg{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 32 {
|
||||
return errors.Errorf("invalid length for Lseg: %v", len(src))
|
||||
}
|
||||
|
||||
x1 := binary.BigEndian.Uint64(src)
|
||||
y1 := binary.BigEndian.Uint64(src[8:])
|
||||
x2 := binary.BigEndian.Uint64(src[16:])
|
||||
y2 := binary.BigEndian.Uint64(src[24:])
|
||||
|
||||
*dst = Lseg{
|
||||
P: [2]Vec2{
|
||||
{math.Float64frombits(x1), math.Float64frombits(y1)},
|
||||
{math.Float64frombits(x2), math.Float64frombits(y2)},
|
||||
},
|
||||
Status: Present,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Lseg) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = append(buf, fmt.Sprintf(`(%s,%s),(%s,%s)`,
|
||||
strconv.FormatFloat(src.P[0].X, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[0].Y, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[1].X, 'f', -1, 64),
|
||||
strconv.FormatFloat(src.P[1].Y, 'f', -1, 64),
|
||||
)...)
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (src *Lseg) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].X))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[0].Y))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].X))
|
||||
buf = pgio.AppendUint64(buf, math.Float64bits(src.P[1].Y))
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Lseg) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Lseg{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Lseg) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgtype/testutil"
|
||||
)
|
||||
|
||||
func TestLsegTranscode(t *testing.T) {
|
||||
testutil.TestSuccessfulTranscode(t, "lseg", []interface{}{
|
||||
&pgtype.Lseg{
|
||||
P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Lseg{
|
||||
P: [2]pgtype.Vec2{{7.1, 1.678}, {-13.14, -5.234}},
|
||||
Status: pgtype.Present,
|
||||
},
|
||||
&pgtype.Lseg{Status: pgtype.Null},
|
||||
})
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"net"
|
||||
|
||||
errors "golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Macaddr struct {
|
||||
Addr net.HardwareAddr
|
||||
Status Status
|
||||
}
|
||||
|
||||
func (dst *Macaddr) Set(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Macaddr{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := src.(type) {
|
||||
case net.HardwareAddr:
|
||||
addr := make(net.HardwareAddr, len(value))
|
||||
copy(addr, value)
|
||||
*dst = Macaddr{Addr: addr, Status: Present}
|
||||
case string:
|
||||
addr, err := net.ParseMAC(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*dst = Macaddr{Addr: addr, Status: Present}
|
||||
default:
|
||||
if originalSrc, ok := underlyingPtrType(src); ok {
|
||||
return dst.Set(originalSrc)
|
||||
}
|
||||
return errors.Errorf("cannot convert %v to Macaddr", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Macaddr) Get() interface{} {
|
||||
switch dst.Status {
|
||||
case Present:
|
||||
return dst.Addr
|
||||
case Null:
|
||||
return nil
|
||||
default:
|
||||
return dst.Status
|
||||
}
|
||||
}
|
||||
|
||||
func (src *Macaddr) AssignTo(dst interface{}) error {
|
||||
switch src.Status {
|
||||
case Present:
|
||||
switch v := dst.(type) {
|
||||
case *net.HardwareAddr:
|
||||
*v = make(net.HardwareAddr, len(src.Addr))
|
||||
copy(*v, src.Addr)
|
||||
return nil
|
||||
case *string:
|
||||
*v = src.Addr.String()
|
||||
return nil
|
||||
default:
|
||||
if nextDst, retry := GetAssignToDstType(dst); retry {
|
||||
return src.AssignTo(nextDst)
|
||||
}
|
||||
return errors.Errorf("unable to assign to %T", dst)
|
||||
}
|
||||
case Null:
|
||||
return NullAssignTo(dst)
|
||||
}
|
||||
|
||||
return errors.Errorf("cannot decode %#v into %T", src, dst)
|
||||
}
|
||||
|
||||
func (dst *Macaddr) DecodeText(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Macaddr{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
addr, err := net.ParseMAC(string(src))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*dst = Macaddr{Addr: addr, Status: Present}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dst *Macaddr) DecodeBinary(ci *ConnInfo, src []byte) error {
|
||||
if src == nil {
|
||||
*dst = Macaddr{Status: Null}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(src) != 6 {
|
||||
return errors.Errorf("Received an invalid size for a macaddr: %d", len(src))
|
||||
}
|
||||
|
||||
addr := make(net.HardwareAddr, 6)
|
||||
copy(addr, src)
|
||||
|
||||
*dst = Macaddr{Addr: addr, Status: Present}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (src *Macaddr) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.Addr.String()...), nil
|
||||
}
|
||||
|
||||
// EncodeBinary encodes src into w.
|
||||
func (src *Macaddr) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
|
||||
switch src.Status {
|
||||
case Null:
|
||||
return nil, nil
|
||||
case Undefined:
|
||||
return nil, errUndefined
|
||||
}
|
||||
|
||||
return append(buf, src.Addr...), nil
|
||||
}
|
||||
|
||||
// Scan implements the database/sql Scanner interface.
|
||||
func (dst *Macaddr) Scan(src interface{}) error {
|
||||
if src == nil {
|
||||
*dst = Macaddr{Status: Null}
|
||||
return 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 errors.Errorf("cannot scan %T", src)
|
||||
}
|
||||
|
||||
// Value implements the database/sql/driver Valuer interface.
|
||||
func (src *Macaddr) Value() (driver.Value, error) {
|
||||
return EncodeValueText(src)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user