Split pgtype into own repo

This commit is contained in:
Jack Christensen 2019-04-20 19:20:00 -05:00
parent cc3461e65d
commit 1b8f0016e9
169 changed files with 23 additions and 25502 deletions

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/jackc/pgconn"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
errors "golang.org/x/xerrors"
)

View File

@ -6,8 +6,8 @@ import (
"testing"
"github.com/jackc/pgconn"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgtype"
)
func TestConnBeginBatch(t *testing.T) {

View File

@ -9,8 +9,8 @@ import (
"testing"
"time"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgtype"
)
func BenchmarkPointerPointerWithNullValues(b *testing.B) {

View File

@ -13,7 +13,7 @@ import (
"github.com/jackc/pgconn"
"github.com/jackc/pgproto3/v2"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
)
const (

View File

@ -9,8 +9,8 @@ import (
"time"
"github.com/jackc/pgconn"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgtype"
"github.com/stretchr/testify/require"
errors "golang.org/x/xerrors"
)

View File

@ -7,8 +7,8 @@ import (
"regexp"
"strconv"
"github.com/jackc/pgtype"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgtype"
errors "golang.org/x/xerrors"
)

View File

@ -5,7 +5,7 @@ import (
"github.com/jackc/pgio"
"github.com/jackc/pgproto3"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
)
func newFastpath(cn *Conn) *fastpath {

7
go.mod
View File

@ -8,18 +8,15 @@ require (
github.com/jackc/pgio v1.0.0
github.com/jackc/pgproto3 v1.1.0
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.0.0
github.com/lib/pq v1.1.0
github.com/pkg/errors v0.8.1
github.com/rs/zerolog v1.13.0
github.com/satori/go.uuid v1.2.0
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.3.0
go.uber.org/atomic v1.3.2 // indirect
go.uber.org/multierr v1.1.0 // indirect
go.uber.org/zap v1.9.1
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

5
go.sum
View File

@ -16,6 +16,10 @@ github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db h1:UpaKn/gYxzH6/zWyRQH1S260zvKqwJJ4h8+Kf09ooh0=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0 h1:mX93v750WifMD1htCt7vqeolcnpaG1gz8URVGjSzcUM=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgx v3.3.0+incompatible h1:Wa90/+qsITBAPkAZjiByeIGHFcj3Ztu+VzrrIpHjL90=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b h1:cIcUpcEP55F/QuZWEtXyqHoWk+IV4TBiLjtBkeq/Q1c=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@ -27,6 +31,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=

View File

@ -4,7 +4,7 @@ import (
"context"
"io"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
)
// LargeObjects is a structure used to access the large objects API. It is only

View File

@ -7,7 +7,7 @@ import (
"time"
"github.com/jackc/pgio"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
)
const (

View File

@ -8,7 +8,7 @@ import (
errors "golang.org/x/xerrors"
"github.com/jackc/pgproto3/v2"
"github.com/jackc/pgx/v4/pgtype"
"github.com/jackc/pgtype"
)
type Server struct {

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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},
},
})
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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,
},
},
})
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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,
},
})
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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},
})
}

View File

@ -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(""),
}
}

View File

@ -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
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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
})
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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},
},
})
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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},
},
})
}

View File

@ -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)
}

View File

@ -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},
},
})
}

View File

@ -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
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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},
})
}

View File

@ -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)
}

View File

@ -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},
})
}

View File

@ -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