mirror of
https://github.com/jackc/pgx.git
synced 2025-05-31 11:42:24 +00:00
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:
parent
83e50f21e8
commit
ee93440ac1
35
conn.go
35
conn.go
@ -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
|
||||
// no way to safely use binary or to specify the parameter OIDs.
|
||||
func (c *Conn) appendParamsForQueryExecModeExec(args []interface{}) error {
|
||||
for i := range args {
|
||||
if args[i] == nil {
|
||||
err := c.eqb.AppendParamFormat(c.typeMap, 0, TextFormatCode, args[i])
|
||||
for _, arg := range args {
|
||||
if arg == nil {
|
||||
err := c.eqb.AppendParamFormat(c.typeMap, 0, TextFormatCode, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dt, ok := c.TypeMap().TypeForValue(args[i])
|
||||
dt, ok := c.TypeMap().TypeForValue(arg)
|
||||
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
|
||||
}
|
||||
|
||||
dt, ok = c.TypeMap().TypeForOID(pgtype.TextOID)
|
||||
if ok {
|
||||
arg = t
|
||||
}
|
||||
}
|
||||
}
|
||||
err := c.eqb.AppendParamFormat(c.typeMap, dt.OID, TextFormatCode, args[i])
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
@ -4,191 +4,184 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestArrayCodec(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{[]int16(nil)},
|
||||
{[]int16{}},
|
||||
{[]int16{1, 2, 3}},
|
||||
} {
|
||||
var actual []int16
|
||||
err := conn.QueryRow(
|
||||
ctx,
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{[]int16(nil)},
|
||||
{[]int16{}},
|
||||
{[]int16{1, 2, 3}},
|
||||
} {
|
||||
var actual []int16
|
||||
err := conn.QueryRow(
|
||||
context.Background(),
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
newInt16 := func(n int16) *int16 { return &n }
|
||||
|
||||
newInt16 := func(n int16) *int16 { return &n }
|
||||
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{[]*int16{newInt16(1), nil, newInt16(3), nil, newInt16(5)}},
|
||||
} {
|
||||
var actual []*int16
|
||||
err := conn.QueryRow(
|
||||
context.Background(),
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{[]*int16{newInt16(1), nil, newInt16(3), nil, newInt16(5)}},
|
||||
} {
|
||||
var actual []*int16
|
||||
err := conn.QueryRow(
|
||||
ctx,
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecAnySlice(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
type _int16Slice []int16
|
||||
|
||||
type _int16Slice []int16
|
||||
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{_int16Slice(nil)},
|
||||
{_int16Slice{}},
|
||||
{_int16Slice{1, 2, 3}},
|
||||
} {
|
||||
var actual _int16Slice
|
||||
err := conn.QueryRow(
|
||||
context.Background(),
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
for i, tt := range []struct {
|
||||
expected interface{}
|
||||
}{
|
||||
{_int16Slice(nil)},
|
||||
{_int16Slice{}},
|
||||
{_int16Slice{1, 2, 3}},
|
||||
} {
|
||||
var actual _int16Slice
|
||||
err := conn.QueryRow(
|
||||
ctx,
|
||||
"select $1::smallint[]",
|
||||
tt.expected,
|
||||
).Scan(&actual)
|
||||
assert.NoErrorf(t, err, "%d", i)
|
||||
assert.Equalf(t, tt.expected, actual, "%d", i)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecDecodeValue(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select '{}'::int4[]`,
|
||||
expected: []interface{}{},
|
||||
},
|
||||
{
|
||||
sql: `select '{1,2}'::int8[]`,
|
||||
expected: []interface{}{int64(1), int64(2)},
|
||||
},
|
||||
{
|
||||
sql: `select '{foo,bar}'::text[]`,
|
||||
expected: []interface{}{"foo", "bar"},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(context.Background(), tt.sql)
|
||||
require.NoError(t, err)
|
||||
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select '{}'::int4[]`,
|
||||
expected: []interface{}{},
|
||||
},
|
||||
{
|
||||
sql: `select '{1,2}'::int8[]`,
|
||||
expected: []interface{}{int64(1), int64(2)},
|
||||
},
|
||||
{
|
||||
sql: `select '{foo,bar}'::text[]`,
|
||||
expected: []interface{}{"foo", "bar"},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(ctx, tt.sql)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecScanMultipleDimensions(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
rows, err := conn.Query(context.Background(), `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
rows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecScanMultipleDimensionsEmpty(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
rows, err := conn.Query(context.Background(), `select '{}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
rows, err := conn.Query(ctx, `select '{}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecScanWrongMultipleDimensions(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
rows, err := conn.Query(ctx, `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
|
||||
rows, err := conn.Query(context.Background(), `select '{{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}'::int4[]`)
|
||||
require.NoError(t, err)
|
||||
|
||||
for rows.Next() {
|
||||
var ss [][][]int32
|
||||
err := rows.Scan(&ss)
|
||||
require.Error(t, err, "can't scan into dest[0]: PostgreSQL array has 2 dimensions but slice has 3 dimensions")
|
||||
}
|
||||
for rows.Next() {
|
||||
var ss [][][]int32
|
||||
err := rows.Scan(&ss)
|
||||
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) {
|
||||
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
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)
|
||||
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
rows, err := conn.Query(ctx, `select $1::int4[]`, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
for rows.Next() {
|
||||
var ss [][]int32
|
||||
err := rows.Scan(&ss)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [][]int32{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}}, ss)
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
|
||||
func TestArrayCodecEncodeMultipleDimensionsRagged(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support nested arrays (https://github.com/cockroachdb/cockroach/issues/36815)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
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")
|
||||
defer rows.Close()
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
rows, err := conn.Query(ctx, `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")
|
||||
defer rows.Close()
|
||||
})
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"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 {
|
||||
@ -17,7 +18,7 @@ func isExpectedEqBits(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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},
|
||||
new(pgtype.Bits),
|
||||
@ -34,7 +35,7 @@ func TestBitsCodecBit(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},
|
||||
new(pgtype.Bits),
|
||||
|
@ -1,14 +1,15 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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)},
|
||||
{false, new(bool), isExpectedEq(false)},
|
||||
{true, new(pgtype.Bool), isExpectedEq(pgtype.Bool{Bool: true, Valid: true})},
|
||||
|
@ -1,16 +1,17 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestBoxCodec(t *testing.T) {
|
||||
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{
|
||||
P: [2]pgtype.Vec2{{7.1, 5.2345678}, {3.14, 1.678}},
|
||||
|
@ -5,8 +5,9 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -28,7 +29,7 @@ func isExpectedEqBytes(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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{}, new([]byte), isExpectedEqBytes([]byte{})},
|
||||
{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
|
||||
@ -37,91 +38,79 @@ func TestByteaCodec(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDriverBytesQueryRow(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var buf []byte
|
||||
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")
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
var buf []byte
|
||||
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")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDriverBytes(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
argBuf := make([]byte, 128)
|
||||
for i := range argBuf {
|
||||
argBuf[i] = byte(i)
|
||||
}
|
||||
|
||||
rows, err := conn.Query(ctx, `select $1::bytea from generate_series(1, 1000)`, argBuf)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
|
||||
rowCount := 0
|
||||
resultBuf := argBuf
|
||||
detectedResultMutation := false
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
|
||||
// At some point the buffer should be reused and change.
|
||||
if bytes.Compare(argBuf, resultBuf) != 0 {
|
||||
detectedResultMutation = true
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
argBuf := make([]byte, 128)
|
||||
for i := range argBuf {
|
||||
argBuf[i] = byte(i)
|
||||
}
|
||||
|
||||
err = rows.Scan((*pgtype.DriverBytes)(&resultBuf))
|
||||
rows, err := conn.Query(ctx, `select $1::bytea from generate_series(1, 1000)`, argBuf)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
|
||||
require.Len(t, resultBuf, len(argBuf))
|
||||
require.Equal(t, resultBuf, argBuf)
|
||||
require.Equalf(t, cap(resultBuf), len(resultBuf), "cap(resultBuf) is larger than len(resultBuf)")
|
||||
}
|
||||
rowCount := 0
|
||||
resultBuf := argBuf
|
||||
detectedResultMutation := false
|
||||
for rows.Next() {
|
||||
rowCount++
|
||||
|
||||
require.True(t, detectedResultMutation)
|
||||
// At some point the buffer should be reused and change.
|
||||
if bytes.Compare(argBuf, resultBuf) != 0 {
|
||||
detectedResultMutation = true
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
require.NoError(t, err)
|
||||
err = rows.Scan((*pgtype.DriverBytes)(&resultBuf))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, resultBuf, len(argBuf))
|
||||
require.Equal(t, resultBuf, argBuf)
|
||||
require.Equalf(t, cap(resultBuf), len(resultBuf), "cap(resultBuf) is larger than len(resultBuf)")
|
||||
}
|
||||
|
||||
require.True(t, detectedResultMutation)
|
||||
|
||||
err = rows.Err()
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPreallocBytes(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
origBuf := []byte{5, 6, 7, 8}
|
||||
buf := origBuf
|
||||
err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
require.Len(t, buf, 2)
|
||||
require.Equal(t, 4, cap(buf))
|
||||
require.Equal(t, buf, []byte{1, 2})
|
||||
|
||||
origBuf := []byte{5, 6, 7, 8}
|
||||
buf := origBuf
|
||||
err := conn.QueryRow(ctx, `select $1::bytea`, []byte{1, 2}).Scan((*pgtype.PreallocBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte{1, 2, 7, 8}, origBuf)
|
||||
|
||||
require.Len(t, buf, 2)
|
||||
require.Equal(t, 4, cap(buf))
|
||||
require.Equal(t, buf, []byte{1, 2})
|
||||
err = conn.QueryRow(ctx, `select $1::bytea`, []byte{3, 4, 5, 6, 7}).Scan((*pgtype.PreallocBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, buf, 5)
|
||||
require.Equal(t, 5, cap(buf))
|
||||
|
||||
require.Equal(t, []byte{1, 2, 7, 8}, origBuf)
|
||||
|
||||
err = conn.QueryRow(ctx, `select $1::bytea`, []byte{3, 4, 5, 6, 7}).Scan((*pgtype.PreallocBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, buf, 5)
|
||||
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) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
var buf []byte
|
||||
err := conn.QueryRow(ctx, `select 1::int4`).Scan((*pgtype.UndecodedBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
var buf []byte
|
||||
err := conn.QueryRow(ctx, `select 1::int4`).Scan((*pgtype.UndecodedBytes)(&buf))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, buf, 4)
|
||||
require.Equal(t, buf, []byte{0, 0, 0, 1})
|
||||
require.Len(t, buf, 4)
|
||||
require.Equal(t, buf, []byte{0, 0, 0, 1})
|
||||
})
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestCircleTranscode(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.Circle),
|
||||
|
@ -7,50 +7,49 @@ import (
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCompositeCodecTranscode(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.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 (
|
||||
a text,
|
||||
b int4
|
||||
);`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type ct_test")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type ct_test")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "ct_test")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
dt, err := conn.LoadType(ctx, "ct_test")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
var a string
|
||||
var b int32
|
||||
for _, format := range formats {
|
||||
var a string
|
||||
var b int32
|
||||
|
||||
err := conn.QueryRow(context.Background(), "select $1::ct_test", pgx.QueryResultFormats{format.code},
|
||||
pgtype.CompositeFields{"hi", int32(42)},
|
||||
).Scan(
|
||||
pgtype.CompositeFields{&a, &b},
|
||||
)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.EqualValuesf(t, "hi", a, "%v", format.name)
|
||||
require.EqualValuesf(t, 42, b, "%v", format.name)
|
||||
}
|
||||
err := conn.QueryRow(ctx, "select $1::ct_test", pgx.QueryResultFormats{format.code},
|
||||
pgtype.CompositeFields{"hi", int32(42)},
|
||||
).Scan(
|
||||
pgtype.CompositeFields{&a, &b},
|
||||
)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.EqualValuesf(t, "hi", a, "%v", format.name)
|
||||
require.EqualValuesf(t, 42, b, "%v", format.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type point3d struct {
|
||||
@ -94,118 +93,118 @@ func (p *point3d) ScanIndex(i int) interface{} {
|
||||
func TestCompositeCodecTranscodeStruct(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
_, err := conn.Exec(context.Background(), `drop type if exists point3d;
|
||||
_, err := conn.Exec(ctx, `drop type if exists point3d;
|
||||
|
||||
create type point3d as (
|
||||
x float8,
|
||||
y float8,
|
||||
z float8
|
||||
);`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type point3d")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type point3d")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
dt, err := conn.LoadType(ctx, "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
input := point3d{X: 1, Y: 2, Z: 3}
|
||||
var output point3d
|
||||
err := conn.QueryRow(context.Background(), "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Equalf(t, input, output, "%v", format.name)
|
||||
}
|
||||
for _, format := range formats {
|
||||
input := point3d{X: 1, Y: 2, Z: 3}
|
||||
var output point3d
|
||||
err := conn.QueryRow(ctx, "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Equalf(t, input, output, "%v", format.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompositeCodecTranscodeStructWrapper(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
_, err := conn.Exec(context.Background(), `drop type if exists point3d;
|
||||
_, err := conn.Exec(ctx, `drop type if exists point3d;
|
||||
|
||||
create type point3d as (
|
||||
x float8,
|
||||
y float8,
|
||||
z float8
|
||||
);`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type point3d")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type point3d")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
dt, err := conn.LoadType(ctx, "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
|
||||
type anotherPoint struct {
|
||||
X, Y, Z float64
|
||||
}
|
||||
type anotherPoint struct {
|
||||
X, Y, Z float64
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
input := anotherPoint{X: 1, Y: 2, Z: 3}
|
||||
var output anotherPoint
|
||||
err := conn.QueryRow(context.Background(), "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Equalf(t, input, output, "%v", format.name)
|
||||
}
|
||||
for _, format := range formats {
|
||||
input := anotherPoint{X: 1, Y: 2, Z: 3}
|
||||
var output anotherPoint
|
||||
err := conn.QueryRow(ctx, "select $1::point3d", pgx.QueryResultFormats{format.code}, input).Scan(&output)
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Equalf(t, input, output, "%v", format.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompositeCodecDecodeValue(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
_, err := conn.Exec(context.Background(), `drop type if exists point3d;
|
||||
_, err := conn.Exec(ctx, `drop type if exists point3d;
|
||||
|
||||
create type point3d as (
|
||||
x float8,
|
||||
y float8,
|
||||
z float8
|
||||
);`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type point3d")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type point3d")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
dt, err := conn.LoadType(ctx, "point3d")
|
||||
require.NoError(t, err)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
formats := []struct {
|
||||
name string
|
||||
code int16
|
||||
}{
|
||||
{name: "TextFormat", code: pgx.TextFormatCode},
|
||||
{name: "BinaryFormat", code: pgx.BinaryFormatCode},
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
rows, err := conn.Query(context.Background(), "select '(1,2,3)'::point3d", pgx.QueryResultFormats{format.code})
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.True(t, rows.Next())
|
||||
values, err := rows.Values()
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Lenf(t, values, 1, "%v", format.name)
|
||||
require.Equalf(t, map[string]interface{}{"x": 1.0, "y": 2.0, "z": 3.0}, values[0], "%v", format.name)
|
||||
require.False(t, rows.Next())
|
||||
require.NoErrorf(t, rows.Err(), "%v", format.name)
|
||||
}
|
||||
for _, format := range formats {
|
||||
rows, err := conn.Query(ctx, "select '(1,2,3)'::point3d", pgx.QueryResultFormats{format.code})
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.True(t, rows.Next())
|
||||
values, err := rows.Values()
|
||||
require.NoErrorf(t, err, "%v", format.name)
|
||||
require.Lenf(t, values, 1, "%v", format.name)
|
||||
require.Equalf(t, map[string]interface{}{"x": 1.0, "y": 2.0, "z": 3.0}, values[0], "%v", format.name)
|
||||
require.False(t, rows.Next())
|
||||
require.NoErrorf(t, rows.Err(), "%v", format.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
@ -18,7 +19,7 @@ func isExpectedEqTime(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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(-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))},
|
||||
|
@ -4,66 +4,66 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestEnumCodec(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.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');`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type enum_test")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type enum_test")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "enum_test")
|
||||
require.NoError(t, err)
|
||||
dt, err := conn.LoadType(ctx, "enum_test")
|
||||
require.NoError(t, err)
|
||||
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
var s string
|
||||
err = conn.QueryRow(context.Background(), `select 'foo'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", s)
|
||||
var s string
|
||||
err = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", s)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select $1::enum_test`, "bar").Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar", s)
|
||||
err = conn.QueryRow(ctx, `select $1::enum_test`, "bar").Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar", s)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select 'foo'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", s)
|
||||
err = conn.QueryRow(ctx, `select 'foo'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "foo", s)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select $1::enum_test`, "bar").Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar", s)
|
||||
err = conn.QueryRow(ctx, `select $1::enum_test`, "bar").Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "bar", s)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select 'baz'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "baz", s)
|
||||
err = conn.QueryRow(ctx, `select 'baz'::enum_test`).Scan(&s)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "baz", s)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnumCodecValues(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.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');`)
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(context.Background(), "drop type enum_test")
|
||||
require.NoError(t, err)
|
||||
defer conn.Exec(ctx, "drop type enum_test")
|
||||
|
||||
dt, err := conn.LoadType(context.Background(), "enum_test")
|
||||
require.NoError(t, err)
|
||||
dt, err := conn.LoadType(ctx, "enum_test")
|
||||
require.NoError(t, err)
|
||||
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
conn.TypeMap().RegisterType(dt)
|
||||
|
||||
rows, err := conn.Query(context.Background(), `select 'foo'::enum_test`)
|
||||
require.NoError(t, err)
|
||||
require.True(t, rows.Next())
|
||||
values, err := rows.Values()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, values, []interface{}{"foo"})
|
||||
rows, err := conn.Query(ctx, `select 'foo'::enum_test`)
|
||||
require.NoError(t, err)
|
||||
require.True(t, rows.Next())
|
||||
values, err := rows.Values()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, values, []interface{}{"foo"})
|
||||
})
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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: 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})},
|
||||
|
@ -1,14 +1,15 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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: 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})},
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"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 {
|
||||
@ -52,30 +52,22 @@ func isExpectedEqMapStringPointerString(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
func TestHstoreCodec(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
ctr := defaultConnTestRunner
|
||||
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
var hstoreOID uint32
|
||||
err := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'hstore'`).Scan(&hstoreOID)
|
||||
if err != nil {
|
||||
t.Skipf("Skipping: cannot find hstore OID")
|
||||
}
|
||||
|
||||
var hstoreOID uint32
|
||||
err := conn.QueryRow(context.Background(), `select oid from pg_type where typname = 'hstore'`).Scan(&hstoreOID)
|
||||
if err != nil {
|
||||
t.Skipf("Skipping: cannot find hstore OID")
|
||||
}
|
||||
|
||||
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},
|
||||
conn.TypeMap().RegisterType(&pgtype.Type{Name: "hstore", OID: hstoreOID, Codec: pgtype.HstoreCodec{}})
|
||||
}
|
||||
|
||||
fs := func(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
tests := []testutil.TranscodeTestCase{
|
||||
tests := []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
map[string]string{},
|
||||
new(map[string]string),
|
||||
@ -134,25 +126,25 @@ func TestHstoreCodec(t *testing.T) {
|
||||
// Special key values
|
||||
|
||||
// at beginning
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{s + "foo": "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{s + "foo": "bar"}),
|
||||
})
|
||||
// in middle
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo" + s + "bar": "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo" + s + "bar": "bar"}),
|
||||
})
|
||||
// at end
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo" + s: "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo" + s: "bar"}),
|
||||
})
|
||||
// is key
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{s: "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{s: "bar"}),
|
||||
@ -161,32 +153,30 @@ func TestHstoreCodec(t *testing.T) {
|
||||
// Special value values
|
||||
|
||||
// at beginning
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo": s + "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo": s + "bar"}),
|
||||
})
|
||||
// in middle
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo": "foo" + s + "bar"},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s + "bar"}),
|
||||
})
|
||||
// at end
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo": "foo" + s},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo": "foo" + s}),
|
||||
})
|
||||
// is key
|
||||
tests = append(tests, testutil.TranscodeTestCase{
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{
|
||||
map[string]string{"foo": s},
|
||||
new(map[string]string),
|
||||
isExpectedEqMapStringString(map[string]string{"foo": s}),
|
||||
})
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
testutil.RunTranscodeTestsFormat(t, "hstore", tests, conn, format.name, format.code)
|
||||
}
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, pgxtest.KnownOIDQueryExecModes, "hstore", tests)
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"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 {
|
||||
@ -18,7 +19,7 @@ func isExpectedEqIPNet(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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, "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"))},
|
||||
@ -37,7 +38,7 @@ func TestInetTranscode(t *testing.T) {
|
||||
func TestCidrTranscode(t *testing.T) {
|
||||
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, "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"))},
|
||||
|
@ -2,15 +2,16 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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))},
|
||||
{int16(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) {
|
||||
testutil.RunTranscodeTests(t, "int4", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int4", []pgxtest.ValueRoundTripTest{
|
||||
{int8(1), new(int32), isExpectedEq(int32(1))},
|
||||
{int16(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) {
|
||||
testutil.RunTranscodeTests(t, "int8", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int8", []pgxtest.ValueRoundTripTest{
|
||||
{int8(1), new(int64), isExpectedEq(int64(1))},
|
||||
{int16(1), new(int64), isExpectedEq(int64(1))},
|
||||
{int32(1), new(int64), isExpectedEq(int64(1))},
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
<% [2, 4, 8].each do |pg_byte_size| %>
|
||||
<% pg_bit_size = pg_byte_size * 8 %>
|
||||
func TestInt<%= pg_byte_size %>Codec(t *testing.T) {
|
||||
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))},
|
||||
{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))},
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,23 +18,22 @@ import (
|
||||
<% rows_columns.each do |rows, columns| %>
|
||||
<% [["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) {
|
||||
conn := testutil.MustConnectPgx(b)
|
||||
defer testutil.MustCloseContext(b, conn)
|
||||
|
||||
b.ResetTimer()
|
||||
var v [<%= columns %>]<%= go_type %>
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := conn.QueryFunc(
|
||||
context.Background(),
|
||||
`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{}{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>},
|
||||
func(pgx.QueryFuncRow) error { return nil },
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
|
||||
b.ResetTimer()
|
||||
var v [<%= columns %>]<%= go_type %>
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := conn.QueryFunc(
|
||||
ctx,
|
||||
`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{}{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>},
|
||||
func(pgx.QueryFuncRow) error { return nil },
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
<% end %>
|
||||
<% end %>
|
||||
@ -44,23 +43,22 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go
|
||||
<% [10, 100, 1000].each do |array_size| %>
|
||||
<% [["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) {
|
||||
conn := testutil.MustConnectPgx(b)
|
||||
defer testutil.MustCloseContext(b, conn)
|
||||
|
||||
b.ResetTimer()
|
||||
var v []int32
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := conn.QueryFunc(
|
||||
context.Background(),
|
||||
`select array_agg(n) from generate_series(1, <%= array_size %>) n`,
|
||||
[]interface{}{pgx.QueryResultFormats{<%= format_code %>}},
|
||||
[]interface{}{&v},
|
||||
func(pgx.QueryFuncRow) error { return nil },
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
defaultConnTestRunner.RunTest(context.Background(), b, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
|
||||
b.ResetTimer()
|
||||
var v []int32
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := conn.QueryFunc(
|
||||
ctx,
|
||||
`select array_agg(n) from generate_series(1, <%= array_size %>) n`,
|
||||
[]interface{}{pgx.QueryResultFormats{<%= format_code %>}},
|
||||
[]interface{}{&v},
|
||||
func(pgx.QueryFuncRow) error { return nil },
|
||||
)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
@ -1,15 +1,16 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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},
|
||||
new(pgtype.Interval),
|
||||
|
@ -1,9 +1,10 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func isExpectedEqMap(a interface{}) func(interface{}) bool {
|
||||
@ -39,7 +40,15 @@ func TestJSONCodec(t *testing.T) {
|
||||
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("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
|
||||
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},
|
||||
@ -47,10 +56,5 @@ func TestJSONCodec(t *testing.T) {
|
||||
{[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)},
|
||||
{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})},
|
||||
{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))},
|
||||
})
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestJSONBTranscode(t *testing.T) {
|
||||
@ -12,7 +13,15 @@ func TestJSONBTranscode(t *testing.T) {
|
||||
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("null"), new([]byte), isExpectedEqBytes([]byte("null"))},
|
||||
{[]byte("42"), new([]byte), isExpectedEqBytes([]byte("42"))},
|
||||
@ -20,10 +29,5 @@ func TestJSONBTranscode(t *testing.T) {
|
||||
{[]byte(`"hello"`), new(string), isExpectedEq(`"hello"`)},
|
||||
{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})},
|
||||
{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))},
|
||||
})
|
||||
}
|
||||
|
@ -4,30 +4,32 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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 {
|
||||
t.Skip("Skipping due to no line type")
|
||||
if _, ok := conn.TypeMap().TypeForName("line"); !ok {
|
||||
t.Skip("Skipping due to no line type")
|
||||
}
|
||||
|
||||
// line may exist but not be usable on 9.3 :(
|
||||
var isPG93 bool
|
||||
err := conn.QueryRow(context.Background(), "select version() ~ '9.3'").Scan(&isPG93)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if isPG93 {
|
||||
t.Skip("Skipping due to unimplemented line type in PG 9.3")
|
||||
}
|
||||
}
|
||||
|
||||
// line may exist but not be usable on 9.3 :(
|
||||
var isPG93 bool
|
||||
err := conn.QueryRow(context.Background(), "select version() ~ '9.3'").Scan(&isPG93)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if isPG93 {
|
||||
t.Skip("Skipping due to unimplemented line type in PG 9.3")
|
||||
}
|
||||
|
||||
testutil.RunTranscodeTests(t, "line", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "line", []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
pgtype.Line{
|
||||
A: 1.23, B: 4.56, C: 7.89012345,
|
||||
|
@ -1,16 +1,17 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestLsegTranscode(t *testing.T) {
|
||||
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{
|
||||
P: [2]pgtype.Vec2{{3.14, 1.678}, {7.1, 5.2345678901}},
|
||||
|
@ -2,10 +2,11 @@ package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
|
||||
@ -28,7 +29,8 @@ func isExpectedEqHardwareAddr(a interface{}) func(interface{}) bool {
|
||||
func TestMacaddrCodec(t *testing.T) {
|
||||
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"),
|
||||
new(net.HardwareAddr),
|
||||
|
@ -9,8 +9,9 @@ import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"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/require"
|
||||
)
|
||||
@ -77,7 +78,7 @@ func TestNumericCodec(t *testing.T) {
|
||||
max.Add(max, big.NewInt(1))
|
||||
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, "3.14159"), new(pgtype.Numeric), isExpectedEqNumeric(mustParseNumeric(t, "3.14159"))},
|
||||
{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")
|
||||
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))},
|
||||
{float32(math.Inf(1)), new(float32), isExpectedEq(float32(math.Inf(1)))},
|
||||
{math.Inf(-1), new(float64), isExpectedEq(math.Inf(-1))},
|
||||
@ -159,54 +160,54 @@ func TestNumericCodecFuzz(t *testing.T) {
|
||||
max := &big.Int{}
|
||||
max.SetString("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", 10)
|
||||
|
||||
tests := make([]testutil.TranscodeTestCase, 0, 2000)
|
||||
tests := make([]pgxtest.ValueRoundTripTest, 0, 2000)
|
||||
for i := 0; i < 10; i++ {
|
||||
for j := -50; j < 50; j++ {
|
||||
num := (&big.Int{}).Rand(r, max)
|
||||
|
||||
n := pgtype.Numeric{Int: num, Exp: int32(j), Valid: true}
|
||||
tests = append(tests, testutil.TranscodeTestCase{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{n, new(pgtype.Numeric), isExpectedEqNumeric(n)})
|
||||
|
||||
negNum := &big.Int{}
|
||||
negNum.Neg(num)
|
||||
n = pgtype.Numeric{Int: negNum, Exp: int32(j), Valid: true}
|
||||
tests = append(tests, 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) {
|
||||
skipCockroachDB(t, "server formats numeric text format differently")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
for i, tt := range []struct {
|
||||
decString string
|
||||
}{
|
||||
{"NaN"},
|
||||
{"0"},
|
||||
{"1"},
|
||||
{"-1"},
|
||||
{"1000000000000000000"},
|
||||
{"1234.56789"},
|
||||
{"1.56789"},
|
||||
{"0.00000000000056789"},
|
||||
{"0.00123000"},
|
||||
{"123e-3"},
|
||||
{"243723409723490243842378942378901237502734019231380123e23790"},
|
||||
{"3409823409243892349028349023482934092340892390101e-14021"},
|
||||
} {
|
||||
var num pgtype.Numeric
|
||||
var pgJSON string
|
||||
err := conn.QueryRow(context.Background(), `select $1::numeric, to_json($1::numeric)`, tt.decString).Scan(&num, &pgJSON)
|
||||
require.NoErrorf(t, err, "%d", i)
|
||||
for i, tt := range []struct {
|
||||
decString string
|
||||
}{
|
||||
{"NaN"},
|
||||
{"0"},
|
||||
{"1"},
|
||||
{"-1"},
|
||||
{"1000000000000000000"},
|
||||
{"1234.56789"},
|
||||
{"1.56789"},
|
||||
{"0.00000000000056789"},
|
||||
{"0.00123000"},
|
||||
{"123e-3"},
|
||||
{"243723409723490243842378942378901237502734019231380123e23790"},
|
||||
{"3409823409243892349028349023482934092340892390101e-14021"},
|
||||
} {
|
||||
var num pgtype.Numeric
|
||||
var pgJSON string
|
||||
err := conn.QueryRow(ctx, `select $1::numeric, to_json($1::numeric)`, tt.decString).Scan(&num, &pgJSON)
|
||||
require.NoErrorf(t, err, "%d", i)
|
||||
|
||||
goJSON, err := json.Marshal(num)
|
||||
require.NoErrorf(t, err, "%d", i)
|
||||
goJSON, err := json.Marshal(num)
|
||||
require.NoErrorf(t, err, "%d", i)
|
||||
|
||||
require.Equal(t, pgJSON, string(goJSON))
|
||||
}
|
||||
require.Equal(t, pgJSON, string(goJSON))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"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 {
|
||||
@ -29,7 +30,7 @@ func isExpectedEqPath(a interface{}) func(interface{}) bool {
|
||||
func TestPathTranscode(t *testing.T) {
|
||||
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{
|
||||
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}},
|
||||
|
@ -374,7 +374,7 @@ func NewMap() *Map {
|
||||
registerDefaultPgTypeVariants("daterange", "_daterange", Daterange{})
|
||||
registerDefaultPgTypeVariants("float4", "_float4", Float4{})
|
||||
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("int2", "_int2", Int2{})
|
||||
registerDefaultPgTypeVariants("int4", "_int4", Int4{})
|
||||
|
@ -1,21 +1,34 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"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/stretchr/testify/assert"
|
||||
"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
|
||||
type _string string
|
||||
type _bool bool
|
||||
@ -70,8 +83,11 @@ func mustParseMacaddr(t testing.TB, s string) net.HardwareAddr {
|
||||
}
|
||||
|
||||
func skipCockroachDB(t testing.TB, msg string) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close(context.Background())
|
||||
|
||||
if conn.PgConn().ParameterStatus("crdb_version") != "" {
|
||||
t.Skip(msg)
|
||||
@ -79,8 +95,11 @@ func skipCockroachDB(t testing.TB, msg string) {
|
||||
}
|
||||
|
||||
func skipPostgreSQLVersionLessThan(t testing.TB, minVersion int64) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer conn.Close(context.Background())
|
||||
|
||||
serverVersionStr := conn.PgConn().ParameterStatus("server_version")
|
||||
serverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)
|
||||
|
@ -1,18 +1,19 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPointCodec(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.Point),
|
||||
|
@ -1,10 +1,11 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"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 {
|
||||
@ -29,7 +30,7 @@ func isExpectedEqPolygon(a interface{}) func(interface{}) bool {
|
||||
func TestPolygonTranscode(t *testing.T) {
|
||||
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{
|
||||
P: []pgtype.Vec2{{3.14, 1.678901234}, {7.1, 5.234}, {5.0, 3.234}},
|
||||
|
@ -1,22 +1,24 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestQcharTranscode(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support qchar")
|
||||
|
||||
var tests []testutil.TranscodeTestCase
|
||||
var tests []pgxtest.ValueRoundTripTest
|
||||
for i := 0; i <= math.MaxUint8; i++ {
|
||||
tests = append(tests, testutil.TranscodeTestCase{rune(i), new(rune), isExpectedEq(rune(i))})
|
||||
tests = append(tests, testutil.TranscodeTestCase{byte(i), new(byte), isExpectedEq(byte(i))})
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{rune(i), new(rune), isExpectedEq(rune(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, testutil.TranscodeTestCase{nil, new(*byte), isExpectedEq((*byte)(nil))})
|
||||
tests = append(tests, pgxtest.ValueRoundTripTest{nil, new(*rune), isExpectedEq((*rune)(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)
|
||||
}
|
||||
|
@ -4,15 +4,16 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRangeCodecTranscode(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.Int4range),
|
||||
@ -39,9 +40,12 @@ func TestRangeCodecTranscode(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},
|
||||
new(pgtype.Float8range),
|
||||
@ -70,90 +74,90 @@ func TestRangeCodecTranscodeCompatibleRangeElementTypes(t *testing.T) {
|
||||
func TestRangeCodecScanRangeTwiceWithUnbounded(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
|
||||
var r pgtype.Int4range
|
||||
var r pgtype.Int4range
|
||||
|
||||
err := conn.QueryRow(context.Background(), `select '[1,5)'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
err := conn.QueryRow(context.Background(), `select '[1,5)'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{Int32: 1, Valid: true},
|
||||
Upper: pgtype.Int4{Int32: 5, Valid: true},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{Int32: 1, Valid: true},
|
||||
Upper: pgtype.Int4{Int32: 5, Valid: true},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select '[1,)'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
err = conn.QueryRow(ctx, `select '[1,)'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{Int32: 1, Valid: true},
|
||||
Upper: pgtype.Int4{},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Unbounded,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{Int32: 1, Valid: true},
|
||||
Upper: pgtype.Int4{},
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Unbounded,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
|
||||
err = conn.QueryRow(context.Background(), `select 'empty'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
err = conn.QueryRow(ctx, `select 'empty'::int4range`).Scan(&r)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{},
|
||||
Upper: pgtype.Int4{},
|
||||
LowerType: pgtype.Empty,
|
||||
UpperType: pgtype.Empty,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
require.Equal(
|
||||
t,
|
||||
pgtype.Int4range{
|
||||
Lower: pgtype.Int4{},
|
||||
Upper: pgtype.Int4{},
|
||||
LowerType: pgtype.Empty,
|
||||
UpperType: pgtype.Empty,
|
||||
Valid: true,
|
||||
},
|
||||
r,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRangeCodecDecodeValue(t *testing.T) {
|
||||
skipCockroachDB(t, "Server does not support range types (see https://github.com/cockroachdb/cockroach/issues/27791)")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
|
||||
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select '[1,5)'::int4range`,
|
||||
expected: pgtype.GenericRange{
|
||||
Lower: int32(1),
|
||||
Upper: int32(5),
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Valid: true,
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select '[1,5)'::int4range`,
|
||||
expected: pgtype.GenericRange{
|
||||
Lower: int32(1),
|
||||
Upper: int32(5),
|
||||
LowerType: pgtype.Inclusive,
|
||||
UpperType: pgtype.Exclusive,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(context.Background(), tt.sql)
|
||||
require.NoError(t, err)
|
||||
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(ctx, tt.sql)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -4,72 +4,70 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRecordCodec(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
var a string
|
||||
var b int32
|
||||
err := conn.QueryRow(ctx, `select row('foo'::text, 42::int4)`).Scan(pgtype.CompositeFields{&a, &b})
|
||||
require.NoError(t, err)
|
||||
|
||||
var a string
|
||||
var b int32
|
||||
err := conn.QueryRow(context.Background(), `select row('foo'::text, 42::int4)`).Scan(pgtype.CompositeFields{&a, &b})
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, "foo", a)
|
||||
require.Equal(t, int32(42), b)
|
||||
require.Equal(t, "foo", a)
|
||||
require.Equal(t, int32(42), b)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordCodecDecodeValue(t *testing.T) {
|
||||
skipCockroachDB(t, "Server converts row int4 to int8")
|
||||
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select row()`,
|
||||
expected: []interface{}{},
|
||||
},
|
||||
{
|
||||
sql: `select row('foo'::text, 42::int4)`,
|
||||
expected: []interface{}{"foo", int32(42)},
|
||||
},
|
||||
{
|
||||
sql: `select row(100.0::float4, 1.09::float4)`,
|
||||
expected: []interface{}{float32(100), float32(1.09)},
|
||||
},
|
||||
{
|
||||
sql: `select row('foo'::text, array[1, 2, null, 4]::int4[], 42::int4)`,
|
||||
expected: []interface{}{"foo", []interface{}{int32(1), int32(2), nil, int32(4)}, int32(42)},
|
||||
},
|
||||
{
|
||||
sql: `select row(null)`,
|
||||
expected: []interface{}{nil},
|
||||
},
|
||||
{
|
||||
sql: `select null::record`,
|
||||
expected: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(context.Background(), tt.sql)
|
||||
require.NoError(t, err)
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
|
||||
for _, tt := range []struct {
|
||||
sql string
|
||||
expected interface{}
|
||||
}{
|
||||
{
|
||||
sql: `select row()`,
|
||||
expected: []interface{}{},
|
||||
},
|
||||
{
|
||||
sql: `select row('foo'::text, 42::int4)`,
|
||||
expected: []interface{}{"foo", int32(42)},
|
||||
},
|
||||
{
|
||||
sql: `select row(100.0::float4, 1.09::float4)`,
|
||||
expected: []interface{}{float32(100), float32(1.09)},
|
||||
},
|
||||
{
|
||||
sql: `select row('foo'::text, array[1, 2, null, 4]::int4[], 42::int4)`,
|
||||
expected: []interface{}{"foo", []interface{}{int32(1), int32(2), nil, int32(4)}, int32(42)},
|
||||
},
|
||||
{
|
||||
sql: `select row(null)`,
|
||||
expected: []interface{}{nil},
|
||||
},
|
||||
{
|
||||
sql: `select null::record`,
|
||||
expected: nil,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.sql, func(t *testing.T) {
|
||||
rows, err := conn.Query(context.Background(), tt.sql)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, values, 1)
|
||||
require.Equal(t, tt.expected, values[0])
|
||||
}
|
||||
|
||||
require.NoError(t, rows.Err())
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@ -17,7 +18,7 @@ func (someFmtStringer) String() string {
|
||||
|
||||
func TestTextCodec(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.Text),
|
||||
@ -31,6 +32,10 @@ func TestTextCodec(t *testing.T) {
|
||||
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
||||
{"foo", new(string), isExpectedEq("foo")},
|
||||
{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'))},
|
||||
})
|
||||
}
|
||||
@ -47,7 +52,7 @@ func TestTextCodec(t *testing.T) {
|
||||
//
|
||||
// So this is simply a smoke test of the name type.
|
||||
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},
|
||||
new(pgtype.Text),
|
||||
@ -67,7 +72,7 @@ func TestTextCodecName(t *testing.T) {
|
||||
func TestTextCodecBPChar(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.Text),
|
||||
@ -94,12 +99,12 @@ func TestTextCodecBPChar(t *testing.T) {
|
||||
//
|
||||
// It only supports the text format.
|
||||
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)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
|
||||
testutil.RunTranscodeTestsFormat(t, "aclitem", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true},
|
||||
new(pgtype.Text),
|
||||
@ -107,33 +112,33 @@ func TestTextCodecACLItem(t *testing.T) {
|
||||
},
|
||||
{pgtype.Text{}, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
||||
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
||||
}, conn, "Text", pgtype.TextFormatCode)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
ctr := defaultConnTestRunner
|
||||
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
|
||||
// of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
|
||||
roleWithSpecialCharacters := ` tricky, ' } " \ test user `
|
||||
|
||||
// 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.
|
||||
roleWithSpecialCharacters := ` tricky, ' } " \ test user `
|
||||
commandTag, err := conn.Exec(ctx, `select * from pg_roles where rolname = $1`, roleWithSpecialCharacters)
|
||||
require.NoError(t, err)
|
||||
|
||||
commandTag, err := conn.Exec(ctx, `select * from pg_roles where rolname = $1`, roleWithSpecialCharacters)
|
||||
require.NoError(t, err)
|
||||
|
||||
if commandTag.RowsAffected() == 0 {
|
||||
t.Skipf("Role with special characters does not exist.")
|
||||
if commandTag.RowsAffected() == 0 {
|
||||
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},
|
||||
new(pgtype.Text),
|
||||
isExpectedEq(pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}),
|
||||
},
|
||||
}, conn, "Text", pgtype.TextFormatCode)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTextMarshalJSON(t *testing.T) {
|
||||
|
@ -1,16 +1,17 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestTIDCodec(t *testing.T) {
|
||||
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},
|
||||
new(pgtype.TID),
|
||||
|
@ -1,15 +1,16 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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},
|
||||
new(pgtype.Time),
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
@ -127,12 +128,29 @@ func (encodePlanTimestampCodecText) Encode(value interface{}, buf []byte) (newBu
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !ts.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var s string
|
||||
|
||||
switch ts.InfinityModifier {
|
||||
case Finite:
|
||||
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)
|
||||
|
||||
if bc {
|
||||
s = s + " BC"
|
||||
}
|
||||
case Infinity:
|
||||
s = "infinity"
|
||||
case NegativeInfinity:
|
||||
@ -219,11 +237,21 @@ func (scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst interface{})
|
||||
case "-infinity":
|
||||
ts = Timestamp{Valid: true, InfinityModifier: -Infinity}
|
||||
default:
|
||||
bc := false
|
||||
if strings.HasSuffix(sbuf, " BC") {
|
||||
sbuf = sbuf[:len(sbuf)-3]
|
||||
bc = true
|
||||
}
|
||||
tim, err := time.Parse(pgTimestampFormat, sbuf)
|
||||
if err != nil {
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,21 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTimestampCodec(t *testing.T) {
|
||||
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(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))},
|
||||
@ -34,19 +40,18 @@ func TestTimestampCodec(t *testing.T) {
|
||||
|
||||
// https://github.com/jackc/pgx/v4/pgtype/pull/128
|
||||
func TestTimestampTranscodeBigTimeBinary(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
|
||||
var out pgtype.Timestamp
|
||||
|
||||
in := &pgtype.Timestamp{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
|
||||
var out pgtype.Timestamp
|
||||
err := conn.QueryRow(ctx, "select $1::timestamp", in).Scan(&out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := conn.QueryRow(context.Background(), "select $1::timestamp", in).Scan(&out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
require.Equal(t, in.Valid, out.Valid)
|
||||
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
|
||||
require.Equal(t, in.Valid, out.Valid)
|
||||
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgtype/issues/74
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
@ -184,11 +185,30 @@ func (encodePlanTimestamptzCodecText) Encode(value interface{}, buf []byte) (new
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !ts.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var s string
|
||||
|
||||
switch ts.InfinityModifier {
|
||||
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:
|
||||
s = "infinity"
|
||||
case NegativeInfinity:
|
||||
@ -267,6 +287,12 @@ func (scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst interfac
|
||||
case "-infinity":
|
||||
tstz = Timestamptz{Valid: true, InfinityModifier: -Infinity}
|
||||
default:
|
||||
bc := false
|
||||
if strings.HasSuffix(sbuf, " BC") {
|
||||
sbuf = sbuf[:len(sbuf)-3]
|
||||
bc = true
|
||||
}
|
||||
|
||||
var format string
|
||||
if len(sbuf) >= 9 && (sbuf[len(sbuf)-9] == '-' || sbuf[len(sbuf)-9] == '+') {
|
||||
format = pgTimestamptzSecondFormat
|
||||
@ -281,6 +307,11 @@ func (scanPlanTextTimestamptzToTimestamptzScanner) Scan(src []byte, dst interfac
|
||||
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}
|
||||
}
|
||||
|
||||
|
@ -5,15 +5,21 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
pgx "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTimestamptzCodec(t *testing.T) {
|
||||
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(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))},
|
||||
@ -34,19 +40,18 @@ func TestTimestamptzCodec(t *testing.T) {
|
||||
|
||||
// https://github.com/jackc/pgx/v4/pgtype/pull/128
|
||||
func TestTimestamptzTranscodeBigTimeBinary(t *testing.T) {
|
||||
conn := testutil.MustConnectPgx(t)
|
||||
defer testutil.MustCloseContext(t, conn)
|
||||
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
||||
in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
|
||||
var out pgtype.Timestamptz
|
||||
|
||||
in := &pgtype.Timestamptz{Time: time.Date(294276, 12, 31, 23, 59, 59, 999999000, time.UTC), Valid: true}
|
||||
var out pgtype.Timestamptz
|
||||
err := conn.QueryRow(ctx, "select $1::timestamptz", in).Scan(&out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := conn.QueryRow(context.Background(), "select $1::timestamptz", in).Scan(&out)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
require.Equal(t, in.Valid, out.Valid)
|
||||
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
|
||||
require.Equal(t, in.Valid, out.Valid)
|
||||
require.Truef(t, in.Time.Equal(out.Time), "expected %v got %v", in.Time, out.Time)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgtype/issues/74
|
||||
|
@ -1,14 +1,15 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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},
|
||||
new(pgtype.Uint32),
|
||||
|
@ -1,31 +1,22 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
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},
|
||||
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}),
|
||||
},
|
||||
{
|
||||
[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",
|
||||
new(pgtype.UUID),
|
||||
@ -40,6 +31,19 @@ func TestUUIDCodec(t *testing.T) {
|
||||
{pgtype.UUID{}, 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) {
|
||||
|
@ -1,10 +1,11 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func isExpectedEq(a interface{}) func(interface{}) bool {
|
||||
@ -14,7 +15,7 @@ func isExpectedEq(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
func TestFloat8Transcode(t *testing.T) {
|
||||
testutil.RunTranscodeTests(t, "float8", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "float8", []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
(zeronull.Float8)(1),
|
||||
new(zeronull.Float8),
|
||||
|
@ -2,14 +2,15 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestInt2Transcode(t *testing.T) {
|
||||
testutil.RunTranscodeTests(t, "int2", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "int2", []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
(zeronull.Int2)(1),
|
||||
new(zeronull.Int2),
|
||||
@ -29,7 +30,7 @@ func TestInt2Transcode(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),
|
||||
new(zeronull.Int4),
|
||||
@ -49,7 +50,7 @@ func TestInt4Transcode(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),
|
||||
new(zeronull.Int8),
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
<% [2, 4, 8].each do |pg_byte_size| %>
|
||||
<% pg_bit_size = pg_byte_size * 8 %>
|
||||
func TestInt<%= pg_byte_size %>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),
|
||||
new(zeronull.Int<%= pg_byte_size %>),
|
||||
|
@ -1,14 +1,15 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func TestTextTranscode(t *testing.T) {
|
||||
testutil.RunTranscodeTests(t, "text", []testutil.TranscodeTestCase{
|
||||
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "text", []pgxtest.ValueRoundTripTest{
|
||||
{
|
||||
(zeronull.Text)("foo"),
|
||||
new(zeronull.Text),
|
||||
|
@ -1,11 +1,12 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func isExpectedEqTimestamp(a interface{}) func(interface{}) bool {
|
||||
@ -18,7 +19,7 @@ func isExpectedEqTimestamp(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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)),
|
||||
new(zeronull.Timestamp),
|
||||
|
@ -1,11 +1,12 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool {
|
||||
@ -18,7 +19,7 @@ func isExpectedEqTimestamptz(a interface{}) func(interface{}) bool {
|
||||
}
|
||||
|
||||
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)),
|
||||
new(zeronull.Timestamptz),
|
||||
|
@ -1,14 +1,15 @@
|
||||
package zeronull_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype/testutil"
|
||||
"github.com/jackc/pgx/v5/pgtype/zeronull"
|
||||
"github.com/jackc/pgx/v5/pgxtest"
|
||||
)
|
||||
|
||||
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}),
|
||||
new(zeronull.UUID),
|
||||
|
17
pgtype/zeronull/zeronull.go
Normal file
17
pgtype/zeronull/zeronull.go
Normal 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")
|
||||
}
|
26
pgtype/zeronull/zeronull_test.go
Normal file
26
pgtype/zeronull/zeronull_test.go
Normal 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())
|
||||
}
|
||||
}
|
@ -3,11 +3,28 @@ package pgxtest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"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 with reasonable default values.
|
||||
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)) {
|
||||
t.Helper()
|
||||
|
||||
config := ctr.CreateConfig(ctx, t)
|
||||
conn, err := pgx.ConnectConfig(ctx, config)
|
||||
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.
|
||||
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 {
|
||||
modes = []pgx.QueryExecMode{
|
||||
pgx.QueryExecModeCacheStatement,
|
||||
pgx.QueryExecModeCacheDescribe,
|
||||
pgx.QueryExecModeDescribeExec,
|
||||
pgx.QueryExecModeExec,
|
||||
pgx.QueryExecModeSimpleProtocol,
|
||||
}
|
||||
modes = AllQueryExecModes
|
||||
}
|
||||
|
||||
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.
|
||||
func SkipCockroachDB(t testing.TB, conn *pgx.Conn, msg string) {
|
||||
if conn.PgConn().ParameterStatus("crdb_version") != "" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user