pgx/pgtype/composite_test.go

248 lines
6.1 KiB
Go

package pgtype_test
import (
"context"
"fmt"
"testing"
pgx "github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgtype"
"github.com/stretchr/testify/require"
)
func TestCompositeCodecTranscode(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
_, 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(ctx, "drop type ct_test")
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},
}
for _, format := range formats {
var a string
var b int32
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 {
X, Y, Z float64
}
func (p point3d) IsNull() bool {
return false
}
func (p point3d) Index(i int) any {
switch i {
case 0:
return p.X
case 1:
return p.Y
case 2:
return p.Z
default:
panic("invalid index")
}
}
func (p *point3d) ScanNull() error {
return fmt.Errorf("cannot scan NULL into point3d")
}
func (p *point3d) ScanIndex(i int) any {
switch i {
case 0:
return &p.X
case 1:
return &p.Y
case 2:
return &p.Z
default:
panic("invalid index")
}
}
func TestCompositeCodecTranscodeStruct(t *testing.T) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
_, 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(ctx, "drop type point3d")
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},
}
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) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
_, 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(ctx, "drop type point3d")
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},
}
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(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) {
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
_, 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(ctx, "drop type point3d")
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},
}
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]any{"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)
}
})
}
// Test for composite type from table instead of create type. Table types have system / hidden columns like tableoid,
// cmax, xmax, etc. These are not included when sending or receiving composite types.
//
// https://github.com/jackc/pgx/issues/1576
func TestCompositeCodecTranscodeStructWrapperForTable(t *testing.T) {
skipCockroachDB(t, "Server does not support composite types from table definitions")
defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
_, err := conn.Exec(ctx, `drop table if exists point3d;
create table point3d (
x float8,
y float8,
z float8
);`)
require.NoError(t, err)
defer conn.Exec(ctx, "drop table point3d")
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},
}
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(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)
}
})
}