Removed TextEncoder and BinaryEncoder

Restructured / fixed a lot of tests along the way.
query-exec-mode
Jack Christensen 2022-01-22 12:07:35 -06:00
parent eb0a4c9626
commit 3a90c6c879
50 changed files with 295 additions and 758 deletions

View File

@ -71,40 +71,12 @@ func (eqb *extendedQueryBuilder) encodeExtendedParamValue(ci *pgtype.ConnInfo, o
eqb.paramValueBytes = make([]byte, 0, 128)
}
var err error
var buf []byte
pos := len(eqb.paramValueBytes)
if arg, ok := arg.(string); ok {
return []byte(arg), nil
}
if formatCode == TextFormatCode {
if arg, ok := arg.(pgtype.TextEncoder); ok {
buf, err = arg.EncodeText(ci, eqb.paramValueBytes)
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
eqb.paramValueBytes = buf
return eqb.paramValueBytes[pos:], nil
}
} else if formatCode == BinaryFormatCode {
if arg, ok := arg.(pgtype.BinaryEncoder); ok {
buf, err = arg.EncodeBinary(ci, eqb.paramValueBytes)
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
eqb.paramValueBytes = buf
return eqb.paramValueBytes[pos:], nil
}
}
if argIsPtr {
// We have already checked that arg is not pointing to nil,
// so it is safe to dereference here.
@ -143,28 +115,6 @@ func (eqb *extendedQueryBuilder) encodeExtendedParamValue(ci *pgtype.ConnInfo, o
}
}
// There is no data type registered for the destination OID, but maybe there is data type registered for the arg
// type. If so use it's text encoder (if available).
if dt, ok := ci.DataTypeForValue(arg); ok {
value := dt.Value
if textEncoder, ok := value.(pgtype.TextEncoder); ok {
err := value.Set(arg)
if err != nil {
return nil, err
}
buf, err = textEncoder.EncodeText(ci, eqb.paramValueBytes)
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
eqb.paramValueBytes = buf
return eqb.paramValueBytes[pos:], nil
}
}
if strippedArg, ok := stripNamedType(&refVal); ok {
return eqb.encodeExtendedParamValue(ci, oid, formatCode, strippedArg)
}

View File

@ -2,15 +2,11 @@ package pgx
import (
"database/sql/driver"
"github.com/jackc/pgx/v5/pgtype"
)
func convertDriverValuers(args []interface{}) ([]interface{}, error) {
for i, arg := range args {
switch arg := arg.(type) {
case pgtype.BinaryEncoder:
case pgtype.TextEncoder:
case driver.Valuer:
v, err := callValuerValue(arg)
if err != nil {

View File

@ -17,7 +17,7 @@ func isExpectedEqBits(a interface{}) func(interface{}) bool {
}
func TestBitsCodecBit(t *testing.T) {
testPgxCodec(t, "bit(40)", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "bit(40)", []testutil.TranscodeTestCase{
{
pgtype.Bits{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true},
new(pgtype.Bits),
@ -34,7 +34,7 @@ func TestBitsCodecBit(t *testing.T) {
}
func TestBitsCodecVarbit(t *testing.T) {
testPgxCodec(t, "varbit", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "varbit", []testutil.TranscodeTestCase{
{
pgtype.Bits{Bytes: []byte{}, Len: 0, Valid: true},
new(pgtype.Bits),
@ -54,12 +54,3 @@ func TestBitsCodecVarbit(t *testing.T) {
{nil, new(pgtype.Bits), isExpectedEqBits(pgtype.Bits{})},
})
}
func TestBitsNormalize(t *testing.T) {
testutil.TestSuccessfulNormalize(t, []testutil.NormalizeTest{
{
SQL: "select B'111111111'",
Value: &pgtype.Bits{Bytes: []byte{255, 128}, Len: 9, Valid: true},
},
})
}

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestBoolCodec(t *testing.T) {
testPgxCodec(t, "bool", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "bool", []testutil.TranscodeTestCase{
{true, new(bool), isExpectedEq(true)},
{false, new(bool), isExpectedEq(false)},
{true, new(pgtype.Bool), isExpectedEq(pgtype.Bool{Bool: true, Valid: true})},

View File

@ -8,7 +8,7 @@ import (
)
func TestBoxCodec(t *testing.T) {
testPgxCodec(t, "box", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "box", []testutil.TranscodeTestCase{
{
pgtype.Box{
P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},
@ -35,15 +35,3 @@ func TestBoxCodec(t *testing.T) {
{nil, new(pgtype.Box), isExpectedEq(pgtype.Box{})},
})
}
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}},
Valid: true,
},
},
})
}

View File

@ -28,7 +28,7 @@ func isExpectedEqBytes(a interface{}) func(interface{}) bool {
}
func TestByteaCodec(t *testing.T) {
testPgxCodec(t, "bytea", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "bytea", []testutil.TranscodeTestCase{
{[]byte{1, 2, 3}, new([]byte), isExpectedEqBytes([]byte{1, 2, 3})},
{[]byte{}, new([]byte), isExpectedEqBytes([]byte{})},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestCircleTranscode(t *testing.T) {
testPgxCodec(t, "circle", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "circle", []testutil.TranscodeTestCase{
{
pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true},
new(pgtype.Circle),

View File

@ -5,6 +5,7 @@ import (
"time"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqTime(a interface{}) func(interface{}) bool {
@ -17,7 +18,7 @@ func isExpectedEqTime(a interface{}) func(interface{}) bool {
}
func TestDateCodec(t *testing.T) {
testPgxCodec(t, "date", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "date", []testutil.TranscodeTestCase{
{time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC))},

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestFloat4Codec(t *testing.T) {
testPgxCodec(t, "float4", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "float4", []testutil.TranscodeTestCase{
{pgtype.Float4{Float: -1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float: -1, Valid: true})},
{pgtype.Float4{Float: 0, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float: 0, Valid: true})},
{pgtype.Float4{Float: 1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float: 1, Valid: true})},

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestFloat8Codec(t *testing.T) {
testPgxCodec(t, "float8", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "float8", []testutil.TranscodeTestCase{
{pgtype.Float8{Float: -1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float: -1, Valid: true})},
{pgtype.Float8{Float: 0, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float: 0, Valid: true})},
{pgtype.Float8{Float: 1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float: 1, Valid: true})},

View File

@ -75,7 +75,7 @@ func TestHstoreCodec(t *testing.T) {
return &s
}
tests := []PgxTranscodeTestCase{
tests := []testutil.TranscodeTestCase{
{
map[string]string{},
new(map[string]string),
@ -134,25 +134,25 @@ func TestHstoreCodec(t *testing.T) {
// Special key values
// at beginning
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{s + "foo": "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{s + "foo": "bar"}),
})
// in middle
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo" + s + "bar": "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo" + s + "bar": "bar"}),
})
// at end
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo" + s: "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo" + s: "bar"}),
})
// is key
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{s: "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{s: "bar"}),
@ -161,25 +161,25 @@ func TestHstoreCodec(t *testing.T) {
// Special value values
// at beginning
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo": s + "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": s + "bar"}),
})
// in middle
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo": "foo" + s + "bar"},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s + "bar"}),
})
// at end
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo": "foo" + s},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s}),
})
// is key
tests = append(tests, PgxTranscodeTestCase{
tests = append(tests, testutil.TranscodeTestCase{
map[string]string{"foo": s},
new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": s}),
@ -187,6 +187,6 @@ func TestHstoreCodec(t *testing.T) {
}
for _, format := range formats {
testPgxCodecFormat(t, "hstore", tests, conn, format.name, format.code)
testutil.RunTranscodeTestsFormat(t, "hstore", tests, conn, format.name, format.code)
}
}

View File

@ -5,6 +5,7 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqIPNet(a interface{}) func(interface{}) bool {
@ -17,7 +18,7 @@ func isExpectedEqIPNet(a interface{}) func(interface{}) bool {
}
func TestInetTranscode(t *testing.T) {
testPgxCodec(t, "inet", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "inet", []testutil.TranscodeTestCase{
{mustParseInet(t, "0.0.0.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "0.0.0.0/32"))},
{mustParseInet(t, "127.0.0.1/8"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "127.0.0.1/8"))},
{mustParseInet(t, "12.34.56.65/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "12.34.56.65/32"))},
@ -34,7 +35,7 @@ func TestInetTranscode(t *testing.T) {
}
func TestCidrTranscode(t *testing.T) {
testPgxCodec(t, "cidr", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "cidr", []testutil.TranscodeTestCase{
{mustParseInet(t, "0.0.0.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "0.0.0.0/32"))},
{mustParseInet(t, "127.0.0.1/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "127.0.0.1/32"))},
{mustParseInet(t, "12.34.56.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "12.34.56.0/32"))},

View File

@ -6,10 +6,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestInt2Codec(t *testing.T) {
testPgxCodec(t, "int2", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "int2", []testutil.TranscodeTestCase{
{int8(1), new(int16), isExpectedEq(int16(1))},
{int16(1), new(int16), isExpectedEq(int16(1))},
{int32(1), new(int16), isExpectedEq(int16(1))},
@ -89,7 +90,7 @@ func TestInt2UnmarshalJSON(t *testing.T) {
}
func TestInt4Codec(t *testing.T) {
testPgxCodec(t, "int4", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "int4", []testutil.TranscodeTestCase{
{int8(1), new(int32), isExpectedEq(int32(1))},
{int16(1), new(int32), isExpectedEq(int32(1))},
{int32(1), new(int32), isExpectedEq(int32(1))},
@ -169,7 +170,7 @@ func TestInt4UnmarshalJSON(t *testing.T) {
}
func TestInt8Codec(t *testing.T) {
testPgxCodec(t, "int8", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "int8", []testutil.TranscodeTestCase{
{int8(1), new(int64), isExpectedEq(int64(1))},
{int16(1), new(int64), isExpectedEq(int64(1))},
{int32(1), new(int64), isExpectedEq(int64(1))},

View File

@ -10,7 +10,7 @@ import (
<% [2, 4, 8].each do |pg_byte_size| %>
<% pg_bit_size = pg_byte_size * 8 %>
func TestInt<%= pg_byte_size %>Codec(t *testing.T) {
testPgxCodec(t, "int<%= pg_byte_size %>", []PgxTranscodeTestCase{
testPgxCodec(t, "int<%= pg_byte_size %>", []testutil.TranscodeTestCase{
{int8(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},
{int16(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},
{int32(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},

View File

@ -5,10 +5,11 @@ import (
"time"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestIntervalCodec(t *testing.T) {
testPgxCodec(t, "interval", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "interval", []testutil.TranscodeTestCase{
{
pgtype.Interval{Microseconds: 1, Valid: true},
new(pgtype.Interval),

View File

@ -2,6 +2,8 @@ package pgtype_test
import (
"testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqMap(a interface{}) func(interface{}) bool {
@ -37,7 +39,7 @@ func TestJSONCodec(t *testing.T) {
Age int `json:"age"`
}
testPgxCodec(t, "json", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "json", []testutil.TranscodeTestCase{
{[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))},
{[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},

View File

@ -2,6 +2,8 @@ package pgtype_test
import (
"testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestJSONBTranscode(t *testing.T) {
@ -10,7 +12,7 @@ func TestJSONBTranscode(t *testing.T) {
Age int `json:"age"`
}
testPgxCodec(t, "jsonb", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "jsonb", []testutil.TranscodeTestCase{
{[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))},
{[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},

View File

@ -25,7 +25,7 @@ func TestLineTranscode(t *testing.T) {
t.Skip("Skipping due to unimplemented line type in PG 9.3")
}
testPgxCodec(t, "line", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "line", []testutil.TranscodeTestCase{
{
pgtype.Line{
A: 1.23, B: 4.56, C: 7.89012345,

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestLsegTranscode(t *testing.T) {
testPgxCodec(t, "lseg", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "lseg", []testutil.TranscodeTestCase{
{
pgtype.Lseg{
P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},

View File

@ -4,6 +4,8 @@ import (
"bytes"
"net"
"testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
@ -24,7 +26,7 @@ func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
}
func TestMacaddrCodec(t *testing.T) {
testPgxCodec(t, "macaddr", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "macaddr", []testutil.TranscodeTestCase{
{
mustParseMacaddr(t, "01:23:45:67:89:ab"),
new(net.HardwareAddr),

View File

@ -74,7 +74,7 @@ func TestNumericCodec(t *testing.T) {
max.Add(max, big.NewInt(1))
longestNumeric := pgtype.Numeric{Int: max, Exp: -16383, Valid: true}
testPgxCodec(t, "numeric", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "numeric", []testutil.TranscodeTestCase{
{mustParseNumeric(t, "1"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "1"))},
{mustParseNumeric(t, "3.14159"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "3.14159"))},
{mustParseNumeric(t, "100010001"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "100010001"))},
@ -122,22 +122,22 @@ func TestNumericCodecFuzz(t *testing.T) {
max := &big.Int{}
max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
tests := make([]PgxTranscodeTestCase, 0, 2000)
tests := make([]testutil.TranscodeTestCase, 0, 2000)
for i := 0; i < 10; i++ {
for j := -50; j < 50; j++ {
num := (&big.Int{}).Rand(r, max)
n := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}
tests = append(tests, PgxTranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
tests = append(tests, testutil.TranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
negNum := &big.Int{}
negNum.Neg(num)
n = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}
tests = append(tests, PgxTranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
tests = append(tests, testutil.TranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
}
}
testPgxCodec(t, "numeric", tests)
testutil.RunTranscodeTests(t, "numeric", tests)
}
func TestNumericMarshalJSON(t *testing.T) {

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqPath(a interface{}) func(interface{}) bool {
@ -26,7 +27,7 @@ func isExpectedEqPath(a interface{}) func(interface{}) bool {
}
func TestPathTranscode(t *testing.T) {
testPgxCodec(t, "path", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "path", []testutil.TranscodeTestCase{
{
pgtype.Path{
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}},

View File

@ -186,26 +186,6 @@ type TextDecoder interface {
DecodeText(ci *ConnInfo, src []byte) error
}
// BinaryEncoder is implemented by types that can encode themselves into the
// PostgreSQL binary wire format.
type BinaryEncoder interface {
// EncodeBinary should append the binary format of self to buf. If self is the
// SQL value NULL then append nothing and return (nil, nil). The caller of
// EncodeBinary is responsible for writing the correct NULL value or the
// length of the data written.
EncodeBinary(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
}
// TextEncoder is implemented by types that can encode themselves into the
// PostgreSQL text wire format.
type TextEncoder interface {
// EncodeText should append the text format of self to buf. If self is the
// SQL value NULL then append nothing and return (nil, nil). The caller of
// EncodeText is responsible for writing the correct NULL value or the
// length of the data written.
EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error)
}
type nullAssignmentError struct {
dst interface{}
}
@ -400,8 +380,6 @@ func (ci *ConnInfo) RegisterDataType(t DataType) {
var formatCode int16
if t.Codec != nil {
formatCode = t.Codec.PreferredFormat()
} else if _, ok := t.Value.(BinaryEncoder); ok {
formatCode = BinaryFormatCode
}
ci.oidToFormatCode[t.OID] = formatCode
}

View File

@ -2,17 +2,13 @@ package pgtype_test
import (
"bytes"
"context"
"database/sql"
"errors"
"fmt"
"net"
"reflect"
"testing"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
_ "github.com/jackc/pgx/v5/stdlib"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -292,54 +288,8 @@ func BenchmarkScanPlanScanInt4IntoGoInt32(b *testing.B) {
}
}
type PgxTranscodeTestCase struct {
src interface{}
dst interface{}
test func(interface{}) bool
}
func isExpectedEq(a interface{}) func(interface{}) bool {
return func(v interface{}) bool {
return a == v
}
}
func testPgxCodec(t testing.TB, pgTypeName string, tests []PgxTranscodeTestCase) {
conn := testutil.MustConnectPgx(t)
defer testutil.MustCloseContext(t, conn)
formats := []struct {
name string
code int16
}{
{name: "TextFormat", code: pgx.TextFormatCode},
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
}
for _, format := range formats {
testPgxCodecFormat(t, pgTypeName, tests, conn, format.name, format.code)
}
}
func testPgxCodecFormat(t testing.TB, pgTypeName string, tests []PgxTranscodeTestCase, conn *pgx.Conn, formatName string, formatCode int16) {
_, err := conn.Prepare(context.Background(), "test", fmt.Sprintf("select $1::%s", pgTypeName))
if err != nil {
t.Fatal(err)
}
for i, tt := range tests {
err := conn.QueryRow(context.Background(), "test", pgx.QueryResultFormats{formatCode}, tt.src).Scan(tt.dst)
if err != nil {
t.Errorf("%s %d: %v", formatName, i, err)
}
dst := reflect.ValueOf(tt.dst)
if dst.Kind() == reflect.Ptr {
dst = dst.Elem()
}
if !tt.test(dst.Interface()) {
t.Errorf("%s %d: unexpected result for %v: %v", formatName, i, tt.src, dst.Interface())
}
}
}

View File

@ -5,11 +5,12 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/stretchr/testify/require"
)
func TestPointCodec(t *testing.T) {
testPgxCodec(t, "point", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "point", []testutil.TranscodeTestCase{
{
pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true},
new(pgtype.Point),

View File

@ -4,6 +4,7 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func isExpectedEqPolygon(a interface{}) func(interface{}) bool {
@ -26,7 +27,7 @@ func isExpectedEqPolygon(a interface{}) func(interface{}) bool {
}
func TestPolygonTranscode(t *testing.T) {
testPgxCodec(t, "polygon", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "polygon", []testutil.TranscodeTestCase{
{
pgtype.Polygon{
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}},

View File

@ -3,16 +3,18 @@ package pgtype_test
import (
"math"
"testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestQcharTranscode(t *testing.T) {
var tests []PgxTranscodeTestCase
var tests []testutil.TranscodeTestCase
for i := 0; i <= math.MaxUint8; i++ {
tests = append(tests, PgxTranscodeTestCase{rune(i), new(rune), isExpectedEq(rune(i))})
tests = append(tests, PgxTranscodeTestCase{byte(i), new(byte), isExpectedEq(byte(i))})
tests = append(tests, testutil.TranscodeTestCase{rune(i), new(rune), isExpectedEq(rune(i))})
tests = append(tests, testutil.TranscodeTestCase{byte(i), new(byte), isExpectedEq(byte(i))})
}
tests = append(tests, PgxTranscodeTestCase{nil, new(*rune), isExpectedEq((*rune)(nil))})
tests = append(tests, PgxTranscodeTestCase{nil, new(*byte), isExpectedEq((*byte)(nil))})
tests = append(tests, testutil.TranscodeTestCase{nil, new(*rune), isExpectedEq((*rune)(nil))})
tests = append(tests, testutil.TranscodeTestCase{nil, new(*byte), isExpectedEq((*byte)(nil))})
testPgxCodec(t, `"char"`, tests)
testutil.RunTranscodeTests(t, `"char"`, tests)
}

View File

@ -2,34 +2,15 @@ package testutil
import (
"context"
"database/sql"
"fmt"
"os"
"reflect"
"testing"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
_ "github.com/jackc/pgx/v5/stdlib"
)
func MustConnectDatabaseSQL(t testing.TB, driverName string) *sql.DB {
var sqlDriverName string
switch driverName {
case "github.com/jackc/pgx/stdlib":
sqlDriverName = "pgx"
default:
t.Fatalf("Unknown driver %v", driverName)
}
db, err := sql.Open(sqlDriverName, os.Getenv("PGX_TEST_DATABASE"))
if err != nil {
t.Fatal(err)
}
return db
}
func MustConnectPgx(t testing.TB) *pgx.Conn {
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
if err != nil {
@ -57,369 +38,48 @@ func MustCloseContext(t testing.TB, conn interface {
}
}
type forceTextEncoder struct {
e pgtype.TextEncoder
type TranscodeTestCase struct {
Src interface{}
Dst interface{}
Test func(interface{}) bool
}
func (f forceTextEncoder) EncodeText(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
return f.e.EncodeText(ci, buf)
}
type forceBinaryEncoder struct {
e pgtype.BinaryEncoder
}
func (f forceBinaryEncoder) EncodeBinary(ci *pgtype.ConnInfo, buf []byte) ([]byte, error) {
return f.e.EncodeBinary(ci, buf)
}
func ForceEncoder(e interface{}, formatCode int16) interface{} {
switch formatCode {
case pgx.TextFormatCode:
if e, ok := e.(pgtype.TextEncoder); ok {
return forceTextEncoder{e: e}
}
case pgx.BinaryFormatCode:
if e, ok := e.(pgtype.BinaryEncoder); ok {
return forceBinaryEncoder{e: e.(pgtype.BinaryEncoder)}
}
}
return nil
}
func TestSuccessfulTranscode(t testing.TB, pgTypeName string, values []interface{}) {
TestSuccessfulTranscodeEqFunc(t, pgTypeName, values, func(a, b interface{}) bool {
return reflect.DeepEqual(a, b)
})
}
func TestSuccessfulTranscodeEqFunc(t testing.TB, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) {
TestPgxSuccessfulTranscodeEqFunc(t, pgTypeName, values, eqFunc)
TestDatabaseSQLSuccessfulTranscodeEqFunc(t, "github.com/jackc/pgx/stdlib", pgTypeName, values, eqFunc)
}
func TestPgxSuccessfulTranscodeEqFunc(t testing.TB, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) {
func RunTranscodeTests(t testing.TB, pgTypeName string, tests []TranscodeTestCase) {
conn := MustConnectPgx(t)
defer MustCloseContext(t, conn)
formats := []struct {
name string
code int16
}{
{name: "TextFormat", code: pgx.TextFormatCode},
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
}
for _, format := range formats {
RunTranscodeTestsFormat(t, pgTypeName, tests, conn, format.name, format.code)
}
}
func RunTranscodeTestsFormat(t testing.TB, pgTypeName string, tests []TranscodeTestCase, conn *pgx.Conn, formatName string, formatCode int16) {
_, err := conn.Prepare(context.Background(), "test", fmt.Sprintf("select $1::%s", pgTypeName))
if err != nil {
t.Fatal(err)
}
formats := []struct {
name string
formatCode int16
}{
{name: "TextFormat", formatCode: pgx.TextFormatCode},
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
}
for i, v := range values {
for _, paramFormat := range formats {
for _, resultFormat := range formats {
vEncoder := ForceEncoder(v, paramFormat.formatCode)
if vEncoder == nil {
t.Logf("Skipping Param %s Result %s: %#v does not implement %v for encoding", paramFormat.name, resultFormat.name, v, paramFormat.name)
continue
}
switch resultFormat.formatCode {
case pgx.TextFormatCode:
if _, ok := v.(pgtype.TextEncoder); !ok {
t.Logf("Skipping Param %s Result %s: %#v does not implement %v for decoding", paramFormat.name, resultFormat.name, v, resultFormat.name)
continue
}
case pgx.BinaryFormatCode:
if _, ok := v.(pgtype.BinaryEncoder); !ok {
t.Logf("Skipping Param %s Result %s: %#v does not implement %v for decoding", paramFormat.name, resultFormat.name, v, resultFormat.name)
continue
}
}
// Derefence value if it is a pointer
derefV := v
refVal := reflect.ValueOf(v)
if refVal.Kind() == reflect.Ptr {
derefV = refVal.Elem().Interface()
}
result := reflect.New(reflect.TypeOf(derefV))
err := conn.QueryRow(context.Background(), "test", pgx.QueryResultFormats{resultFormat.formatCode}, vEncoder).Scan(result.Interface())
if err != nil {
t.Errorf("Param %s Result %s %d: %v", paramFormat.name, resultFormat.name, i, err)
}
if !eqFunc(result.Elem().Interface(), derefV) {
t.Errorf("Param %s Result %s %d: expected %v, got %v", paramFormat.name, resultFormat.name, i, derefV, result.Elem().Interface())
}
}
}
}
}
func TestDatabaseSQLSuccessfulTranscodeEqFunc(t testing.TB, driverName, pgTypeName string, values []interface{}, eqFunc func(a, b interface{}) bool) {
conn := MustConnectDatabaseSQL(t, driverName)
defer MustClose(t, conn)
ps, err := conn.Prepare(fmt.Sprintf("select $1::%s", pgTypeName))
if err != nil {
t.Fatal(err)
}
for i, v := range values {
// Derefence value if it is a pointer
derefV := v
refVal := reflect.ValueOf(v)
if refVal.Kind() == reflect.Ptr {
derefV = refVal.Elem().Interface()
}
result := reflect.New(reflect.TypeOf(derefV))
err := ps.QueryRow(v).Scan(result.Interface())
if err != nil {
t.Errorf("%v %d: %v", driverName, i, err)
}
if !eqFunc(result.Elem().Interface(), derefV) {
t.Errorf("%v %d: expected %v, got %v", driverName, i, derefV, result.Elem().Interface())
}
}
}
type NormalizeTest struct {
SQL string
Value interface{}
}
func TestSuccessfulNormalize(t testing.TB, tests []NormalizeTest) {
TestSuccessfulNormalizeEqFunc(t, tests, func(a, b interface{}) bool {
return reflect.DeepEqual(a, b)
})
}
func TestSuccessfulNormalizeEqFunc(t testing.TB, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) {
TestPgxSuccessfulNormalizeEqFunc(t, tests, eqFunc)
TestDatabaseSQLSuccessfulNormalizeEqFunc(t, "github.com/jackc/pgx/stdlib", tests, eqFunc)
}
func TestPgxSuccessfulNormalizeEqFunc(t testing.TB, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) {
conn := MustConnectPgx(t)
defer MustCloseContext(t, conn)
formats := []struct {
name string
formatCode int16
}{
{name: "TextFormat", formatCode: pgx.TextFormatCode},
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
}
for i, tt := range tests {
for _, fc := range formats {
psName := fmt.Sprintf("test%d", i)
_, err := conn.Prepare(context.Background(), psName, tt.SQL)
if err != nil {
t.Fatal(err)
}
queryResultFormats := pgx.QueryResultFormats{fc.formatCode}
if ForceEncoder(tt.Value, fc.formatCode) == nil {
t.Logf("Skipping: %#v does not implement %v", tt.Value, fc.name)
continue
}
// Derefence value if it is a pointer
derefV := tt.Value
refVal := reflect.ValueOf(tt.Value)
if refVal.Kind() == reflect.Ptr {
derefV = refVal.Elem().Interface()
}
result := reflect.New(reflect.TypeOf(derefV))
err = conn.QueryRow(context.Background(), psName, queryResultFormats).Scan(result.Interface())
if err != nil {
t.Errorf("%v %d: %v", fc.name, i, err)
}
if !eqFunc(result.Elem().Interface(), derefV) {
t.Errorf("%v %d: expected %v, got %v", fc.name, i, derefV, result.Elem().Interface())
}
}
}
}
func TestDatabaseSQLSuccessfulNormalizeEqFunc(t testing.TB, driverName string, tests []NormalizeTest, eqFunc func(a, b interface{}) bool) {
conn := MustConnectDatabaseSQL(t, driverName)
defer MustClose(t, conn)
for i, tt := range tests {
ps, err := conn.Prepare(tt.SQL)
err := conn.QueryRow(context.Background(), "test", pgx.QueryResultFormats{formatCode}, tt.Src).Scan(tt.Dst)
if err != nil {
t.Errorf("%d. %v", i, err)
continue
t.Errorf("%s %d: %v", formatName, i, err)
}
// Derefence value if it is a pointer
derefV := tt.Value
refVal := reflect.ValueOf(tt.Value)
if refVal.Kind() == reflect.Ptr {
derefV = refVal.Elem().Interface()
dst := reflect.ValueOf(tt.Dst)
if dst.Kind() == reflect.Ptr {
dst = dst.Elem()
}
result := reflect.New(reflect.TypeOf(derefV))
err = ps.QueryRow().Scan(result.Interface())
if err != nil {
t.Errorf("%v %d: %v", driverName, i, err)
}
if !eqFunc(result.Elem().Interface(), derefV) {
t.Errorf("%v %d: expected %v, got %v", driverName, i, derefV, result.Elem().Interface())
if !tt.Test(dst.Interface()) {
t.Errorf("%s %d: unexpected result for %v: %v", formatName, i, tt.Src, dst.Interface())
}
}
}
func TestGoZeroToNullConversion(t testing.TB, pgTypeName string, zero interface{}) {
TestPgxGoZeroToNullConversion(t, pgTypeName, zero)
TestDatabaseSQLGoZeroToNullConversion(t, "github.com/jackc/pgx/stdlib", pgTypeName, zero)
}
func TestNullToGoZeroConversion(t testing.TB, pgTypeName string, zero interface{}) {
TestPgxNullToGoZeroConversion(t, pgTypeName, zero)
TestDatabaseSQLNullToGoZeroConversion(t, "github.com/jackc/pgx/stdlib", pgTypeName, zero)
}
func TestPgxGoZeroToNullConversion(t testing.TB, pgTypeName string, zero interface{}) {
conn := MustConnectPgx(t)
defer MustCloseContext(t, conn)
_, err := conn.Prepare(context.Background(), "test", fmt.Sprintf("select $1::%s is null", pgTypeName))
if err != nil {
t.Fatal(err)
}
formats := []struct {
name string
formatCode int16
}{
{name: "TextFormat", formatCode: pgx.TextFormatCode},
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
}
for _, paramFormat := range formats {
vEncoder := ForceEncoder(zero, paramFormat.formatCode)
if vEncoder == nil {
t.Logf("Skipping Param %s: %#v does not implement %v for encoding", paramFormat.name, zero, paramFormat.name)
continue
}
var result bool
err := conn.QueryRow(context.Background(), "test", vEncoder).Scan(&result)
if err != nil {
t.Errorf("Param %s: %v", paramFormat.name, err)
}
if !result {
t.Errorf("Param %s: did not convert zero to null", paramFormat.name)
}
}
}
func TestPgxNullToGoZeroConversion(t testing.TB, pgTypeName string, zero interface{}) {
conn := MustConnectPgx(t)
defer MustCloseContext(t, conn)
_, err := conn.Prepare(context.Background(), "test", fmt.Sprintf("select null::%s", pgTypeName))
if err != nil {
t.Fatal(err)
}
formats := []struct {
name string
formatCode int16
}{
{name: "TextFormat", formatCode: pgx.TextFormatCode},
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
}
for _, resultFormat := range formats {
switch resultFormat.formatCode {
case pgx.TextFormatCode:
if _, ok := zero.(pgtype.TextEncoder); !ok {
t.Logf("Skipping Result %s: %#v does not implement %v for decoding", resultFormat.name, zero, resultFormat.name)
continue
}
case pgx.BinaryFormatCode:
if _, ok := zero.(pgtype.BinaryEncoder); !ok {
t.Logf("Skipping Result %s: %#v does not implement %v for decoding", resultFormat.name, zero, resultFormat.name)
continue
}
}
// Derefence value if it is a pointer
derefZero := zero
refVal := reflect.ValueOf(zero)
if refVal.Kind() == reflect.Ptr {
derefZero = refVal.Elem().Interface()
}
result := reflect.New(reflect.TypeOf(derefZero))
err := conn.QueryRow(context.Background(), "test").Scan(result.Interface())
if err != nil {
t.Errorf("Result %s: %v", resultFormat.name, err)
}
if !reflect.DeepEqual(result.Elem().Interface(), derefZero) {
t.Errorf("Result %s: did not convert null to zero", resultFormat.name)
}
}
}
func TestDatabaseSQLGoZeroToNullConversion(t testing.TB, driverName, pgTypeName string, zero interface{}) {
conn := MustConnectDatabaseSQL(t, driverName)
defer MustClose(t, conn)
ps, err := conn.Prepare(fmt.Sprintf("select $1::%s is null", pgTypeName))
if err != nil {
t.Fatal(err)
}
var result bool
err = ps.QueryRow(zero).Scan(&result)
if err != nil {
t.Errorf("%v %v", driverName, err)
}
if !result {
t.Errorf("%v: did not convert zero to null", driverName)
}
}
func TestDatabaseSQLNullToGoZeroConversion(t testing.TB, driverName, pgTypeName string, zero interface{}) {
conn := MustConnectDatabaseSQL(t, driverName)
defer MustClose(t, conn)
ps, err := conn.Prepare(fmt.Sprintf("select null::%s", pgTypeName))
if err != nil {
t.Fatal(err)
}
// Derefence value if it is a pointer
derefZero := zero
refVal := reflect.ValueOf(zero)
if refVal.Kind() == reflect.Ptr {
derefZero = refVal.Elem().Interface()
}
result := reflect.New(reflect.TypeOf(derefZero))
err = ps.QueryRow().Scan(result.Interface())
if err != nil {
t.Errorf("%v %v", driverName, err)
}
if !reflect.DeepEqual(result.Elem().Interface(), derefZero) {
t.Errorf("%s: did not convert null to zero", driverName)
}
}

View File

@ -17,7 +17,7 @@ func (someFmtStringer) String() string {
func TestTextCodec(t *testing.T) {
for _, pgTypeName := range []string{"text", "varchar"} {
testPgxCodec(t, pgTypeName, []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, pgTypeName, []testutil.TranscodeTestCase{
{
pgtype.Text{String: "", Valid: true},
new(pgtype.Text),
@ -47,7 +47,7 @@ func TestTextCodec(t *testing.T) {
//
// So this is simply a smoke test of the name type.
func TestTextCodecName(t *testing.T) {
testPgxCodec(t, "name", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "name", []testutil.TranscodeTestCase{
{
pgtype.Text{String: "", Valid: true},
new(pgtype.Text),
@ -65,7 +65,7 @@ func TestTextCodecName(t *testing.T) {
// Test fixed length char types like char(3)
func TestTextCodecBPChar(t *testing.T) {
testPgxCodec(t, "char(3)", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "char(3)", []testutil.TranscodeTestCase{
{
pgtype.Text{String: "a ", Valid: true},
new(pgtype.Text),
@ -95,7 +95,7 @@ func TestTextCodecACLItem(t *testing.T) {
conn := testutil.MustConnectPgx(t)
defer testutil.MustCloseContext(t, conn)
testPgxCodecFormat(t, "aclitem", []PgxTranscodeTestCase{
testutil.RunTranscodeTestsFormat(t, "aclitem", []testutil.TranscodeTestCase{
{
pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true},
new(pgtype.Text),
@ -123,7 +123,7 @@ func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
t.Skipf("Role with special characters does not exist.")
}
testPgxCodecFormat(t, "aclitem", []PgxTranscodeTestCase{
testutil.RunTranscodeTestsFormat(t, "aclitem", []testutil.TranscodeTestCase{
{
pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true},
new(pgtype.Text),

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestTIDCodec(t *testing.T) {
testPgxCodec(t, "tid", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "tid", []testutil.TranscodeTestCase{
{
pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true},
new(pgtype.TID),

View File

@ -5,10 +5,11 @@ import (
"time"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestTimeCodec(t *testing.T) {
testPgxCodec(t, "time", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "time", []testutil.TranscodeTestCase{
{
pgtype.Time{Microseconds: 0, Valid: true},
new(pgtype.Time),

View File

@ -11,7 +11,7 @@ import (
)
func TestTimestampCodec(t *testing.T) {
testPgxCodec(t, "timestamp", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "timestamp", []testutil.TranscodeTestCase{
{time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.UTC))},

View File

@ -11,7 +11,7 @@ import (
)
func TestTimestamptzCodec(t *testing.T) {
testPgxCodec(t, "timestamptz", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "timestamptz", []testutil.TranscodeTestCase{
{time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local))},
{time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(1970, 1, 1, 0, 0, 0, 0, time.Local))},
{time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local))},

View File

@ -4,10 +4,11 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
)
func TestUint32Codec(t *testing.T) {
testPgxCodec(t, "oid", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "oid", []testutil.TranscodeTestCase{
{
pgtype.Uint32{Uint: pgtype.TextOID, Valid: true},
new(pgtype.Uint32),

View File

@ -5,11 +5,12 @@ import (
"testing"
"github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/stretchr/testify/require"
)
func TestUUIDCodec(t *testing.T) {
testPgxCodec(t, "uuid", []PgxTranscodeTestCase{
testutil.RunTranscodeTests(t, "uuid", []testutil.TranscodeTestCase{
{
pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true},
new(pgtype.UUID),

View File

@ -8,6 +8,8 @@ import (
type Float8 float64
func (Float8) SkipUnderlyingTypePlan() {}
// ScanFloat64 implements the Float64Scanner interface.
func (f *Float8) ScanFloat64(n pgtype.Float8) error {
if !n.Valid {

View File

@ -7,17 +7,28 @@ import (
"github.com/jackc/pgx/v5/pgtype/zeronull"
)
func isExpectedEq(a interface{}) func(interface{}) bool {
return func(v interface{}) bool {
return a == v
}
}
func TestFloat8Transcode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "float8", []interface{}{
(zeronull.Float8)(1),
(zeronull.Float8)(0),
testutil.RunTranscodeTests(t, "float8", []testutil.TranscodeTestCase{
{
(zeronull.Float8)(1),
new(zeronull.Float8),
isExpectedEq((zeronull.Float8)(1)),
},
{
nil,
new(zeronull.Float8),
isExpectedEq((zeronull.Float8)(0)),
},
{
(zeronull.Float8)(0),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestFloat8ConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "float8", (zeronull.Float8)(0))
}
func TestFloat8ConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "float8", (zeronull.Float8)(0))
}

View File

@ -11,6 +11,8 @@ import (
type Int2 int16
func (Int2) SkipUnderlyingTypePlan() {}
// ScanInt64 implements the Int64Scanner interface.
func (dst *Int2) ScanInt64(n int64, valid bool) error {
if !valid {
@ -57,6 +59,8 @@ func (src Int2) Value() (driver.Value, error) {
type Int4 int32
func (Int4) SkipUnderlyingTypePlan() {}
// ScanInt64 implements the Int64Scanner interface.
func (dst *Int4) ScanInt64(n int64, valid bool) error {
if !valid {
@ -103,6 +107,8 @@ func (src Int4) Value() (driver.Value, error) {
type Int8 int64
func (Int8) SkipUnderlyingTypePlan() {}
// ScanInt64 implements the Int64Scanner interface.
func (dst *Int8) ScanInt64(n int64, valid bool) error {
if !valid {

View File

@ -12,6 +12,8 @@ import (
<% pg_bit_size = pg_byte_size * 8 %>
type Int<%= pg_byte_size %> int<%= pg_bit_size %>
func (Int<%= pg_byte_size %>) SkipUnderlyingTypePlan() {}
// ScanInt64 implements the Int64Scanner interface.
func (dst *Int<%= pg_byte_size %>) ScanInt64(n int64, valid bool) error {
if !valid {

View File

@ -9,46 +9,61 @@ import (
)
func TestInt2Transcode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "int2", []interface{}{
(zeronull.Int2)(1),
(zeronull.Int2)(0),
testutil.RunTranscodeTests(t, "int2", []testutil.TranscodeTestCase{
{
(zeronull.Int2)(1),
new(zeronull.Int2),
isExpectedEq((zeronull.Int2)(1)),
},
{
nil,
new(zeronull.Int2),
isExpectedEq((zeronull.Int2)(0)),
},
{
(zeronull.Int2)(0),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestInt2ConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "int2", (zeronull.Int2)(0))
}
func TestInt2ConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "int2", (zeronull.Int2)(0))
}
func TestInt4Transcode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "int4", []interface{}{
(zeronull.Int4)(1),
(zeronull.Int4)(0),
testutil.RunTranscodeTests(t, "int4", []testutil.TranscodeTestCase{
{
(zeronull.Int4)(1),
new(zeronull.Int4),
isExpectedEq((zeronull.Int4)(1)),
},
{
nil,
new(zeronull.Int4),
isExpectedEq((zeronull.Int4)(0)),
},
{
(zeronull.Int4)(0),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestInt4ConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "int4", (zeronull.Int4)(0))
}
func TestInt4ConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "int4", (zeronull.Int4)(0))
}
func TestInt8Transcode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "int8", []interface{}{
(zeronull.Int8)(1),
(zeronull.Int8)(0),
testutil.RunTranscodeTests(t, "int8", []testutil.TranscodeTestCase{
{
(zeronull.Int8)(1),
new(zeronull.Int8),
isExpectedEq((zeronull.Int8)(1)),
},
{
nil,
new(zeronull.Int8),
isExpectedEq((zeronull.Int8)(0)),
},
{
(zeronull.Int8)(0),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestInt8ConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "int8", (zeronull.Int8)(0))
}
func TestInt8ConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "int8", (zeronull.Int8)(0))
}

View File

@ -10,17 +10,22 @@ import (
<% [2, 4, 8].each do |pg_byte_size| %>
<% pg_bit_size = pg_byte_size * 8 %>
func TestInt<%= pg_byte_size %>Transcode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "int<%= pg_byte_size %>", []interface{}{
(zeronull.Int<%= pg_byte_size %>)(1),
(zeronull.Int<%= pg_byte_size %>)(0),
testutil.RunTranscodeTests(t, "int<%= pg_byte_size %>", []testutil.TranscodeTestCase{
{
(zeronull.Int<%= pg_byte_size %>)(1),
new(zeronull.Int<%= pg_byte_size %>),
isExpectedEq((zeronull.Int<%= pg_byte_size %>)(1)),
},
{
nil,
new(zeronull.Int<%= pg_byte_size %>),
isExpectedEq((zeronull.Int<%= pg_byte_size %>)(0)),
},
{
(zeronull.Int<%= pg_byte_size %>)(0),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestInt<%= pg_byte_size %>ConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "int<%= pg_byte_size %>", (zeronull.Int<%= pg_byte_size %>)(0))
}
func TestInt<%= pg_byte_size %>ConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "int<%= pg_byte_size %>", (zeronull.Int<%= pg_byte_size %>)(0))
}
<% end %>

View File

@ -8,6 +8,8 @@ import (
type Text string
func (Text) SkipUnderlyingTypePlan() {}
// ScanText implements the TextScanner interface.
func (dst *Text) ScanText(v pgtype.Text) error {
if !v.Valid {

View File

@ -8,16 +8,21 @@ import (
)
func TestTextTranscode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "text", []interface{}{
(zeronull.Text)("foo"),
(zeronull.Text)(""),
testutil.RunTranscodeTests(t, "text", []testutil.TranscodeTestCase{
{
(zeronull.Text)("foo"),
new(zeronull.Text),
isExpectedEq((zeronull.Text)("foo")),
},
{
nil,
new(zeronull.Text),
isExpectedEq((zeronull.Text)("")),
},
{
(zeronull.Text)(""),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestTextConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "text", (zeronull.Text)(""))
}
func TestTextConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "text", (zeronull.Text)(""))
}

View File

@ -10,6 +10,8 @@ import (
type Timestamp time.Time
func (Timestamp) SkipUnderlyingTypePlan() {}
func (ts *Timestamp) ScanTimestamp(v pgtype.Timestamp) error {
if !v.Valid {
*ts = Timestamp{}

View File

@ -8,22 +8,31 @@ import (
"github.com/jackc/pgx/v5/pgtype/zeronull"
)
func TestTimestampTranscode(t *testing.T) {
testutil.TestSuccessfulTranscodeEqFunc(t, "timestamp", []interface{}{
(zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
(zeronull.Timestamp)(time.Time{}),
}, func(a, b interface{}) bool {
at := a.(zeronull.Timestamp)
bt := b.(zeronull.Timestamp)
func isExpectedEqTimestamp(a interface{}) func(interface{}) bool {
return func(v interface{}) bool {
at := time.Time(a.(zeronull.Timestamp))
vt := time.Time(v.(zeronull.Timestamp))
return time.Time(at).Equal(time.Time(bt))
return at.Equal(vt)
}
}
func TestTimestampTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "timestamp", []testutil.TranscodeTestCase{
{
(zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
new(zeronull.Timestamp),
isExpectedEqTimestamp((zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC))),
},
{
nil,
new(zeronull.Timestamp),
isExpectedEqTimestamp((zeronull.Timestamp)(time.Time{})),
},
{
(zeronull.Timestamp)(time.Time{}),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestTimestampConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "timestamp", (zeronull.Timestamp)(time.Time{}))
}
func TestTimestampConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "timestamp", (zeronull.Timestamp)(time.Time{}))
}

View File

@ -10,6 +10,8 @@ import (
type Timestamptz time.Time
func (Timestamptz) SkipUnderlyingTypePlan() {}
func (ts *Timestamptz) ScanTimestamptz(v pgtype.Timestamptz) error {
if !v.Valid {
*ts = Timestamptz{}

View File

@ -8,22 +8,31 @@ import (
"github.com/jackc/pgx/v5/pgtype/zeronull"
)
func TestTimestamptzTranscode(t *testing.T) {
testutil.TestSuccessfulTranscodeEqFunc(t, "timestamptz", []interface{}{
(zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
(zeronull.Timestamptz)(time.Time{}),
}, func(a, b interface{}) bool {
at := a.(zeronull.Timestamptz)
bt := b.(zeronull.Timestamptz)
func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool {
return func(v interface{}) bool {
at := time.Time(a.(zeronull.Timestamptz))
vt := time.Time(v.(zeronull.Timestamptz))
return time.Time(at).Equal(time.Time(bt))
return at.Equal(vt)
}
}
func TestTimestamptzTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "timestamptz", []testutil.TranscodeTestCase{
{
(zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
new(zeronull.Timestamptz),
isExpectedEqTimestamptz((zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC))),
},
{
nil,
new(zeronull.Timestamptz),
isExpectedEqTimestamptz((zeronull.Timestamptz)(time.Time{})),
},
{
(zeronull.Timestamptz)(time.Time{}),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestTimestamptzConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "timestamptz", (zeronull.Timestamptz)(time.Time{}))
}
func TestTimestamptzConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "timestamptz", (zeronull.Timestamptz)(time.Time{}))
}

View File

@ -8,6 +8,8 @@ import (
type UUID [16]byte
func (UUID) SkipUnderlyingTypePlan() {}
// ScanUUID implements the UUIDScanner interface.
func (u *UUID) ScanUUID(v pgtype.UUID) error {
if !v.Valid {

View File

@ -8,16 +8,21 @@ import (
)
func TestUUIDTranscode(t *testing.T) {
testutil.TestSuccessfulTranscode(t, "uuid", []interface{}{
(*zeronull.UUID)(&[16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
(*zeronull.UUID)(&[16]byte{}),
testutil.RunTranscodeTests(t, "uuid", []testutil.TranscodeTestCase{
{
(zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
new(zeronull.UUID),
isExpectedEq((zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15})),
},
{
nil,
new(zeronull.UUID),
isExpectedEq((zeronull.UUID)([16]byte{})),
},
{
(zeronull.UUID)([16]byte{}),
new(interface{}),
isExpectedEq(nil),
},
})
}
func TestUUIDConvertsGoZeroToNull(t *testing.T) {
testutil.TestGoZeroToNullConversion(t, "uuid", (*zeronull.UUID)(&[16]byte{}))
}
func TestUUIDConvertsNullToGoZero(t *testing.T) {
testutil.TestNullToGoZeroConversion(t, "uuid", (*zeronull.UUID)(&[16]byte{}))
}

View File

@ -37,15 +37,6 @@ func convertSimpleArgument(ci *pgtype.ConnInfo, arg interface{}) (interface{}, e
switch arg := arg.(type) {
case driver.Valuer:
return callValuerValue(arg)
case pgtype.TextEncoder:
buf, err := arg.EncodeText(ci, nil)
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return string(buf), nil
case float32:
return float64(arg), nil
case float64:
@ -89,21 +80,7 @@ func convertSimpleArgument(ci *pgtype.ConnInfo, arg interface{}) (interface{}, e
}
if dt, found := ci.DataTypeForValue(arg); found {
if dt.Value != nil {
v := dt.Value
err := v.Set(arg)
if err != nil {
return nil, err
}
buf, err := v.(pgtype.TextEncoder).EncodeText(ci, nil)
if err != nil {
return nil, err
}
if buf == nil {
return nil, nil
}
return string(buf), nil
} else if dt.Codec != nil {
if dt.Codec != nil {
buf, err := ci.Encode(0, TextFormatCode, arg, nil)
if err != nil {
return nil, err
@ -132,30 +109,6 @@ func encodePreparedStatementArgument(ci *pgtype.ConnInfo, buf []byte, oid uint32
}
switch arg := arg.(type) {
case pgtype.BinaryEncoder:
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
argBuf, err := arg.EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if argBuf != nil {
buf = argBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
return buf, nil
case pgtype.TextEncoder:
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
argBuf, err := arg.EncodeText(ci, buf)
if err != nil {
return nil, err
}
if argBuf != nil {
buf = argBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
return buf, nil
case string:
buf = pgio.AppendInt32(buf, int32(len(arg)))
buf = append(buf, arg...)
@ -173,35 +126,7 @@ func encodePreparedStatementArgument(ci *pgtype.ConnInfo, buf []byte, oid uint32
}
if dt, ok := ci.DataTypeForOID(oid); ok {
if dt.Value != nil {
value := dt.Value
err := value.Set(arg)
if err != nil {
{
if arg, ok := arg.(driver.Valuer); ok {
v, err := callValuerValue(arg)
if err != nil {
return nil, err
}
return encodePreparedStatementArgument(ci, buf, oid, v)
}
}
return nil, err
}
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
argBuf, err := value.(pgtype.BinaryEncoder).EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if argBuf != nil {
buf = argBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
return buf, nil
} else if dt.Codec != nil {
if dt.Codec != nil {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
argBuf, err := ci.Encode(oid, BinaryFormatCode, arg, buf)
@ -227,9 +152,7 @@ func encodePreparedStatementArgument(ci *pgtype.ConnInfo, buf []byte, oid uint32
// determination can be made.
func chooseParameterFormatCode(ci *pgtype.ConnInfo, oid uint32, arg interface{}) int16 {
switch arg.(type) {
case pgtype.BinaryEncoder:
return BinaryFormatCode
case string, *string, pgtype.TextEncoder:
case string, *string:
return TextFormatCode
}