...

Source file src/github.com/jackc/pgx/v5/pgtype/text_test.go

Documentation: github.com/jackc/pgx/v5/pgtype

     1  package pgtype_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	pgx "github.com/jackc/pgx/v5"
     8  	"github.com/jackc/pgx/v5/pgtype"
     9  	"github.com/jackc/pgx/v5/pgxtest"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  type someFmtStringer struct{}
    14  
    15  func (someFmtStringer) String() string {
    16  	return "some fmt.Stringer"
    17  }
    18  
    19  func TestTextCodec(t *testing.T) {
    20  	for _, pgTypeName := range []string{"text", "varchar"} {
    21  		pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, pgTypeName, []pgxtest.ValueRoundTripTest{
    22  			{
    23  				pgtype.Text{String: "", Valid: true},
    24  				new(pgtype.Text),
    25  				isExpectedEq(pgtype.Text{String: "", Valid: true}),
    26  			},
    27  			{
    28  				pgtype.Text{String: "foo", Valid: true},
    29  				new(pgtype.Text),
    30  				isExpectedEq(pgtype.Text{String: "foo", Valid: true}),
    31  			},
    32  			{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
    33  			{"foo", new(string), isExpectedEq("foo")},
    34  			{someFmtStringer{}, new(string), isExpectedEq("some fmt.Stringer")},
    35  		})
    36  	}
    37  }
    38  
    39  // name is PostgreSQL's special 63-byte data type, used for identifiers like table names.  The pg_class.relname column
    40  // is a good example of where the name data type is used.
    41  //
    42  // TextCodec does not do length checking. Inputting a longer name into PostgreSQL will result in silent truncation to
    43  // 63 bytes.
    44  //
    45  // Length checking would be possible with a Codec specialized for "name" but it would be perfect because a
    46  // custom-compiled PostgreSQL could have set NAMEDATALEN to a different value rather than the default 63.
    47  //
    48  // So this is simply a smoke test of the name type.
    49  func TestTextCodecName(t *testing.T) {
    50  	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "name", []pgxtest.ValueRoundTripTest{
    51  		{
    52  			pgtype.Text{String: "", Valid: true},
    53  			new(pgtype.Text),
    54  			isExpectedEq(pgtype.Text{String: "", Valid: true}),
    55  		},
    56  		{
    57  			pgtype.Text{String: "foo", Valid: true},
    58  			new(pgtype.Text),
    59  			isExpectedEq(pgtype.Text{String: "foo", Valid: true}),
    60  		},
    61  		{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
    62  		{"foo", new(string), isExpectedEq("foo")},
    63  	})
    64  }
    65  
    66  // Test fixed length char types like char(3)
    67  func TestTextCodecBPChar(t *testing.T) {
    68  	skipCockroachDB(t, "Server does not properly handle bpchar with multi-byte character")
    69  
    70  	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "char(3)", []pgxtest.ValueRoundTripTest{
    71  		{
    72  			pgtype.Text{String: "a  ", Valid: true},
    73  			new(pgtype.Text),
    74  			isExpectedEq(pgtype.Text{String: "a  ", Valid: true}),
    75  		},
    76  		{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
    77  		{"   ", new(string), isExpectedEq("   ")},
    78  		{"", new(string), isExpectedEq("   ")},
    79  		{" 嗨 ", new(string), isExpectedEq(" 嗨 ")},
    80  	})
    81  }
    82  
    83  // ACLItem is used for PostgreSQL's aclitem data type. A sample aclitem
    84  // might look like this:
    85  //
    86  //	postgres=arwdDxt/postgres
    87  //
    88  // Note, however, that because the user/role name part of an aclitem is
    89  // an identifier, it follows all the usual formatting rules for SQL
    90  // identifiers: if it contains spaces and other special characters,
    91  // it should appear in double-quotes:
    92  //
    93  //	postgres=arwdDxt/"role with spaces"
    94  //
    95  // It only supports the text format.
    96  func TestTextCodecACLItem(t *testing.T) {
    97  	ctr := defaultConnTestRunner
    98  	ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
    99  		pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
   100  	}
   101  
   102  	pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
   103  		{
   104  			pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true},
   105  			new(pgtype.Text),
   106  			isExpectedEq(pgtype.Text{String: "postgres=arwdDxt/postgres", Valid: true}),
   107  		},
   108  		{pgtype.Text{}, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
   109  		{nil, new(pgtype.Text), isExpectedEq(pgtype.Text{})},
   110  	})
   111  }
   112  
   113  func TestTextCodecACLItemRoleWithSpecialCharacters(t *testing.T) {
   114  	ctr := defaultConnTestRunner
   115  	ctr.AfterConnect = func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
   116  		pgxtest.SkipCockroachDB(t, conn, "Server does not support type aclitem")
   117  
   118  		// The tricky test user, below, has to actually exist so that it can be used in a test
   119  		// of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
   120  		roleWithSpecialCharacters := ` tricky, ' } " \ test user `
   121  
   122  		commandTag, err := conn.Exec(ctx, `select * from pg_roles where rolname = $1`, roleWithSpecialCharacters)
   123  		require.NoError(t, err)
   124  
   125  		if commandTag.RowsAffected() == 0 {
   126  			t.Skipf("Role with special characters does not exist.")
   127  		}
   128  	}
   129  
   130  	pgxtest.RunValueRoundTripTests(context.Background(), t, ctr, nil, "aclitem", []pgxtest.ValueRoundTripTest{
   131  		{
   132  			pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true},
   133  			new(pgtype.Text),
   134  			isExpectedEq(pgtype.Text{String: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}),
   135  		},
   136  	})
   137  }
   138  
   139  func TestTextMarshalJSON(t *testing.T) {
   140  	successfulTests := []struct {
   141  		source pgtype.Text
   142  		result string
   143  	}{
   144  		{source: pgtype.Text{String: ""}, result: "null"},
   145  		{source: pgtype.Text{String: "a", Valid: true}, result: "\"a\""},
   146  	}
   147  	for i, tt := range successfulTests {
   148  		r, err := tt.source.MarshalJSON()
   149  		if err != nil {
   150  			t.Errorf("%d: %v", i, err)
   151  		}
   152  
   153  		if string(r) != tt.result {
   154  			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, string(r))
   155  		}
   156  	}
   157  }
   158  
   159  func TestTextUnmarshalJSON(t *testing.T) {
   160  	successfulTests := []struct {
   161  		source string
   162  		result pgtype.Text
   163  	}{
   164  		{source: "null", result: pgtype.Text{String: ""}},
   165  		{source: "\"a\"", result: pgtype.Text{String: "a", Valid: true}},
   166  	}
   167  	for i, tt := range successfulTests {
   168  		var r pgtype.Text
   169  		err := r.UnmarshalJSON([]byte(tt.source))
   170  		if err != nil {
   171  			t.Errorf("%d: %v", i, err)
   172  		}
   173  
   174  		if r != tt.result {
   175  			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, r)
   176  		}
   177  	}
   178  }
   179  

View as plain text