pgtype uses pgxtest

Added ValueRoundTripTest to pgxtest
Removed pgtype/testutil

pgtype tests now run with all (applicable) query modes. This gives
better coverage than before and revealed several bugs which are also
fixed in this commit.
This commit is contained in:
Jack Christensen 2022-04-02 14:34:19 -05:00
parent 83e50f21e8
commit ee93440ac1
54 changed files with 2028 additions and 1954 deletions

35
conn.go
View File

@ -558,18 +558,41 @@ func (c *Conn) execSQLParams(ctx context.Context, sql string, args []interface{}
// Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is // Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is
// no way to safely use binary or to specify the parameter OIDs. // no way to safely use binary or to specify the parameter OIDs.
func (c *Conn) appendParamsForQueryExecModeExec(args []interface{}) error { func (c *Conn) appendParamsForQueryExecModeExec(args []interface{}) error {
for i := range args { for _, arg := range args {
if args[i] == nil { if arg == nil {
err := c.eqb.AppendParamFormat(c.typeMap, 0, TextFormatCode, args[i]) err := c.eqb.AppendParamFormat(c.typeMap, 0, TextFormatCode, arg)
if err != nil { if err != nil {
return err return err
} }
} else { } else {
dt, ok := c.TypeMap().TypeForValue(args[i]) dt, ok := c.TypeMap().TypeForValue(arg)
if !ok { if !ok {
return &unknownArgumentTypeQueryExecModeExecError{arg: args[i]} var tv pgtype.TextValuer
if tv, ok = arg.(pgtype.TextValuer); ok {
t, err := tv.TextValue()
if err != nil {
return err
} }
err := c.eqb.AppendParamFormat(c.typeMap, dt.OID, TextFormatCode, args[i])
dt, ok = c.TypeMap().TypeForOID(pgtype.TextOID)
if ok {
arg = t
}
}
}
if !ok {
var str fmt.Stringer
if str, ok = arg.(fmt.Stringer); ok {
dt, ok = c.TypeMap().TypeForOID(pgtype.TextOID)
if ok {
arg = str.String()
}
}
}
if !ok {
return &unknownArgumentTypeQueryExecModeExecError{arg: arg}
}
err := c.eqb.AppendParamFormat(c.typeMap, dt.OID, TextFormatCode, arg)
if err != nil { if err != nil {
return err return err
} }

View File

@ -4,15 +4,13 @@ import (
"context" "context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" pgx "github.com/jackc/pgx/v5"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestArrayCodec(t *testing.T) { func TestArrayCodec(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
for i, tt := range []struct { for i, tt := range []struct {
expected interface{} expected interface{}
}{ }{
@ -22,7 +20,7 @@ func TestArrayCodec(t *testing.T) {
} { } {
var actual []int16 var actual []int16
err := conn.QueryRow( err := conn.QueryRow(
context.Background(), ctx,
"select $1::smallint[]", "select $1::smallint[]",
tt.expected, tt.expected,
).Scan(&actual) ).Scan(&actual)
@ -39,19 +37,18 @@ func TestArrayCodec(t *testing.T) {
} { } {
var actual []*int16 var actual []*int16
err := conn.QueryRow( err := conn.QueryRow(
context.Background(), ctx,
"select $1::smallint[]", "select $1::smallint[]",
tt.expected, tt.expected,
).Scan(&actual) ).Scan(&actual)
assert.NoErrorf(t, err, "%d", i) assert.NoErrorf(t, err, "%d", i)
assert.Equalf(t, tt.expected, actual, "%d", i) assert.Equalf(t, tt.expected, actual, "%d", i)
} }
})
} }
func TestArrayCodecAnySlice(t *testing.T) { func TestArrayCodecAnySlice(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
type _int16Slice []int16 type _int16Slice []int16
for i, tt := range []struct { for i, tt := range []struct {
@ -63,19 +60,18 @@ func TestArrayCodecAnySlice(t *testing.T) {
} { } {
var actual _int16Slice var actual _int16Slice
err := conn.QueryRow( err := conn.QueryRow(
context.Background(), ctx,
"select $1::smallint[]", "select $1::smallint[]",
tt.expected, tt.expected,
).Scan(&actual) ).Scan(&actual)
assert.NoErrorf(t, err, "%d", i) assert.NoErrorf(t, err, "%d", i)
assert.Equalf(t, tt.expected, actual, "%d", i) assert.Equalf(t, tt.expected, actual, "%d", i)
} }
})
} }
func TestArrayCodecDecodeValue(t *testing.T) { func TestArrayCodecDecodeValue(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
for _, tt := range []struct { for _, tt := range []struct {
sql string sql string
expected interface{} expected interface{}
@ -94,7 +90,7 @@ func TestArrayCodecDecodeValue(t *testing.T) {
}, },
} { } {
t.Run(tt.sql, func(t *testing.T) { t.Run(tt.sql, func(t *testing.T) {
rows, err := conn.Query(context.Background(), tt.sql) rows, err := conn.Query(ctx, tt.sql)
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -107,15 +103,15 @@ func TestArrayCodecDecodeValue(t *testing.T) {
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
}) })
} }
})
} }
func TestArrayCodecScanMultipleDimensions(t *testing.T) { func TestArrayCodecScanMultipleDimensions(t *testing.T) {
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)") skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
rows, err := conn.Query(context.Background(), `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`) rows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -126,15 +122,14 @@ func TestArrayCodecScanMultipleDimensions(t *testing.T) {
} }
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
})
} }
func TestArrayCodecScanMultipleDimensionsEmpty(t *testing.T) { func TestArrayCodecScanMultipleDimensionsEmpty(t *testing.T) {
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)") skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn) rows, err := conn.Query(ctx, `select '{}'::int4[]`)
rows, err := conn.Query(context.Background(), `select '{}'::int4[]`)
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -145,15 +140,14 @@ func TestArrayCodecScanMultipleDimensionsEmpty(t *testing.T) {
} }
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
})
} }
func TestArrayCodecScanWrongMultipleDimensions(t *testing.T) { func TestArrayCodecScanWrongMultipleDimensions(t *testing.T) {
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)") skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn) rows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
rows, err := conn.Query(context.Background(), `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -161,15 +155,14 @@ func TestArrayCodecScanWrongMultipleDimensions(t *testing.T) {
err := rows.Scan(&ss) err := rows.Scan(&ss)
require.Error(t, err, "can't scan into dest[0]: PostgreSQL array has 2 dimensions but slice has 3 dimensions") require.Error(t, err, "can't scan into dest[0]: PostgreSQL array has 2 dimensions but slice has 3 dimensions")
} }
})
} }
func TestArrayCodecEncodeMultipleDimensions(t *testing.T) { func TestArrayCodecEncodeMultipleDimensions(t *testing.T) {
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)") skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn) rows, err := conn.Query(ctx, `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}})
rows, err := conn.Query(context.Background(), `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}})
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -180,15 +173,15 @@ func TestArrayCodecEncodeMultipleDimensions(t *testing.T) {
} }
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
})
} }
func TestArrayCodecEncodeMultipleDimensionsRagged(t *testing.T) { func TestArrayCodecEncodeMultipleDimensionsRagged(t *testing.T) {
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)") skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn) rows, err := conn.Query(ctx, `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5}, {9, 10, 11, 12}})
rows, err := conn.Query(context.Background(), `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5}, {9, 10, 11, 12}})
require.Error(t, err, "cannot convert [][]int32 to ArrayGetter because it is a ragged multi-dimensional") require.Error(t, err, "cannot convert [][]int32 to ArrayGetter because it is a ragged multi-dimensional")
defer rows.Close() defer rows.Close()
})
} }

View File

@ -2,10 +2,11 @@ package pgtype_test
import ( import (
"bytes" "bytes"
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqBits(a interface{}) func(interface{}) bool { func isExpectedEqBits(a interface{}) func(interface{}) bool {
@ -17,7 +18,7 @@ func isExpectedEqBits(a interface{}) func(interface{}) bool {
} }
func TestBitsCodecBit(t *testing.T) { func TestBitsCodecBit(t *testing.T) {
testutil.RunTranscodeTests(t, "bit(40)", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "bit(40)", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Bits{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true}, pgtype.Bits{Bytes: []byte{0, 0, 0, 0, 0}, Len: 40, Valid: true},
new(pgtype.Bits), new(pgtype.Bits),
@ -34,7 +35,7 @@ func TestBitsCodecBit(t *testing.T) {
} }
func TestBitsCodecVarbit(t *testing.T) { func TestBitsCodecVarbit(t *testing.T) {
testutil.RunTranscodeTests(t, "varbit", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "varbit", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Bits{Bytes: []byte{}, Len: 0, Valid: true}, pgtype.Bits{Bytes: []byte{}, Len: 0, Valid: true},
new(pgtype.Bits), new(pgtype.Bits),

View File

@ -1,14 +1,15 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestBoolCodec(t *testing.T) { func TestBoolCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "bool", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "bool", []pgxtest.ValueRoundTripTest{
{true, new(bool), isExpectedEq(true)}, {true, new(bool), isExpectedEq(true)},
{false, new(bool), isExpectedEq(false)}, {false, new(bool), isExpectedEq(false)},
{true, new(pgtype.Bool), isExpectedEq(pgtype.Bool{Bool: true, Valid: true})}, {true, new(pgtype.Bool), isExpectedEq(pgtype.Bool{Bool: true, Valid: true})},

View File

@ -1,16 +1,17 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestBoxCodec(t *testing.T) { func TestBoxCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support box type") skipCockroachDB(t, "Server does not support box type")
testutil.RunTranscodeTests(t, "box", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "box", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Box{ pgtype.Box{
P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}}, P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},

View File

@ -5,8 +5,9 @@ import (
"context" "context"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -28,7 +29,7 @@ func isExpectedEqBytes(a interface{}) func(interface{}) bool {
} }
func TestByteaCodec(t *testing.T) { func TestByteaCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "bytea", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "bytea", []pgxtest.ValueRoundTripTest{
{[]byte{1, 2, 3}, new([]byte), isExpectedEqBytes([]byte{1, 2, 3})}, {[]byte{1, 2, 3}, new([]byte), isExpectedEqBytes([]byte{1, 2, 3})},
{[]byte{}, new([]byte), isExpectedEqBytes([]byte{})}, {[]byte{}, new([]byte), isExpectedEqBytes([]byte{})},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))}, {[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
@ -37,22 +38,15 @@ func TestByteaCodec(t *testing.T) {
} }
func TestDriverBytesQueryRow(t *testing.T) { func TestDriverBytesQueryRow(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
ctx := context.Background()
var buf []byte var buf []byte
err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.DriverBytes)(&buf)) err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.DriverBytes)(&buf))
require.EqualError(t, err, "cannot scan into *pgtype.DriverBytes from QueryRow") require.EqualError(t, err, "cannot scan into *pgtype.DriverBytes from QueryRow")
})
} }
func TestDriverBytes(t *testing.T) { func TestDriverBytes(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
ctx := context.Background()
argBuf := make([]byte, 128) argBuf := make([]byte, 128)
for i := range argBuf { for i := range argBuf {
argBuf[i] = byte(i) argBuf[i] = byte(i)
@ -85,14 +79,11 @@ func TestDriverBytes(t *testing.T) {
err = rows.Err() err = rows.Err()
require.NoError(t, err) require.NoError(t, err)
})
} }
func TestPreallocBytes(t *testing.T) { func TestPreallocBytes(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
ctx := context.Background()
origBuf := []byte{5, 6, 7, 8} origBuf := []byte{5, 6, 7, 8}
buf := origBuf buf := origBuf
err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf)) err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf))
@ -110,18 +101,16 @@ func TestPreallocBytes(t *testing.T) {
require.Equal(t, 5, cap(buf)) require.Equal(t, 5, cap(buf))
require.Equal(t, []byte{1, 2, 7, 8}, origBuf) require.Equal(t, []byte{1, 2, 7, 8}, origBuf)
})
} }
func TestUndecodedBytes(t *testing.T) { func TestUndecodedBytes(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
ctx := context.Background()
var buf []byte var buf []byte
err := conn.QueryRow(ctx, `select 1::int4`).Scan((*pgtype.UndecodedBytes)(&buf)) err := conn.QueryRow(ctx, `select 1::int4`).Scan((*pgtype.UndecodedBytes)(&buf))
require.NoError(t, err) require.NoError(t, err)
require.Len(t, buf, 4) require.Len(t, buf, 4)
require.Equal(t, buf, []byte{0, 0, 0, 1}) require.Equal(t, buf, []byte{0, 0, 0, 1})
})
} }

View File

@ -1,16 +1,17 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestCircleTranscode(t *testing.T) { func TestCircleTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support box type") skipCockroachDB(t, "Server does not support box type")
testutil.RunTranscodeTests(t, "circle", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "circle", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true}, pgtype.Circle{P: pgtype.Vec2{1.234, 5.67890123}, R: 3.5, Valid: true},
new(pgtype.Circle), new(pgtype.Circle),

View File

@ -7,26 +7,24 @@ import (
pgx "github.com/jackc/pgx/v5" pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestCompositeCodecTranscode(t *testing.T) { func TestCompositeCodecTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)") skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists ct_test; _, err := conn.Exec(ctx, `drop type if exists ct_test;
create type ct_test as ( create type ct_test as (
a text, a text,
b int4 b int4
);`) );`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type ct_test") defer conn.Exec(ctx, "drop type ct_test")
dt, err := conn.LoadType(context.Background(), "ct_test") dt, err := conn.LoadType(ctx, "ct_test")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
@ -42,7 +40,7 @@ create type ct_test as (
var a string var a string
var b int32 var b int32
err := conn.QueryRow(context.Background(), "select $1::ct_test", pgx.QueryResultFormats{format.code}, err := conn.QueryRow(ctx, "select $1::ct_test", pgx.QueryResultFormats{format.code},
pgtype.CompositeFields{"hi", int32(42)}, pgtype.CompositeFields{"hi", int32(42)},
).Scan( ).Scan(
pgtype.CompositeFields{&a, &b}, pgtype.CompositeFields{&a, &b},
@ -51,6 +49,7 @@ create type ct_test as (
require.EqualValuesf(t, "hi", a, "%v", format.name) require.EqualValuesf(t, "hi", a, "%v", format.name)
require.EqualValuesf(t, 42, b, "%v", format.name) require.EqualValuesf(t, 42, b, "%v", format.name)
} }
})
} }
type point3d struct { type point3d struct {
@ -94,10 +93,9 @@ func (p *point3d) ScanIndex(i int) interface{} {
func TestCompositeCodecTranscodeStruct(t *testing.T) { func TestCompositeCodecTranscodeStruct(t *testing.T) {
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)") skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists point3d; _, err := conn.Exec(ctx, `drop type if exists point3d;
create type point3d as ( create type point3d as (
x float8, x float8,
@ -105,9 +103,9 @@ create type point3d as (
z float8 z float8
);`) );`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type point3d") defer conn.Exec(ctx, "drop type point3d")
dt, err := conn.LoadType(context.Background(), "point3d") dt, err := conn.LoadType(ctx, "point3d")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
@ -122,19 +120,19 @@ create type point3d as (
for _, format := range formats { for _, format := range formats {
input := point3d{X: 1, Y: 2, Z: 3} input := point3d{X: 1, Y: 2, Z: 3}
var output point3d var output point3d
err := conn.QueryRow(context.Background(), "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output) err := conn.QueryRow(ctx, "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
require.NoErrorf(t, err, "%v", format.name) require.NoErrorf(t, err, "%v", format.name)
require.Equalf(t, input, output, "%v", format.name) require.Equalf(t, input, output, "%v", format.name)
} }
})
} }
func TestCompositeCodecTranscodeStructWrapper(t *testing.T) { func TestCompositeCodecTranscodeStructWrapper(t *testing.T) {
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)") skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists point3d; _, err := conn.Exec(ctx, `drop type if exists point3d;
create type point3d as ( create type point3d as (
x float8, x float8,
@ -142,9 +140,9 @@ create type point3d as (
z float8 z float8
);`) );`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type point3d") defer conn.Exec(ctx, "drop type point3d")
dt, err := conn.LoadType(context.Background(), "point3d") dt, err := conn.LoadType(ctx, "point3d")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
@ -163,19 +161,19 @@ create type point3d as (
for _, format := range formats { for _, format := range formats {
input := anotherPoint{X: 1, Y: 2, Z: 3} input := anotherPoint{X: 1, Y: 2, Z: 3}
var output anotherPoint var output anotherPoint
err := conn.QueryRow(context.Background(), "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output) err := conn.QueryRow(ctx, "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
require.NoErrorf(t, err, "%v", format.name) require.NoErrorf(t, err, "%v", format.name)
require.Equalf(t, input, output, "%v", format.name) require.Equalf(t, input, output, "%v", format.name)
} }
})
} }
func TestCompositeCodecDecodeValue(t *testing.T) { func TestCompositeCodecDecodeValue(t *testing.T) {
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)") skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists point3d; _, err := conn.Exec(ctx, `drop type if exists point3d;
create type point3d as ( create type point3d as (
x float8, x float8,
@ -183,9 +181,9 @@ create type point3d as (
z float8 z float8
);`) );`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type point3d") defer conn.Exec(ctx, "drop type point3d")
dt, err := conn.LoadType(context.Background(), "point3d") dt, err := conn.LoadType(ctx, "point3d")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
@ -198,7 +196,7 @@ create type point3d as (
} }
for _, format := range formats { for _, format := range formats {
rows, err := conn.Query(context.Background(), "select '(1,2,3)'::point3d", pgx.QueryResultFormats{format.code}) rows, err := conn.Query(ctx, "select '(1,2,3)'::point3d", pgx.QueryResultFormats{format.code})
require.NoErrorf(t, err, "%v", format.name) require.NoErrorf(t, err, "%v", format.name)
require.True(t, rows.Next()) require.True(t, rows.Next())
values, err := rows.Values() values, err := rows.Values()
@ -208,4 +206,5 @@ create type point3d as (
require.False(t, rows.Next()) require.False(t, rows.Next())
require.NoErrorf(t, rows.Err(), "%v", format.name) require.NoErrorf(t, rows.Err(), "%v", format.name)
} }
})
} }

View File

@ -1,11 +1,12 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqTime(a interface{}) func(interface{}) bool { func isExpectedEqTime(a interface{}) func(interface{}) bool {
@ -18,7 +19,7 @@ func isExpectedEqTime(a interface{}) func(interface{}) bool {
} }
func TestDateCodec(t *testing.T) { func TestDateCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "date", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "date", []pgxtest.ValueRoundTripTest{
{time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC))}, {time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC))}, {time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))}, {time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))},

View File

@ -4,66 +4,66 @@ import (
"context" "context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" pgx "github.com/jackc/pgx/v5"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestEnumCodec(t *testing.T) { func TestEnumCodec(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists enum_test; _, err := conn.Exec(ctx, `drop type if exists enum_test;
create type enum_test as enum ('foo', 'bar', 'baz');`) create type enum_test as enum ('foo', 'bar', 'baz');`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type enum_test") defer conn.Exec(ctx, "drop type enum_test")
dt, err := conn.LoadType(context.Background(), "enum_test") dt, err := conn.LoadType(ctx, "enum_test")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
var s string var s string
err = conn.QueryRow(context.Background(), `select 'foo'::enum_test`).Scan(&s) err = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "foo", s) require.Equal(t, "foo", s)
err = conn.QueryRow(context.Background(), `select $1::enum_test`, "bar").Scan(&s) err = conn.QueryRow(ctx, `select $1::enum_test`, "bar").Scan(&s)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "bar", s) require.Equal(t, "bar", s)
err = conn.QueryRow(context.Background(), `select 'foo'::enum_test`).Scan(&s) err = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "foo", s) require.Equal(t, "foo", s)
err = conn.QueryRow(context.Background(), `select $1::enum_test`, "bar").Scan(&s) err = conn.QueryRow(ctx, `select $1::enum_test`, "bar").Scan(&s)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "bar", s) require.Equal(t, "bar", s)
err = conn.QueryRow(context.Background(), `select 'baz'::enum_test`).Scan(&s) err = conn.QueryRow(ctx, `select 'baz'::enum_test`).Scan(&s)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "baz", s) require.Equal(t, "baz", s)
})
} }
func TestEnumCodecValues(t *testing.T) { func TestEnumCodecValues(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
_, err := conn.Exec(context.Background(), `drop type if exists enum_test; _, err := conn.Exec(ctx, `drop type if exists enum_test;
create type enum_test as enum ('foo', 'bar', 'baz');`) create type enum_test as enum ('foo', 'bar', 'baz');`)
require.NoError(t, err) require.NoError(t, err)
defer conn.Exec(context.Background(), "drop type enum_test") defer conn.Exec(ctx, "drop type enum_test")
dt, err := conn.LoadType(context.Background(), "enum_test") dt, err := conn.LoadType(ctx, "enum_test")
require.NoError(t, err) require.NoError(t, err)
conn.TypeMap().RegisterType(dt) conn.TypeMap().RegisterType(dt)
rows, err := conn.Query(context.Background(), `select 'foo'::enum_test`) rows, err := conn.Query(ctx, `select 'foo'::enum_test`)
require.NoError(t, err) require.NoError(t, err)
require.True(t, rows.Next()) require.True(t, rows.Next())
values, err := rows.Values() values, err := rows.Values()
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, values, []interface{}{"foo"}) require.Equal(t, values, []interface{}{"foo"})
})
} }

View File

@ -1,14 +1,15 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestFloat4Codec(t *testing.T) { func TestFloat4Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "float4", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "float4", []pgxtest.ValueRoundTripTest{
{pgtype.Float4{Float32: -1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: -1, Valid: true})}, {pgtype.Float4{Float32: -1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: -1, Valid: true})},
{pgtype.Float4{Float32: 0, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: 0, Valid: true})}, {pgtype.Float4{Float32: 0, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: 0, Valid: true})},
{pgtype.Float4{Float32: 1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: 1, Valid: true})}, {pgtype.Float4{Float32: 1, Valid: true}, new(pgtype.Float4), isExpectedEq(pgtype.Float4{Float32: 1, Valid: true})},

View File

@ -1,14 +1,15 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestFloat8Codec(t *testing.T) { func TestFloat8Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "float8", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "float8", []pgxtest.ValueRoundTripTest{
{pgtype.Float8{Float64: -1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: -1, Valid: true})}, {pgtype.Float8{Float64: -1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: -1, Valid: true})},
{pgtype.Float8{Float64: 0, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: 0, Valid: true})}, {pgtype.Float8{Float64: 0, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: 0, Valid: true})},
{pgtype.Float8{Float64: 1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: 1, Valid: true})}, {pgtype.Float8{Float64: 1, Valid: true}, new(pgtype.Float8), isExpectedEq(pgtype.Float8{Float64: 1, Valid: true})},

View File

@ -6,7 +6,7 @@ import (
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqMapStringString(a interface{}) func(interface{}) bool { func isExpectedEqMapStringString(a interface{}) func(interface{}) bool {
@ -52,9 +52,8 @@ func isExpectedEqMapStringPointerString(a interface{}) func(interface{}) bool {
} }
func TestHstoreCodec(t *testing.T) { func TestHstoreCodec(t *testing.T) {
conn := testutil.MustConnectPgx(t) ctr := defaultConnTestRunner
defer testutil.MustCloseContext(t, conn) ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
var hstoreOID uint32 var hstoreOID uint32
err := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'hstore'`).Scan(&hstoreOID) err := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'hstore'`).Scan(&hstoreOID)
if err != nil { if err != nil {
@ -62,20 +61,13 @@ func TestHstoreCodec(t *testing.T) {
} }
conn.TypeMap().RegisterType(&pgtype.Type{Name: "hstore", OID: hstoreOID, Codec: pgtype.HstoreCodec{}}) conn.TypeMap().RegisterType(&pgtype.Type{Name: "hstore", OID: hstoreOID, Codec: pgtype.HstoreCodec{}})
formats := []struct {
name string
code int16
}{
{name: "TextFormat", code: pgx.TextFormatCode},
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
} }
fs := func(s string) *string { fs := func(s string) *string {
return &s return &s
} }
tests := []testutil.TranscodeTestCase{ tests := []pgxtest.ValueRoundTripTest{
{ {
map[string]string{}, map[string]string{},
new(map[string]string), new(map[string]string),
@ -134,25 +126,25 @@ func TestHstoreCodec(t *testing.T) {
// Special key values // Special key values
// at beginning // at beginning
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{s + "foo": "bar"}, map[string]string{s + "foo": "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{s + "foo": "bar"}), isExpectedEqMapStringString(map[string]string{s + "foo": "bar"}),
}) })
// in middle // in middle
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo" + s + "bar": "bar"}, map[string]string{"foo" + s + "bar": "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo" + s + "bar": "bar"}), isExpectedEqMapStringString(map[string]string{"foo" + s + "bar": "bar"}),
}) })
// at end // at end
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo" + s: "bar"}, map[string]string{"foo" + s: "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo" + s: "bar"}), isExpectedEqMapStringString(map[string]string{"foo" + s: "bar"}),
}) })
// is key // is key
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{s: "bar"}, map[string]string{s: "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{s: "bar"}), isExpectedEqMapStringString(map[string]string{s: "bar"}),
@ -161,32 +153,30 @@ func TestHstoreCodec(t *testing.T) {
// Special value values // Special value values
// at beginning // at beginning
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo": s + "bar"}, map[string]string{"foo": s + "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": s + "bar"}), isExpectedEqMapStringString(map[string]string{"foo": s + "bar"}),
}) })
// in middle // in middle
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo": "foo" + s + "bar"}, map[string]string{"foo": "foo" + s + "bar"},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s + "bar"}), isExpectedEqMapStringString(map[string]string{"foo": "foo" + s + "bar"}),
}) })
// at end // at end
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo": "foo" + s}, map[string]string{"foo": "foo" + s},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s}), isExpectedEqMapStringString(map[string]string{"foo": "foo" + s}),
}) })
// is key // is key
tests = append(tests, testutil.TranscodeTestCase{ tests = append(tests, pgxtest.ValueRoundTripTest{
map[string]string{"foo": s}, map[string]string{"foo": s},
new(map[string]string), new(map[string]string),
isExpectedEqMapStringString(map[string]string{"foo": s}), isExpectedEqMapStringString(map[string]string{"foo": s}),
}) })
} }
for _, format := range formats { pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, pgxtest.KnownOIDQueryExecModes, "hstore", tests)
testutil.RunTranscodeTestsFormat(t, "hstore", tests, conn, format.name, format.code)
}
} }

View File

@ -1,11 +1,12 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"net" "net"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqIPNet(a interface{}) func(interface{}) bool { func isExpectedEqIPNet(a interface{}) func(interface{}) bool {
@ -18,7 +19,7 @@ func isExpectedEqIPNet(a interface{}) func(interface{}) bool {
} }
func TestInetTranscode(t *testing.T) { func TestInetTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "inet", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "inet", []pgxtest.ValueRoundTripTest{
{mustParseInet(t, "0.0.0.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "0.0.0.0/32"))}, {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, "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"))}, {mustParseInet(t, "12.34.56.65/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "12.34.56.65/32"))},
@ -37,7 +38,7 @@ func TestInetTranscode(t *testing.T) {
func TestCidrTranscode(t *testing.T) { func TestCidrTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support cidr type (see https://github.com/cockroachdb/cockroach/issues/18846)") skipCockroachDB(t, "Server does not support cidr type (see https://github.com/cockroachdb/cockroach/issues/18846)")
testutil.RunTranscodeTests(t, "cidr", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "cidr", []pgxtest.ValueRoundTripTest{
{mustParseInet(t, "0.0.0.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "0.0.0.0/32"))}, {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, "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"))}, {mustParseInet(t, "12.34.56.0/32"), new(net.IPNet), isExpectedEqIPNet(mustParseInet(t, "12.34.56.0/32"))},

View File

@ -2,15 +2,16 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"math" "math"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestInt2Codec(t *testing.T) { func TestInt2Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "int2", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int2", []pgxtest.ValueRoundTripTest{
{int8(1), new(int16), isExpectedEq(int16(1))}, {int8(1), new(int16), isExpectedEq(int16(1))},
{int16(1), new(int16), isExpectedEq(int16(1))}, {int16(1), new(int16), isExpectedEq(int16(1))},
{int32(1), new(int16), isExpectedEq(int16(1))}, {int32(1), new(int16), isExpectedEq(int16(1))},
@ -91,7 +92,7 @@ func TestInt2UnmarshalJSON(t *testing.T) {
} }
func TestInt4Codec(t *testing.T) { func TestInt4Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "int4", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int4", []pgxtest.ValueRoundTripTest{
{int8(1), new(int32), isExpectedEq(int32(1))}, {int8(1), new(int32), isExpectedEq(int32(1))},
{int16(1), new(int32), isExpectedEq(int32(1))}, {int16(1), new(int32), isExpectedEq(int32(1))},
{int32(1), new(int32), isExpectedEq(int32(1))}, {int32(1), new(int32), isExpectedEq(int32(1))},
@ -172,7 +173,7 @@ func TestInt4UnmarshalJSON(t *testing.T) {
} }
func TestInt8Codec(t *testing.T) { func TestInt8Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "int8", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int8", []pgxtest.ValueRoundTripTest{
{int8(1), new(int64), isExpectedEq(int64(1))}, {int8(1), new(int64), isExpectedEq(int64(1))},
{int16(1), new(int64), isExpectedEq(int64(1))}, {int16(1), new(int64), isExpectedEq(int64(1))},
{int32(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| %> <% [2, 4, 8].each do |pg_byte_size| %>
<% pg_bit_size = pg_byte_size * 8 %> <% pg_bit_size = pg_byte_size * 8 %>
func TestInt<%= pg_byte_size %>Codec(t *testing.T) { func TestInt<%= pg_byte_size %>Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "int<%= pg_byte_size %>", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int<%= pg_byte_size %>", []pgxtest.ValueRoundTripTest{
{int8(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))}, {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))}, {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))}, {int32(1), new(int<%= pg_bit_size %>), isExpectedEq(int<%= pg_bit_size %>(1))},

File diff suppressed because it is too large Load Diff

View File

@ -18,14 +18,12 @@ import (
<% rows_columns.each do |rows, columns| %> <% rows_columns.each do |rows, columns| %>
<% [["Text", "pgx.TextFormatCode"], ["Binary", "pgx.BinaryFormatCode"]].each do |format_name, format_code| %> <% [["Text", "pgx.TextFormatCode"], ["Binary", "pgx.BinaryFormatCode"]].each do |format_name, format_code| %>
func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go_type.gsub(/\W/, "_") %>_<%= rows %>_rows_<%= columns %>_columns(b *testing.B) { func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go_type.gsub(/\W/, "_") %>_<%= rows %>_rows_<%= columns %>_columns(b *testing.B) {
conn := testutil.MustConnectPgx(b) defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(b, conn)
b.ResetTimer() b.ResetTimer()
var v [<%= columns %>]<%= go_type %> var v [<%= columns %>]<%= go_type %>
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := conn.QueryFunc( _, err := conn.QueryFunc(
context.Background(), ctx,
`select <% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>n::<%= pg_type %> + <%= col_idx%><% end %> from generate_series(1, <%= rows %>) n`, `select <% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>n::<%= pg_type %> + <%= col_idx%><% end %> from generate_series(1, <%= rows %>) n`,
[]interface{}{pgx.QueryResultFormats{<%= format_code %>}}, []interface{}{pgx.QueryResultFormats{<%= format_code %>}},
[]interface{}{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>}, []interface{}{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>},
@ -35,6 +33,7 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go
b.Fatal(err) b.Fatal(err)
} }
} }
})
} }
<% end %> <% end %>
<% end %> <% end %>
@ -44,14 +43,12 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go
<% [10, 100, 1000].each do |array_size| %> <% [10, 100, 1000].each do |array_size| %>
<% [["Text", "pgx.TextFormatCode"], ["Binary", "pgx.BinaryFormatCode"]].each do |format_name, format_code| %> <% [["Text", "pgx.TextFormatCode"], ["Binary", "pgx.BinaryFormatCode"]].each do |format_name, format_code| %>
func BenchmarkQuery<%= format_name %>FormatDecode_PG_Int4Array_With_Go_Int4Array_<%= array_size %>(b *testing.B) { func BenchmarkQuery<%= format_name %>FormatDecode_PG_Int4Array_With_Go_Int4Array_<%= array_size %>(b *testing.B) {
conn := testutil.MustConnectPgx(b) defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(b, conn)
b.ResetTimer() b.ResetTimer()
var v []int32 var v []int32
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := conn.QueryFunc( _, err := conn.QueryFunc(
context.Background(), ctx,
`select array_agg(n) from generate_series(1, <%= array_size %>) n`, `select array_agg(n) from generate_series(1, <%= array_size %>) n`,
[]interface{}{pgx.QueryResultFormats{<%= format_code %>}}, []interface{}{pgx.QueryResultFormats{<%= format_code %>}},
[]interface{}{&v}, []interface{}{&v},
@ -61,6 +58,7 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_Int4Array_With_Go_Int4Array
b.Fatal(err) b.Fatal(err)
} }
} }
})
} }
<% end %> <% end %>
<% end %> <% end %>

View File

@ -1,15 +1,16 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestIntervalCodec(t *testing.T) { func TestIntervalCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "interval", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "interval", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Interval{Microseconds: 1, Valid: true}, pgtype.Interval{Microseconds: 1, Valid: true},
new(pgtype.Interval), new(pgtype.Interval),

View File

@ -1,9 +1,10 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqMap(a interface{}) func(interface{}) bool { func isExpectedEqMap(a interface{}) func(interface{}) bool {
@ -39,7 +40,15 @@ func TestJSONCodec(t *testing.T) {
Age int `json:"age"` Age int `json:"age"`
} }
testutil.RunTranscodeTests(t, "json", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "json", []pgxtest.ValueRoundTripTest{
{nil, new(*jsonStruct), isExpectedEq((*jsonStruct)(nil))},
{map[string]interface{}(nil), new(*string), isExpectedEq((*string)(nil))},
{map[string]interface{}(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
})
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "json", []pgxtest.ValueRoundTripTest{
{[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))}, {[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))},
{[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))}, {[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))}, {[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},
@ -47,10 +56,5 @@ func TestJSONCodec(t *testing.T) {
{[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)}, {[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)},
{map[string]interface{}{"foo": "bar"}, new(map[string]interface{}), isExpectedEqMap(map[string]interface{}{"foo": "bar"})}, {map[string]interface{}{"foo": "bar"}, new(map[string]interface{}), isExpectedEqMap(map[string]interface{}{"foo": "bar"})},
{jsonStruct{Name: "Adam", Age: 10}, new(jsonStruct), isExpectedEq(jsonStruct{Name: "Adam", Age: 10})}, {jsonStruct{Name: "Adam", Age: 10}, new(jsonStruct), isExpectedEq(jsonStruct{Name: "Adam", Age: 10})},
{nil, new(*jsonStruct), isExpectedEq((*jsonStruct)(nil))},
{map[string]interface{}(nil), new(*string), isExpectedEq((*string)(nil))},
{map[string]interface{}(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
}) })
} }

View File

@ -1,9 +1,10 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestJSONBTranscode(t *testing.T) { func TestJSONBTranscode(t *testing.T) {
@ -12,7 +13,15 @@ func TestJSONBTranscode(t *testing.T) {
Age int `json:"age"` Age int `json:"age"`
} }
testutil.RunTranscodeTests(t, "jsonb", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "jsonb", []pgxtest.ValueRoundTripTest{
{nil, new(*jsonStruct), isExpectedEq((*jsonStruct)(nil))},
{map[string]interface{}(nil), new(*string), isExpectedEq((*string)(nil))},
{map[string]interface{}(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
})
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "jsonb", []pgxtest.ValueRoundTripTest{
{[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))}, {[]byte("{}"), new([]byte), isExpectedEqBytes([]byte("{}"))},
{[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))}, {[]byte("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))}, {[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},
@ -20,10 +29,5 @@ func TestJSONBTranscode(t *testing.T) {
{[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)}, {[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)},
{map[string]interface{}{"foo": "bar"}, new(map[string]interface{}), isExpectedEqMap(map[string]interface{}{"foo": "bar"})}, {map[string]interface{}{"foo": "bar"}, new(map[string]interface{}), isExpectedEqMap(map[string]interface{}{"foo": "bar"})},
{jsonStruct{Name: "Adam", Age: 10}, new(jsonStruct), isExpectedEq(jsonStruct{Name: "Adam", Age: 10})}, {jsonStruct{Name: "Adam", Age: 10}, new(jsonStruct), isExpectedEq(jsonStruct{Name: "Adam", Age: 10})},
{nil, new(*jsonStruct), isExpectedEq((*jsonStruct)(nil))},
{map[string]interface{}(nil), new(*string), isExpectedEq((*string)(nil))},
{map[string]interface{}(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
}) })
} }

View File

@ -4,15 +4,16 @@ import (
"context" "context"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestLineTranscode(t *testing.T) { func TestLineTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support type line") ctr := defaultConnTestRunner
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
pgxtest.SkipCockroachDB(t, conn, "Server does not support type line")
conn := testutil.MustConnectPgx(t)
defer conn.Close(context.Background())
if _, ok := conn.TypeMap().TypeForName("line"); !ok { if _, ok := conn.TypeMap().TypeForName("line"); !ok {
t.Skip("Skipping due to no line type") t.Skip("Skipping due to no line type")
} }
@ -26,8 +27,9 @@ func TestLineTranscode(t *testing.T) {
if isPG93 { if isPG93 {
t.Skip("Skipping due to unimplemented line type in PG 9.3") t.Skip("Skipping due to unimplemented line type in PG 9.3")
} }
}
testutil.RunTranscodeTests(t, "line", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "line", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Line{ pgtype.Line{
A: 1.23, B: 4.56, C: 7.89012345, A: 1.23, B: 4.56, C: 7.89012345,

View File

@ -1,16 +1,17 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestLsegTranscode(t *testing.T) { func TestLsegTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support type lseg") skipCockroachDB(t, "Server does not support type lseg")
testutil.RunTranscodeTests(t, "lseg", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "lseg", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Lseg{ pgtype.Lseg{
P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}}, P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},

View File

@ -2,10 +2,11 @@ package pgtype_test
import ( import (
"bytes" "bytes"
"context"
"net" "net"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool { func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
@ -28,7 +29,8 @@ func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
func TestMacaddrCodec(t *testing.T) { func TestMacaddrCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support type macaddr") skipCockroachDB(t, "Server does not support type macaddr")
testutil.RunTranscodeTests(t, "macaddr", []testutil.TranscodeTestCase{ // Only testing known OID query exec modes as net.HardwareAddr could map to macaddr or macaddr8.
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "macaddr", []pgxtest.ValueRoundTripTest{
{ {
mustParseMacaddr(t, "01:23:45:67:89:ab"), mustParseMacaddr(t, "01:23:45:67:89:ab"),
new(net.HardwareAddr), new(net.HardwareAddr),

View File

@ -9,8 +9,9 @@ import (
"strconv" "strconv"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -77,7 +78,7 @@ func TestNumericCodec(t *testing.T) {
max.Add(max, big.NewInt(1)) max.Add(max, big.NewInt(1))
longestNumeric := pgtype.Numeric{Int: max, Exp: -16383, Valid: true} longestNumeric := pgtype.Numeric{Int: max, Exp: -16383, Valid: true}
testutil.RunTranscodeTests(t, "numeric", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "numeric", []pgxtest.ValueRoundTripTest{
{mustParseNumeric(t, "1"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "1"))}, {mustParseNumeric(t, "1"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "1"))},
{mustParseNumeric(t, "3.14159"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "3.14159"))}, {mustParseNumeric(t, "3.14159"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "3.14159"))},
{mustParseNumeric(t, "100010001"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "100010001"))}, {mustParseNumeric(t, "100010001"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "100010001"))},
@ -118,7 +119,7 @@ func TestNumericCodecInfinity(t *testing.T) {
skipCockroachDB(t, "server formats numeric text format differently") skipCockroachDB(t, "server formats numeric text format differently")
skipPostgreSQLVersionLessThan(t, 14) skipPostgreSQLVersionLessThan(t, 14)
testutil.RunTranscodeTests(t, "numeric", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "numeric", []pgxtest.ValueRoundTripTest{
{math.Inf(1), new(float64), isExpectedEq(math.Inf(1))}, {math.Inf(1), new(float64), isExpectedEq(math.Inf(1))},
{float32(math.Inf(1)), new(float32), isExpectedEq(float32(math.Inf(1)))}, {float32(math.Inf(1)), new(float32), isExpectedEq(float32(math.Inf(1)))},
{math.Inf(-1), new(float64), isExpectedEq(math.Inf(-1))}, {math.Inf(-1), new(float64), isExpectedEq(math.Inf(-1))},
@ -159,29 +160,28 @@ func TestNumericCodecFuzz(t *testing.T) {
max := &big.Int{} max := &big.Int{}
max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10) max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
tests := make([]testutil.TranscodeTestCase, 0, 2000) tests := make([]pgxtest.ValueRoundTripTest, 0, 2000)
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
for j := -50; j < 50; j++ { for j := -50; j < 50; j++ {
num := (&big.Int{}).Rand(r, max) num := (&big.Int{}).Rand(r, max)
n := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true} n := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}
tests = append(tests, testutil.TranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)}) tests = append(tests, pgxtest.ValueRoundTripTest{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
negNum := &big.Int{} negNum := &big.Int{}
negNum.Neg(num) negNum.Neg(num)
n = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true} n = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}
tests = append(tests, testutil.TranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)}) tests = append(tests, pgxtest.ValueRoundTripTest{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
} }
} }
testutil.RunTranscodeTests(t, "numeric", tests) pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "numeric", tests)
} }
func TestNumericMarshalJSON(t *testing.T) { func TestNumericMarshalJSON(t *testing.T) {
skipCockroachDB(t, "server formats numeric text format differently") skipCockroachDB(t, "server formats numeric text format differently")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
for i, tt := range []struct { for i, tt := range []struct {
decString string decString string
@ -201,7 +201,7 @@ func TestNumericMarshalJSON(t *testing.T) {
} { } {
var num pgtype.Numeric var num pgtype.Numeric
var pgJSON string var pgJSON string
err := conn.QueryRow(context.Background(), `select $1::numeric, to_json($1::numeric)`, tt.decString).Scan(&num, &pgJSON) err := conn.QueryRow(ctx, `select $1::numeric, to_json($1::numeric)`, tt.decString).Scan(&num, &pgJSON)
require.NoErrorf(t, err, "%d", i) require.NoErrorf(t, err, "%d", i)
goJSON, err := json.Marshal(num) goJSON, err := json.Marshal(num)
@ -209,4 +209,5 @@ func TestNumericMarshalJSON(t *testing.T) {
require.Equal(t, pgJSON, string(goJSON)) require.Equal(t, pgJSON, string(goJSON))
} }
})
} }

View File

@ -1,10 +1,11 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqPath(a interface{}) func(interface{}) bool { func isExpectedEqPath(a interface{}) func(interface{}) bool {
@ -29,7 +30,7 @@ func isExpectedEqPath(a interface{}) func(interface{}) bool {
func TestPathTranscode(t *testing.T) { func TestPathTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support type path") skipCockroachDB(t, "Server does not support type path")
testutil.RunTranscodeTests(t, "path", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "path", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Path{ pgtype.Path{
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}}, P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}},

View File

@ -374,7 +374,7 @@ func NewMap() *Map {
registerDefaultPgTypeVariants("daterange", "_daterange", Daterange{}) registerDefaultPgTypeVariants("daterange", "_daterange", Daterange{})
registerDefaultPgTypeVariants("float4", "_float4", Float4{}) registerDefaultPgTypeVariants("float4", "_float4", Float4{})
registerDefaultPgTypeVariants("float8", "_float8", Float8{}) registerDefaultPgTypeVariants("float8", "_float8", Float8{})
registerDefaultPgTypeVariants("float8range", "_float8range", Float8range{}) registerDefaultPgTypeVariants("numrange", "_numrange", Float8range{}) // There is no PostgreSQL builtin float8range so map it to numrange.
registerDefaultPgTypeVariants("inet", "_inet", Inet{}) registerDefaultPgTypeVariants("inet", "_inet", Inet{})
registerDefaultPgTypeVariants("int2", "_int2", Int2{}) registerDefaultPgTypeVariants("int2", "_int2", Int2{})
registerDefaultPgTypeVariants("int4", "_int4", Int4{}) registerDefaultPgTypeVariants("int4", "_int4", Int4{})

View File

@ -1,21 +1,34 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"database/sql" "database/sql"
"errors" "errors"
"net" "net"
"os"
"regexp" "regexp"
"strconv" "strconv"
"testing" "testing"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
_ "github.com/jackc/pgx/v5/stdlib" _ "github.com/jackc/pgx/v5/stdlib"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var defaultConnTestRunner pgxtest.ConnTestRunner
func init() {
defaultConnTestRunner = pgxtest.DefaultConnTestRunner()
defaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {
config, err := pgx.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
require.NoError(t, err)
return config
}
}
// Test for renamed types // Test for renamed types
type _string string type _string string
type _bool bool type _bool bool
@ -70,8 +83,11 @@ func mustParseMacaddr(t testing.TB, s string) net.HardwareAddr {
} }
func skipCockroachDB(t testing.TB, msg string) { func skipCockroachDB(t testing.TB, msg string) {
conn := testutil.MustConnectPgx(t) conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
defer testutil.MustCloseContext(t, conn) if err != nil {
t.Fatal(err)
}
defer conn.Close(context.Background())
if conn.PgConn().ParameterStatus("crdb_version") != "" { if conn.PgConn().ParameterStatus("crdb_version") != "" {
t.Skip(msg) t.Skip(msg)
@ -79,8 +95,11 @@ func skipCockroachDB(t testing.TB, msg string) {
} }
func skipPostgreSQLVersionLessThan(t testing.TB, minVersion int64) { func skipPostgreSQLVersionLessThan(t testing.TB, minVersion int64) {
conn := testutil.MustConnectPgx(t) conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
defer testutil.MustCloseContext(t, conn) if err != nil {
t.Fatal(err)
}
defer conn.Close(context.Background())
serverVersionStr := conn.PgConn().ParameterStatus("server_version") serverVersionStr := conn.PgConn().ParameterStatus("server_version")
serverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr) serverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)

View File

@ -1,18 +1,19 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestPointCodec(t *testing.T) { func TestPointCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support type point") skipCockroachDB(t, "Server does not support type point")
testutil.RunTranscodeTests(t, "point", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "point", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true}, pgtype.Point{P: pgtype.Vec2{1.234, 5.6789012345}, Valid: true},
new(pgtype.Point), new(pgtype.Point),

View File

@ -1,10 +1,11 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqPolygon(a interface{}) func(interface{}) bool { func isExpectedEqPolygon(a interface{}) func(interface{}) bool {
@ -29,7 +30,7 @@ func isExpectedEqPolygon(a interface{}) func(interface{}) bool {
func TestPolygonTranscode(t *testing.T) { func TestPolygonTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support type polygon") skipCockroachDB(t, "Server does not support type polygon")
testutil.RunTranscodeTests(t, "polygon", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "polygon", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Polygon{ pgtype.Polygon{
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}}, P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}},

View File

@ -1,22 +1,24 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"math" "math"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestQcharTranscode(t *testing.T) { func TestQcharTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support qchar") skipCockroachDB(t, "Server does not support qchar")
var tests []testutil.TranscodeTestCase var tests []pgxtest.ValueRoundTripTest
for i := 0; i <= math.MaxUint8; i++ { for i := 0; i <= math.MaxUint8; i++ {
tests = append(tests, testutil.TranscodeTestCase{rune(i), new(rune), isExpectedEq(rune(i))}) tests = append(tests, pgxtest.ValueRoundTripTest{rune(i), new(rune), isExpectedEq(rune(i))})
tests = append(tests, testutil.TranscodeTestCase{byte(i), new(byte), isExpectedEq(byte(i))}) tests = append(tests, pgxtest.ValueRoundTripTest{byte(i), new(byte), isExpectedEq(byte(i))})
} }
tests = append(tests, testutil.TranscodeTestCase{nil, new(*rune), isExpectedEq((*rune)(nil))}) tests = append(tests, pgxtest.ValueRoundTripTest{nil, new(*rune), isExpectedEq((*rune)(nil))})
tests = append(tests, testutil.TranscodeTestCase{nil, new(*byte), isExpectedEq((*byte)(nil))}) tests = append(tests, pgxtest.ValueRoundTripTest{nil, new(*byte), isExpectedEq((*byte)(nil))})
testutil.RunTranscodeTests(t, `"char"`, tests) // Can only test with known OIDs as rune and byte would be considered numbers.
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, `"char"`, tests)
} }

View File

@ -4,15 +4,16 @@ import (
"context" "context"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestRangeCodecTranscode(t *testing.T) { func TestRangeCodecTranscode(t *testing.T) {
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)") skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
testutil.RunTranscodeTests(t, "int4range", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int4range", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, pgtype.Int4range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true},
new(pgtype.Int4range), new(pgtype.Int4range),
@ -39,9 +40,12 @@ func TestRangeCodecTranscode(t *testing.T) {
} }
func TestRangeCodecTranscodeCompatibleRangeElementTypes(t *testing.T) { func TestRangeCodecTranscodeCompatibleRangeElementTypes(t *testing.T) {
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)") ctr := defaultConnTestRunner
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
pgxtest.SkipCockroachDB(t, conn, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
}
testutil.RunTranscodeTests(t, "numrange", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "numrange", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Float8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true}, pgtype.Float8range{LowerType: pgtype.Empty, UpperType: pgtype.Empty, Valid: true},
new(pgtype.Float8range), new(pgtype.Float8range),
@ -70,8 +74,7 @@ func TestRangeCodecTranscodeCompatibleRangeElementTypes(t *testing.T) {
func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) { func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)") skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
var r pgtype.Int4range var r pgtype.Int4range
@ -90,7 +93,7 @@ func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {
r, r,
) )
err = conn.QueryRow(context.Background(), `select '[1,)'::int4range`).Scan(&r) err = conn.QueryRow(ctx, `select '[1,)'::int4range`).Scan(&r)
require.NoError(t, err) require.NoError(t, err)
require.Equal( require.Equal(
@ -105,7 +108,7 @@ func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {
r, r,
) )
err = conn.QueryRow(context.Background(), `select 'empty'::int4range`).Scan(&r) err = conn.QueryRow(ctx, `select 'empty'::int4range`).Scan(&r)
require.NoError(t, err) require.NoError(t, err)
require.Equal( require.Equal(
@ -119,13 +122,13 @@ func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {
}, },
r, r,
) )
})
} }
func TestRangeCodecDecodeValue(t *testing.T) { func TestRangeCodecDecodeValue(t *testing.T) {
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)") skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
for _, tt := range []struct { for _, tt := range []struct {
sql string sql string
@ -143,7 +146,7 @@ func TestRangeCodecDecodeValue(t *testing.T) {
}, },
} { } {
t.Run(tt.sql, func(t *testing.T) { t.Run(tt.sql, func(t *testing.T) {
rows, err := conn.Query(context.Background(), tt.sql) rows, err := conn.Query(ctx, tt.sql)
require.NoError(t, err) require.NoError(t, err)
for rows.Next() { for rows.Next() {
@ -156,4 +159,5 @@ func TestRangeCodecDecodeValue(t *testing.T) {
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
}) })
} }
})
} }

View File

@ -4,30 +4,27 @@ import (
"context" "context"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestRecordCodec(t *testing.T) { func TestRecordCodec(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
var a string var a string
var b int32 var b int32
err := conn.QueryRow(context.Background(), `select row('foo'::text, 42::int4)`).Scan(pgtype.CompositeFields{&a, &b}) err := conn.QueryRow(ctx, `select row('foo'::text, 42::int4)`).Scan(pgtype.CompositeFields{&a, &b})
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, "foo", a) require.Equal(t, "foo", a)
require.Equal(t, int32(42), b) require.Equal(t, int32(42), b)
})
} }
func TestRecordCodecDecodeValue(t *testing.T) { func TestRecordCodecDecodeValue(t *testing.T) {
skipCockroachDB(t, "Server converts row int4 to int8") skipCockroachDB(t, "Server converts row int4 to int8")
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
for _, tt := range []struct { for _, tt := range []struct {
sql string sql string
expected interface{} expected interface{}
@ -72,4 +69,5 @@ func TestRecordCodecDecodeValue(t *testing.T) {
require.NoError(t, rows.Err()) require.NoError(t, rows.Err())
}) })
} }
})
} }

View File

@ -1,85 +0,0 @@
package testutil
import (
"context"
"fmt"
"os"
"reflect"
"testing"
"github.com/jackc/pgx/v5"
_ "github.com/jackc/pgx/v5/stdlib"
)
func MustConnectPgx(t testing.TB) *pgx.Conn {
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
if err != nil {
t.Fatal(err)
}
return conn
}
func MustClose(t testing.TB, conn interface {
Close() error
}) {
err := conn.Close()
if err != nil {
t.Fatal(err)
}
}
func MustCloseContext(t testing.TB, conn interface {
Close(context.Context) error
}) {
err := conn.Close(context.Background())
if err != nil {
t.Fatal(err)
}
}
type TranscodeTestCase struct {
Src interface{}
Dst interface{}
Test func(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)
}
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

@ -4,8 +4,9 @@ import (
"context" "context"
"testing" "testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -17,7 +18,7 @@ func (someFmtStringer) String() string {
func TestTextCodec(t *testing.T) { func TestTextCodec(t *testing.T) {
for _, pgTypeName := range []string{"text", "varchar"} { for _, pgTypeName := range []string{"text", "varchar"} {
testutil.RunTranscodeTests(t, pgTypeName, []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, pgTypeName, []pgxtest.ValueRoundTripTest{
{ {
pgtype.Text{String: "", Valid: true}, pgtype.Text{String: "", Valid: true},
new(pgtype.Text), new(pgtype.Text),
@ -31,6 +32,10 @@ func TestTextCodec(t *testing.T) {
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})}, {nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
{"foo", new(string), isExpectedEq("foo")}, {"foo", new(string), isExpectedEq("foo")},
{someFmtStringer{}, new(string), isExpectedEq("some fmt.Stringer")}, {someFmtStringer{}, new(string), isExpectedEq("some fmt.Stringer")},
})
// rune requires known OID because otherwise it is considered an int32.
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, pgTypeName, []pgxtest.ValueRoundTripTest{
{rune('R'), new(rune), isExpectedEq(rune('R'))}, {rune('R'), new(rune), isExpectedEq(rune('R'))},
}) })
} }
@ -47,7 +52,7 @@ func TestTextCodec(t *testing.T) {
// //
// So this is simply a smoke test of the name type. // So this is simply a smoke test of the name type.
func TestTextCodecName(t *testing.T) { func TestTextCodecName(t *testing.T) {
testutil.RunTranscodeTests(t, "name", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "name", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Text{String: "", Valid: true}, pgtype.Text{String: "", Valid: true},
new(pgtype.Text), new(pgtype.Text),
@ -67,7 +72,7 @@ func TestTextCodecName(t *testing.T) {
func TestTextCodecBPChar(t *testing.T) { func TestTextCodecBPChar(t *testing.T) {
skipCockroachDB(t, "Server does not properly handle bpchar with multi-byte character") skipCockroachDB(t, "Server does not properly handle bpchar with multi-byte character")
testutil.RunTranscodeTests(t, "char(3)", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "char(3)", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Text{String: "a ", Valid: true}, pgtype.Text{String: "a ", Valid: true},
new(pgtype.Text), new(pgtype.Text),
@ -94,12 +99,12 @@ func TestTextCodecBPChar(t *testing.T) {
// //
// It only supports the text format. // It only supports the text format.
func TestTextCodecACLItem(t *testing.T) { func TestTextCodecACLItem(t *testing.T) {
skipCockroachDB(t, "Server does not support type aclitem") ctr := defaultConnTestRunner
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
}
conn := testutil.MustConnectPgx(t) pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
defer testutil.MustCloseContext(t, conn)
testutil.RunTranscodeTestsFormat(t, "aclitem", []testutil.TranscodeTestCase{
{ {
pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true}, pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true},
new(pgtype.Text), new(pgtype.Text),
@ -107,14 +112,13 @@ func TestTextCodecACLItem(t *testing.T) {
}, },
{pgtype.Text{}, new(pgtype.Text), isExpectedEq(pgtype.Text{})}, {pgtype.Text{}, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})}, {nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
}, conn, "Text", pgtype.TextFormatCode) })
} }
func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) { func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
conn := testutil.MustConnectPgx(t) ctr := defaultConnTestRunner
defer testutil.MustCloseContext(t, conn) ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
ctx := context.Background()
// The tricky test user, below, has to actually exist so that it can be used in a test // The tricky test user, below, has to actually exist so that it can be used in a test
// of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles. // of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
@ -126,14 +130,15 @@ func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
if commandTag.RowsAffected() == 0 { if commandTag.RowsAffected() == 0 {
t.Skipf("Role with special characters does not exist.") t.Skipf("Role with special characters does not exist.")
} }
}
testutil.RunTranscodeTestsFormat(t, "aclitem", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}, pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true},
new(pgtype.Text), new(pgtype.Text),
isExpectedEq(pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}), isExpectedEq(pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}),
}, },
}, conn, "Text", pgtype.TextFormatCode) })
} }
func TestTextMarshalJSON(t *testing.T) { func TestTextMarshalJSON(t *testing.T) {

View File

@ -1,16 +1,17 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestTIDCodec(t *testing.T) { func TestTIDCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support type tid") skipCockroachDB(t, "Server does not support type tid")
testutil.RunTranscodeTests(t, "tid", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "tid", []pgxtest.ValueRoundTripTest{
{ {
pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true}, pgtype.TID{BlockNumber: 42, OffsetNumber: 43, Valid: true},
new(pgtype.TID), new(pgtype.TID),

View File

@ -1,15 +1,16 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestTimeCodec(t *testing.T) { func TestTimeCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "time", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "time", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Time{Microseconds: 0, Valid: true}, pgtype.Time{Microseconds: 0, Valid: true},
new(pgtype.Time), new(pgtype.Time),

View File

@ -4,6 +4,7 @@ import (
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"strings"
"time" "time"
"github.com/jackc/pgx/v5/internal/pgio" "github.com/jackc/pgx/v5/internal/pgio"
@ -127,12 +128,29 @@ func (encodePlanTimestampCodecText) Encode(value interface{}, buf []byte) (newBu
return nil, err return nil, err
} }
if !ts.Valid {
return nil, nil
}
var s string var s string
switch ts.InfinityModifier { switch ts.InfinityModifier {
case Finite: case Finite:
t := discardTimeZone(ts.Time) t := discardTimeZone(ts.Time)
// Year 0000 is 1 BC
bc := false
if year := t.Year(); year <= 0 {
year = -year + 1
t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)
bc = true
}
s = t.Truncate(time.Microsecond).Format(pgTimestampFormat) s = t.Truncate(time.Microsecond).Format(pgTimestampFormat)
if bc {
s = s + " BC"
}
case Infinity: case Infinity:
s = "infinity" s = "infinity"
case NegativeInfinity: case NegativeInfinity:
@ -219,11 +237,21 @@ func (scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst interface{})
case "-infinity": case "-infinity":
ts = Timestamp{Valid: true, InfinityModifier: -Infinity} ts = Timestamp{Valid: true, InfinityModifier: -Infinity}
default: default:
bc := false
if strings.HasSuffix(sbuf, " BC") {
sbuf = sbuf[:len(sbuf)-3]
bc = true
}
tim, err := time.Parse(pgTimestampFormat, sbuf) tim, err := time.Parse(pgTimestampFormat, sbuf)
if err != nil { if err != nil {
return err return err
} }
if bc {
year := -tim.Year() + 1
tim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location())
}
ts = Timestamp{Time: tim, Valid: true} ts = Timestamp{Time: tim, Valid: true}
} }

View File

@ -5,15 +5,21 @@ import (
"testing" "testing"
"time" "time"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestTimestampCodec(t *testing.T) { func TestTimestampCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)") skipCockroachDB(t, "Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)")
testutil.RunTranscodeTests(t, "timestamp", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "timestamp", []pgxtest.ValueRoundTripTest{
{time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.UTC))},
{time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC), new(time.Time), isExpectedEqTime(time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC))},
{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(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(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))}, {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))},
@ -34,19 +40,18 @@ func TestTimestampCodec(t *testing.T) {
// https://github.com/jackc/pgx/v4/pgtype/pull/128 // https://github.com/jackc/pgx/v4/pgtype/pull/128
func TestTimestampTranscodeBigTimeBinary(t *testing.T) { func TestTimestampTranscodeBigTimeBinary(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true} in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
var out pgtype.Timestamp var out pgtype.Timestamp
err := conn.QueryRow(context.Background(), "select $1::timestamp", in).Scan(&out) err := conn.QueryRow(ctx, "select $1::timestamp", in).Scan(&out)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
require.Equal(t, in.Valid, out.Valid) require.Equal(t, in.Valid, out.Valid)
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time) require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
})
} }
// https://github.com/jackc/pgtype/issues/74 // https://github.com/jackc/pgtype/issues/74

View File

@ -5,6 +5,7 @@ import (
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"time" "time"
"github.com/jackc/pgx/v5/internal/pgio" "github.com/jackc/pgx/v5/internal/pgio"
@ -184,11 +185,30 @@ func (encodePlanTimestamptzCodecText) Encode(value interface{}, buf []byte) (new
return nil, err return nil, err
} }
if !ts.Valid {
return nil, nil
}
var s string var s string
switch ts.InfinityModifier { switch ts.InfinityModifier {
case Finite: case Finite:
s = ts.Time.UTC().Truncate(time.Microsecond).Format(pgTimestamptzSecondFormat)
t := ts.Time.UTC().Truncate(time.Microsecond)
// Year 0000 is 1 BC
bc := false
if year := t.Year(); year <= 0 {
year = -year + 1
t = time.Date(year, t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), time.UTC)
bc = true
}
s = t.Format(pgTimestamptzSecondFormat)
if bc {
s = s + " BC"
}
case Infinity: case Infinity:
s = "infinity" s = "infinity"
case NegativeInfinity: case NegativeInfinity:
@ -267,6 +287,12 @@ func (scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst interfac
case "-infinity": case "-infinity":
tstz = Timestamptz{Valid: true, InfinityModifier: -Infinity} tstz = Timestamptz{Valid: true, InfinityModifier: -Infinity}
default: default:
bc := false
if strings.HasSuffix(sbuf, " BC") {
sbuf = sbuf[:len(sbuf)-3]
bc = true
}
var format string var format string
if len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') { if len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') {
format = pgTimestamptzSecondFormat format = pgTimestamptzSecondFormat
@ -281,6 +307,11 @@ func (scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst interfac
return err return err
} }
if bc {
year := -tim.Year() + 1
tim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location())
}
tstz = Timestamptz{Time: tim, Valid: true} tstz = Timestamptz{Time: tim, Valid: true}
} }

View File

@ -5,15 +5,21 @@ import (
"testing" "testing"
"time" "time"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestTimestamptzCodec(t *testing.T) { func TestTimestamptzCodec(t *testing.T) {
skipCockroachDB(t, "Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)") skipCockroachDB(t, "Server does not support infinite timestamps (see https://github.com/cockroachdb/cockroach/issues/41564)")
testutil.RunTranscodeTests(t, "timestamptz", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "timestamptz", []pgxtest.ValueRoundTripTest{
{time.Date(-100, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(-100, 1, 1, 0, 0, 0, 0, time.Local))},
{time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(-1, 1, 1, 0, 0, 0, 0, time.Local))},
{time.Date(0, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(0, 1, 1, 0, 0, 0, 0, time.Local))},
{time.Date(1, 1, 1, 0, 0, 0, 0, time.Local), new(time.Time), isExpectedEqTime(time.Date(1, 1, 1, 0, 0, 0, 0, time.Local))},
{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(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(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))}, {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))},
@ -34,19 +40,18 @@ func TestTimestamptzCodec(t *testing.T) {
// https://github.com/jackc/pgx/v4/pgtype/pull/128 // https://github.com/jackc/pgx/v4/pgtype/pull/128
func TestTimestamptzTranscodeBigTimeBinary(t *testing.T) { func TestTimestamptzTranscodeBigTimeBinary(t *testing.T) {
conn := testutil.MustConnectPgx(t) defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
defer testutil.MustCloseContext(t, conn)
in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true} in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
var out pgtype.Timestamptz var out pgtype.Timestamptz
err := conn.QueryRow(context.Background(), "select $1::timestamptz", in).Scan(&out) err := conn.QueryRow(ctx, "select $1::timestamptz", in).Scan(&out)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
require.Equal(t, in.Valid, out.Valid) require.Equal(t, in.Valid, out.Valid)
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time) require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
})
} }
// https://github.com/jackc/pgtype/issues/74 // https://github.com/jackc/pgtype/issues/74

View File

@ -1,14 +1,15 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
) )
func TestUint32Codec(t *testing.T) { func TestUint32Codec(t *testing.T) {
testutil.RunTranscodeTests(t, "oid", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "oid", []pgxtest.ValueRoundTripTest{
{ {
pgtype.Uint32{Uint32: pgtype.TextOID, Valid: true}, pgtype.Uint32{Uint32: pgtype.TextOID, Valid: true},
new(pgtype.Uint32), new(pgtype.Uint32),

View File

@ -1,31 +1,22 @@
package pgtype_test package pgtype_test
import ( import (
"context"
"reflect" "reflect"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
"github.com/jackc/pgx/v5/pgtype/testutil" "github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestUUIDCodec(t *testing.T) { func TestUUIDCodec(t *testing.T) {
testutil.RunTranscodeTests(t, "uuid", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "uuid", []pgxtest.ValueRoundTripTest{
{ {
pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}, 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), new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}), isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
}, },
{
[16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
},
{
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
},
{ {
"00010203-0405-0607-0809-0a0b0c0d0e0f", "00010203-0405-0607-0809-0a0b0c0d0e0f",
new(pgtype.UUID), new(pgtype.UUID),
@ -40,6 +31,19 @@ func TestUUIDCodec(t *testing.T) {
{pgtype.UUID{}, new(pgtype.UUID), isExpectedEq(pgtype.UUID{})}, {pgtype.UUID{}, new(pgtype.UUID), isExpectedEq(pgtype.UUID{})},
{nil, new(pgtype.UUID), isExpectedEq(pgtype.UUID{})}, {nil, new(pgtype.UUID), isExpectedEq(pgtype.UUID{})},
}) })
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "uuid", []pgxtest.ValueRoundTripTest{
{
[16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
},
{
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
new(pgtype.UUID),
isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
},
})
} }
func TestUUID_MarshalJSON(t *testing.T) { func TestUUID_MarshalJSON(t *testing.T) {

View File

@ -1,10 +1,11 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEq(a interface{}) func(interface{}) bool { func isExpectedEq(a interface{}) func(interface{}) bool {
@ -14,7 +15,7 @@ func isExpectedEq(a interface{}) func(interface{}) bool {
} }
func TestFloat8Transcode(t *testing.T) { func TestFloat8Transcode(t *testing.T) {
testutil.RunTranscodeTests(t, "float8", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "float8", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Float8)(1), (zeronull.Float8)(1),
new(zeronull.Float8), new(zeronull.Float8),

View File

@ -2,14 +2,15 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func TestInt2Transcode(t *testing.T) { func TestInt2Transcode(t *testing.T) {
testutil.RunTranscodeTests(t, "int2", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int2", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Int2)(1), (zeronull.Int2)(1),
new(zeronull.Int2), new(zeronull.Int2),
@ -29,7 +30,7 @@ func TestInt2Transcode(t *testing.T) {
} }
func TestInt4Transcode(t *testing.T) { func TestInt4Transcode(t *testing.T) {
testutil.RunTranscodeTests(t, "int4", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int4", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Int4)(1), (zeronull.Int4)(1),
new(zeronull.Int4), new(zeronull.Int4),
@ -49,7 +50,7 @@ func TestInt4Transcode(t *testing.T) {
} }
func TestInt8Transcode(t *testing.T) { func TestInt8Transcode(t *testing.T) {
testutil.RunTranscodeTests(t, "int8", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int8", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Int8)(1), (zeronull.Int8)(1),
new(zeronull.Int8), new(zeronull.Int8),

View File

@ -10,7 +10,7 @@ import (
<% [2, 4, 8].each do |pg_byte_size| %> <% [2, 4, 8].each do |pg_byte_size| %>
<% pg_bit_size = pg_byte_size * 8 %> <% pg_bit_size = pg_byte_size * 8 %>
func TestInt<%= pg_byte_size %>Transcode(t *testing.T) { func TestInt<%= pg_byte_size %>Transcode(t *testing.T) {
testutil.RunTranscodeTests(t, "int<%= pg_byte_size %>", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int<%= pg_byte_size %>", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Int<%= pg_byte_size %>)(1), (zeronull.Int<%= pg_byte_size %>)(1),
new(zeronull.Int<%= pg_byte_size %>), new(zeronull.Int<%= pg_byte_size %>),

View File

@ -1,14 +1,15 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func TestTextTranscode(t *testing.T) { func TestTextTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "text", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "text", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Text)("foo"), (zeronull.Text)("foo"),
new(zeronull.Text), new(zeronull.Text),

View File

@ -1,11 +1,12 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqTimestamp(a interface{}) func(interface{}) bool { func isExpectedEqTimestamp(a interface{}) func(interface{}) bool {
@ -18,7 +19,7 @@ func isExpectedEqTimestamp(a interface{}) func(interface{}) bool {
} }
func TestTimestampTranscode(t *testing.T) { func TestTimestampTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "timestamp", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "timestamp", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), (zeronull.Timestamp)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
new(zeronull.Timestamp), new(zeronull.Timestamp),

View File

@ -1,11 +1,12 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool { func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool {
@ -18,7 +19,7 @@ func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool {
} }
func TestTimestamptzTranscode(t *testing.T) { func TestTimestamptzTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "timestamptz", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "timestamptz", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)), (zeronull.Timestamptz)(time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)),
new(zeronull.Timestamptz), new(zeronull.Timestamptz),

View File

@ -1,14 +1,15 @@
package zeronull_test package zeronull_test
import ( import (
"context"
"testing" "testing"
"github.com/jackc/pgx/v5/pgtype/testutil"
"github.com/jackc/pgx/v5/pgtype/zeronull" "github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
) )
func TestUUIDTranscode(t *testing.T) { func TestUUIDTranscode(t *testing.T) {
testutil.RunTranscodeTests(t, "uuid", []testutil.TranscodeTestCase{ pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "uuid", []pgxtest.ValueRoundTripTest{
{ {
(zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}), (zeronull.UUID)([16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
new(zeronull.UUID), new(zeronull.UUID),

View File

@ -0,0 +1,17 @@
package zeronull
import (
"github.com/jackc/pgx/v5/pgtype"
)
// Register registers the zeronull types so they can be used in query exec modes that do not know the server OIDs.
func Register(m *pgtype.Map) {
m.RegisterDefaultPgType(Float8(0), "float8")
m.RegisterDefaultPgType(Int2(0), "int2")
m.RegisterDefaultPgType(Int4(0), "int4")
m.RegisterDefaultPgType(Int8(0), "int8")
m.RegisterDefaultPgType(Text(""), "text")
m.RegisterDefaultPgType(Timestamp{}, "timestamp")
m.RegisterDefaultPgType(Timestamptz{}, "timestamptz")
m.RegisterDefaultPgType(UUID{}, "uuid")
}

View File

@ -0,0 +1,26 @@
package zeronull_test
import (
"context"
"os"
"testing"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype/zeronull"
"github.com/jackc/pgx/v5/pgxtest"
"github.com/stretchr/testify/require"
)
var defaultConnTestRunner pgxtest.ConnTestRunner
func init() {
defaultConnTestRunner = pgxtest.DefaultConnTestRunner()
defaultConnTestRunner.CreateConfig = func(ctx context.Context, t testing.TB) *pgx.ConnConfig {
config, err := pgx.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
require.NoError(t, err)
return config
}
defaultConnTestRunner.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
zeronull.Register(conn.TypeMap())
}
}

View File

@ -3,11 +3,28 @@ package pgxtest
import ( import (
"context" "context"
"fmt"
"reflect"
"testing" "testing"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
) )
var AllQueryExecModes = []pgx.QueryExecMode{
pgx.QueryExecModeCacheStatement,
pgx.QueryExecModeCacheDescribe,
pgx.QueryExecModeDescribeExec,
pgx.QueryExecModeExec,
pgx.QueryExecModeSimpleProtocol,
}
// KnownOIDQueryExecModes is a slice of all query exec modes where the param and result OIDs are known before sending the query.
var KnownOIDQueryExecModes = []pgx.QueryExecMode{
pgx.QueryExecModeCacheStatement,
pgx.QueryExecModeCacheDescribe,
pgx.QueryExecModeDescribeExec,
}
// ConnTestRunner controls how a *pgx.Conn is created and closed by tests. All fields are required. Use DefaultConnTestRunner to get a // ConnTestRunner controls how a *pgx.Conn is created and closed by tests. All fields are required. Use DefaultConnTestRunner to get a
// ConnTestRunner with reasonable default values. // ConnTestRunner with reasonable default values.
type ConnTestRunner struct { type ConnTestRunner struct {
@ -46,6 +63,8 @@ func DefaultConnTestRunner() ConnTestRunner {
} }
func (ctr *ConnTestRunner) RunTest(ctx context.Context, t testing.TB, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) { func (ctr *ConnTestRunner) RunTest(ctx context.Context, t testing.TB, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) {
t.Helper()
config := ctr.CreateConfig(ctx, t) config := ctr.CreateConfig(ctx, t)
conn, err := pgx.ConnectConfig(ctx, config) conn, err := pgx.ConnectConfig(ctx, config)
if err != nil { if err != nil {
@ -62,13 +81,7 @@ func (ctr *ConnTestRunner) RunTest(ctx context.Context, t testing.TB, f func(ctx
// If modes is nil all pgx.QueryExecModes are tested. // If modes is nil all pgx.QueryExecModes are tested.
func RunWithQueryExecModes(ctx context.Context, t *testing.T, ctr ConnTestRunner, modes []pgx.QueryExecMode, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) { func RunWithQueryExecModes(ctx context.Context, t *testing.T, ctr ConnTestRunner, modes []pgx.QueryExecMode, f func(ctx context.Context, t testing.TB, conn *pgx.Conn)) {
if modes == nil { if modes == nil {
modes = []pgx.QueryExecMode{ modes = AllQueryExecModes
pgx.QueryExecModeCacheStatement,
pgx.QueryExecModeCacheDescribe,
pgx.QueryExecModeDescribeExec,
pgx.QueryExecModeExec,
pgx.QueryExecModeSimpleProtocol,
}
} }
for _, mode := range modes { for _, mode := range modes {
@ -87,6 +100,51 @@ func RunWithQueryExecModes(ctx context.Context, t *testing.T, ctr ConnTestRunner
} }
} }
type ValueRoundTripTest struct {
Param interface{}
Result interface{}
Test func(interface{}) bool
}
func RunValueRoundTripTests(
ctx context.Context,
t testing.TB,
ctr ConnTestRunner,
modes []pgx.QueryExecMode,
pgTypeName string,
tests []ValueRoundTripTest,
) {
t.Helper()
if modes == nil {
modes = AllQueryExecModes
}
ctr.RunTest(ctx, t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
t.Helper()
sql := fmt.Sprintf("select $1::%s", pgTypeName)
for i, tt := range tests {
for _, mode := range modes {
err := conn.QueryRow(ctx, sql, mode, tt.Param).Scan(tt.Result)
if err != nil {
t.Errorf("%d. %v: %v", i, mode, err)
}
result := reflect.ValueOf(tt.Result)
if result.Kind() == reflect.Ptr {
result = result.Elem()
}
if !tt.Test(result.Interface()) {
t.Errorf("%d. %v: unexpected result for %v: %v", i, mode, tt.Param, result.Interface())
}
}
}
})
}
// SkipCockroachDB calls Skip on t with msg if the connection is to a CockroachDB server. // SkipCockroachDB calls Skip on t with msg if the connection is to a CockroachDB server.
func SkipCockroachDB(t testing.TB, conn *pgx.Conn, msg string) { func SkipCockroachDB(t testing.TB, conn *pgx.Conn, msg string) {
if conn.PgConn().ParameterStatus("crdb_version") != "" { if conn.PgConn().ParameterStatus("crdb_version") != "" {

View File

@ -17,7 +17,7 @@ func convertSimpleArgument(m *pgtype.Map, arg interface{}) (interface{}, error)
return nil, nil return nil, nil
} }
buf, err := m.Encode(0, TextFormatCode, arg, nil) buf, err := m.Encode(0, TextFormatCode, arg, []byte{})
if err != nil { if err != nil {
return nil, err return nil, err
} }