Split pgtype into own repo
This commit is contained in:
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/jackc/pgconn"
|
"github.com/jackc/pgconn"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
errors "golang.org/x/xerrors"
|
errors "golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -6,8 +6,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jackc/pgconn"
|
"github.com/jackc/pgconn"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConnBeginBatch(t *testing.T) {
|
func TestConnBeginBatch(t *testing.T) {
|
||||||
|
|||||||
+1
-1
@@ -9,8 +9,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkPointerPointerWithNullValues(b *testing.B) {
|
func BenchmarkPointerPointerWithNullValues(b *testing.B) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jackc/pgconn"
|
"github.com/jackc/pgconn"
|
||||||
"github.com/jackc/pgproto3/v2"
|
"github.com/jackc/pgproto3/v2"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
+1
-1
@@ -9,8 +9,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgconn"
|
"github.com/jackc/pgconn"
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
errors "golang.org/x/xerrors"
|
errors "golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/jackc/pgtype"
|
||||||
"github.com/jackc/pgx/v4"
|
"github.com/jackc/pgx/v4"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
|
||||||
errors "golang.org/x/xerrors"
|
errors "golang.org/x/xerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/jackc/pgio"
|
"github.com/jackc/pgio"
|
||||||
"github.com/jackc/pgproto3"
|
"github.com/jackc/pgproto3"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newFastpath(cn *Conn) *fastpath {
|
func newFastpath(cn *Conn) *fastpath {
|
||||||
|
|||||||
@@ -8,18 +8,15 @@ require (
|
|||||||
github.com/jackc/pgio v1.0.0
|
github.com/jackc/pgio v1.0.0
|
||||||
github.com/jackc/pgproto3 v1.1.0
|
github.com/jackc/pgproto3 v1.1.0
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db
|
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/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b
|
||||||
github.com/kr/pretty v0.1.0 // indirect
|
github.com/lib/pq v1.1.0
|
||||||
github.com/lib/pq v1.0.0
|
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/rs/zerolog v1.13.0
|
github.com/rs/zerolog v1.13.0
|
||||||
github.com/satori/go.uuid v1.2.0
|
github.com/satori/go.uuid v1.2.0
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
|
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
|
||||||
github.com/sirupsen/logrus v1.4.1
|
github.com/sirupsen/logrus v1.4.1
|
||||||
github.com/stretchr/testify v1.3.0
|
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
|
go.uber.org/zap v1.9.1
|
||||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373
|
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 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 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/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 h1:cIcUpcEP55F/QuZWEtXyqHoWk+IV4TBiLjtBkeq/Q1c=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
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=
|
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/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 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
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 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"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
|
// LargeObjects is a structure used to access the large objects API. It is only
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgio"
|
"github.com/jackc/pgio"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
|||||||
errors "golang.org/x/xerrors"
|
errors "golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/jackc/pgproto3/v2"
|
"github.com/jackc/pgproto3/v2"
|
||||||
"github.com/jackc/pgx/v4/pgtype"
|
"github.com/jackc/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
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