mirror of
https://github.com/jackc/pgx.git
synced 2025-05-29 18:53:50 +00:00
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.
184 lines
6.0 KiB
Go
184 lines
6.0 KiB
Go
package pgtype_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
pgx "github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/jackc/pgx/v5/pgxtest"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type someFmtStringer struct{}
|
|
|
|
func (someFmtStringer) String() string {
|
|
return "some fmt.Stringer"
|
|
}
|
|
|
|
func TestTextCodec(t *testing.T) {
|
|
for _, pgTypeName := range []string{"text", "varchar"} {
|
|
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, pgTypeName, []pgxtest.ValueRoundTripTest{
|
|
{
|
|
pgtype.Text{String: "", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "", Valid: true}),
|
|
},
|
|
{
|
|
pgtype.Text{String: "foo", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "foo", Valid: true}),
|
|
},
|
|
{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'))},
|
|
})
|
|
}
|
|
}
|
|
|
|
// name is PostgreSQL's special 63-byte data type, used for identifiers like table names. The pg_class.relname column
|
|
// is a good example of where the name data type is used.
|
|
//
|
|
// TextCodec does not do length checking. Inputting a longer name into PostgreSQL will result in silent truncation to
|
|
// 63 bytes.
|
|
//
|
|
// Length checking would be possible with a Codec specialized for "name" but it would be perfect because a
|
|
// custom-compiled PostgreSQL could have set NAMEDATALEN to a different value rather than the default 63.
|
|
//
|
|
// So this is simply a smoke test of the name type.
|
|
func TestTextCodecName(t *testing.T) {
|
|
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "name", []pgxtest.ValueRoundTripTest{
|
|
{
|
|
pgtype.Text{String: "", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "", Valid: true}),
|
|
},
|
|
{
|
|
pgtype.Text{String: "foo", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "foo", Valid: true}),
|
|
},
|
|
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
|
{"foo", new(string), isExpectedEq("foo")},
|
|
})
|
|
}
|
|
|
|
// Test fixed length char types like char(3)
|
|
func TestTextCodecBPChar(t *testing.T) {
|
|
skipCockroachDB(t, "Server does not properly handle bpchar with multi-byte character")
|
|
|
|
pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "char(3)", []pgxtest.ValueRoundTripTest{
|
|
{
|
|
pgtype.Text{String: "a ", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "a ", Valid: true}),
|
|
},
|
|
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
|
{" ", new(string), isExpectedEq(" ")},
|
|
{"", new(string), isExpectedEq(" ")},
|
|
{" 嗨 ", new(string), isExpectedEq(" 嗨 ")},
|
|
})
|
|
}
|
|
|
|
// ACLItem is used for PostgreSQL's aclitem data type. A sample aclitem
|
|
// might look like this:
|
|
//
|
|
// postgres=arwdDxt/postgres
|
|
//
|
|
// Note, however, that because the user/role name part of an aclitem is
|
|
// an identifier, it follows all the usual formatting rules for SQL
|
|
// identifiers: if it contains spaces and other special characters,
|
|
// it should appear in double-quotes:
|
|
//
|
|
// postgres=arwdDxt/"role with spaces"
|
|
//
|
|
// It only supports the text format.
|
|
func TestTextCodecACLItem(t *testing.T) {
|
|
ctr := defaultConnTestRunner
|
|
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
|
pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
|
|
}
|
|
|
|
pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
|
|
{
|
|
pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true},
|
|
new(pgtype.Text),
|
|
isExpectedEq(pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true}),
|
|
},
|
|
{pgtype.Text{}, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
|
{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
|
|
})
|
|
}
|
|
|
|
func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
|
|
ctr := defaultConnTestRunner
|
|
ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
|
|
pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
|
|
|
|
// 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)
|
|
|
|
if commandTag.RowsAffected() == 0 {
|
|
t.Skipf("Role with special characters does not exist.")
|
|
}
|
|
}
|
|
|
|
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}),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestTextMarshalJSON(t *testing.T) {
|
|
successfulTests := []struct {
|
|
source pgtype.Text
|
|
result string
|
|
}{
|
|
{source: pgtype.Text{String: ""}, result: "null"},
|
|
{source: pgtype.Text{String: "a", Valid: true}, result: "\"a\""},
|
|
}
|
|
for i, tt := range successfulTests {
|
|
r, err := tt.source.MarshalJSON()
|
|
if err != nil {
|
|
t.Errorf("%d: %v", i, err)
|
|
}
|
|
|
|
if string(r) != tt.result {
|
|
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, string(r))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTextUnmarshalJSON(t *testing.T) {
|
|
successfulTests := []struct {
|
|
source string
|
|
result pgtype.Text
|
|
}{
|
|
{source: "null", result: pgtype.Text{String: ""}},
|
|
{source: "\"a\"", result: pgtype.Text{String: "a", Valid: true}},
|
|
}
|
|
for i, tt := range successfulTests {
|
|
var r pgtype.Text
|
|
err := r.UnmarshalJSON([]byte(tt.source))
|
|
if err != nil {
|
|
t.Errorf("%d: %v", i, err)
|
|
}
|
|
|
|
if r != tt.result {
|
|
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
|
|
}
|
|
}
|
|
}
|