aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/jackc/pgx/values_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/jackc/pgx/values_test.go')
-rw-r--r--vendor/github.com/jackc/pgx/values_test.go1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/vendor/github.com/jackc/pgx/values_test.go b/vendor/github.com/jackc/pgx/values_test.go
new file mode 100644
index 0000000..42d5bd3
--- /dev/null
+++ b/vendor/github.com/jackc/pgx/values_test.go
@@ -0,0 +1,1183 @@
+package pgx_test
+
+import (
+ "bytes"
+ "net"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/jackc/pgx"
+)
+
+func TestDateTranscode(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ dates := []time.Time{
+ time.Date(1, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1000, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1600, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1700, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1800, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1900, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1990, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(1999, 12, 31, 0, 0, 0, 0, time.Local),
+ time.Date(2000, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(2001, 1, 2, 0, 0, 0, 0, time.Local),
+ time.Date(2004, 2, 29, 0, 0, 0, 0, time.Local),
+ time.Date(2013, 7, 4, 0, 0, 0, 0, time.Local),
+ time.Date(2013, 12, 25, 0, 0, 0, 0, time.Local),
+ time.Date(2029, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(2081, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(2096, 2, 29, 0, 0, 0, 0, time.Local),
+ time.Date(2550, 1, 1, 0, 0, 0, 0, time.Local),
+ time.Date(9999, 12, 31, 0, 0, 0, 0, time.Local),
+ }
+
+ for _, actualDate := range dates {
+ var d time.Time
+
+ err := conn.QueryRow("select $1::date", actualDate).Scan(&d)
+ if err != nil {
+ t.Fatalf("Unexpected failure on QueryRow Scan: %v", err)
+ }
+ if !actualDate.Equal(d) {
+ t.Errorf("Did not transcode date successfully: %v is not %v", d, actualDate)
+ }
+ }
+}
+
+func TestTimestampTzTranscode(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ inputTime := time.Date(2013, 1, 2, 3, 4, 5, 6000, time.Local)
+
+ var outputTime time.Time
+
+ err := conn.QueryRow("select $1::timestamptz", inputTime).Scan(&outputTime)
+ if err != nil {
+ t.Fatalf("QueryRow Scan failed: %v", err)
+ }
+ if !inputTime.Equal(outputTime) {
+ t.Errorf("Did not transcode time successfully: %v is not %v", outputTime, inputTime)
+ }
+
+ err = conn.QueryRow("select $1::timestamptz", inputTime).Scan(&outputTime)
+ if err != nil {
+ t.Fatalf("QueryRow Scan failed: %v", err)
+ }
+ if !inputTime.Equal(outputTime) {
+ t.Errorf("Did not transcode time successfully: %v is not %v", outputTime, inputTime)
+ }
+}
+
+func TestJsonAndJsonbTranscode(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ for _, oid := range []pgx.Oid{pgx.JsonOid, pgx.JsonbOid} {
+ if _, ok := conn.PgTypes[oid]; !ok {
+ return // No JSON/JSONB type -- must be running against old PostgreSQL
+ }
+
+ for _, format := range []int16{pgx.TextFormatCode, pgx.BinaryFormatCode} {
+ pgtype := conn.PgTypes[oid]
+ pgtype.DefaultFormat = format
+ conn.PgTypes[oid] = pgtype
+
+ typename := conn.PgTypes[oid].Name
+
+ testJsonString(t, conn, typename, format)
+ testJsonStringPointer(t, conn, typename, format)
+ testJsonSingleLevelStringMap(t, conn, typename, format)
+ testJsonNestedMap(t, conn, typename, format)
+ testJsonStringArray(t, conn, typename, format)
+ testJsonInt64Array(t, conn, typename, format)
+ testJsonInt16ArrayFailureDueToOverflow(t, conn, typename, format)
+ testJsonStruct(t, conn, typename, format)
+ }
+ }
+}
+
+func testJsonString(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := `{"key": "value"}`
+ expectedOutput := map[string]string{"key": "value"}
+ var output map[string]string
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ return
+ }
+
+ if !reflect.DeepEqual(expectedOutput, output) {
+ t.Errorf("%s %d: Did not transcode map[string]string successfully: %v is not %v", typename, format, expectedOutput, output)
+ return
+ }
+}
+
+func testJsonStringPointer(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := `{"key": "value"}`
+ expectedOutput := map[string]string{"key": "value"}
+ var output map[string]string
+ err := conn.QueryRow("select $1::"+typename, &input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ return
+ }
+
+ if !reflect.DeepEqual(expectedOutput, output) {
+ t.Errorf("%s %d: Did not transcode map[string]string successfully: %v is not %v", typename, format, expectedOutput, output)
+ return
+ }
+}
+
+func testJsonSingleLevelStringMap(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := map[string]string{"key": "value"}
+ var output map[string]string
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ return
+ }
+
+ if !reflect.DeepEqual(input, output) {
+ t.Errorf("%s %d: Did not transcode map[string]string successfully: %v is not %v", typename, format, input, output)
+ return
+ }
+}
+
+func testJsonNestedMap(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := map[string]interface{}{
+ "name": "Uncanny",
+ "stats": map[string]interface{}{"hp": float64(107), "maxhp": float64(150)},
+ "inventory": []interface{}{"phone", "key"},
+ }
+ var output map[string]interface{}
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ return
+ }
+
+ if !reflect.DeepEqual(input, output) {
+ t.Errorf("%s %d: Did not transcode map[string]interface{} successfully: %v is not %v", typename, format, input, output)
+ return
+ }
+}
+
+func testJsonStringArray(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := []string{"foo", "bar", "baz"}
+ var output []string
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ }
+
+ if !reflect.DeepEqual(input, output) {
+ t.Errorf("%s %d: Did not transcode []string successfully: %v is not %v", typename, format, input, output)
+ }
+}
+
+func testJsonInt64Array(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := []int64{1, 2, 234432}
+ var output []int64
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ }
+
+ if !reflect.DeepEqual(input, output) {
+ t.Errorf("%s %d: Did not transcode []int64 successfully: %v is not %v", typename, format, input, output)
+ }
+}
+
+func testJsonInt16ArrayFailureDueToOverflow(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ input := []int{1, 2, 234432}
+ var output []int16
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err == nil || err.Error() != "can't scan into dest[0]: json: cannot unmarshal number 234432 into Go value of type int16" {
+ t.Errorf("%s %d: Expected *json.UnmarkalTypeError, but got %v", typename, format, err)
+ }
+}
+
+func testJsonStruct(t *testing.T, conn *pgx.Conn, typename string, format int16) {
+ type person struct {
+ Name string `json:"name"`
+ Age int `json:"age"`
+ }
+
+ input := person{
+ Name: "John",
+ Age: 42,
+ }
+
+ var output person
+
+ err := conn.QueryRow("select $1::"+typename, input).Scan(&output)
+ if err != nil {
+ t.Errorf("%s %d: QueryRow Scan failed: %v", typename, format, err)
+ }
+
+ if !reflect.DeepEqual(input, output) {
+ t.Errorf("%s %d: Did not transcode struct successfully: %v is not %v", typename, format, input, output)
+ }
+}
+
+func mustParseCIDR(t *testing.T, s string) net.IPNet {
+ _, ipnet, err := net.ParseCIDR(s)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return *ipnet
+}
+
+func TestStringToNotTextTypeTranscode(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ input := "01086ee0-4963-4e35-9116-30c173a8d0bd"
+
+ var output string
+ err := conn.QueryRow("select $1::uuid", input).Scan(&output)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if input != output {
+ t.Errorf("uuid: Did not transcode string successfully: %s is not %s", input, output)
+ }
+
+ err = conn.QueryRow("select $1::uuid", &input).Scan(&output)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if input != output {
+ t.Errorf("uuid: Did not transcode pointer to string successfully: %s is not %s", input, output)
+ }
+}
+
+func TestInetCidrTranscodeIPNet(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ value net.IPNet
+ }{
+ {"select $1::inet", mustParseCIDR(t, "0.0.0.0/32")},
+ {"select $1::inet", mustParseCIDR(t, "127.0.0.1/32")},
+ {"select $1::inet", mustParseCIDR(t, "12.34.56.0/32")},
+ {"select $1::inet", mustParseCIDR(t, "192.168.1.0/24")},
+ {"select $1::inet", mustParseCIDR(t, "255.0.0.0/8")},
+ {"select $1::inet", mustParseCIDR(t, "255.255.255.255/32")},
+ {"select $1::inet", mustParseCIDR(t, "::/128")},
+ {"select $1::inet", mustParseCIDR(t, "::/0")},
+ {"select $1::inet", mustParseCIDR(t, "::1/128")},
+ {"select $1::inet", mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128")},
+ {"select $1::cidr", mustParseCIDR(t, "0.0.0.0/32")},
+ {"select $1::cidr", mustParseCIDR(t, "127.0.0.1/32")},
+ {"select $1::cidr", mustParseCIDR(t, "12.34.56.0/32")},
+ {"select $1::cidr", mustParseCIDR(t, "192.168.1.0/24")},
+ {"select $1::cidr", mustParseCIDR(t, "255.0.0.0/8")},
+ {"select $1::cidr", mustParseCIDR(t, "255.255.255.255/32")},
+ {"select $1::cidr", mustParseCIDR(t, "::/128")},
+ {"select $1::cidr", mustParseCIDR(t, "::/0")},
+ {"select $1::cidr", mustParseCIDR(t, "::1/128")},
+ {"select $1::cidr", mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128")},
+ }
+
+ for i, tt := range tests {
+ var actual net.IPNet
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ if actual.String() != tt.value.String() {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.value, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestInetCidrTranscodeIP(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ value net.IP
+ }{
+ {"select $1::inet", net.ParseIP("0.0.0.0")},
+ {"select $1::inet", net.ParseIP("127.0.0.1")},
+ {"select $1::inet", net.ParseIP("12.34.56.0")},
+ {"select $1::inet", net.ParseIP("255.255.255.255")},
+ {"select $1::inet", net.ParseIP("::1")},
+ {"select $1::inet", net.ParseIP("2607:f8b0:4009:80b::200e")},
+ {"select $1::cidr", net.ParseIP("0.0.0.0")},
+ {"select $1::cidr", net.ParseIP("127.0.0.1")},
+ {"select $1::cidr", net.ParseIP("12.34.56.0")},
+ {"select $1::cidr", net.ParseIP("255.255.255.255")},
+ {"select $1::cidr", net.ParseIP("::1")},
+ {"select $1::cidr", net.ParseIP("2607:f8b0:4009:80b::200e")},
+ }
+
+ for i, tt := range tests {
+ var actual net.IP
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ if !actual.Equal(tt.value) {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.value, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+
+ failTests := []struct {
+ sql string
+ value net.IPNet
+ }{
+ {"select $1::inet", mustParseCIDR(t, "192.168.1.0/24")},
+ {"select $1::cidr", mustParseCIDR(t, "192.168.1.0/24")},
+ }
+ for i, tt := range failTests {
+ var actual net.IP
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if !strings.Contains(err.Error(), "Cannot decode netmask") {
+ t.Errorf("%d. Expected failure cannot decode netmask, but got: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestInetCidrArrayTranscodeIPNet(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ value []net.IPNet
+ }{
+ {
+ "select $1::inet[]",
+ []net.IPNet{
+ mustParseCIDR(t, "0.0.0.0/32"),
+ mustParseCIDR(t, "127.0.0.1/32"),
+ mustParseCIDR(t, "12.34.56.0/32"),
+ mustParseCIDR(t, "192.168.1.0/24"),
+ mustParseCIDR(t, "255.0.0.0/8"),
+ mustParseCIDR(t, "255.255.255.255/32"),
+ mustParseCIDR(t, "::/128"),
+ mustParseCIDR(t, "::/0"),
+ mustParseCIDR(t, "::1/128"),
+ mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"),
+ },
+ },
+ {
+ "select $1::cidr[]",
+ []net.IPNet{
+ mustParseCIDR(t, "0.0.0.0/32"),
+ mustParseCIDR(t, "127.0.0.1/32"),
+ mustParseCIDR(t, "12.34.56.0/32"),
+ mustParseCIDR(t, "192.168.1.0/24"),
+ mustParseCIDR(t, "255.0.0.0/8"),
+ mustParseCIDR(t, "255.255.255.255/32"),
+ mustParseCIDR(t, "::/128"),
+ mustParseCIDR(t, "::/0"),
+ mustParseCIDR(t, "::1/128"),
+ mustParseCIDR(t, "2607:f8b0:4009:80b::200e/128"),
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ var actual []net.IPNet
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ if !reflect.DeepEqual(actual, tt.value) {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.value, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestInetCidrArrayTranscodeIP(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ value []net.IP
+ }{
+ {
+ "select $1::inet[]",
+ []net.IP{
+ net.ParseIP("0.0.0.0"),
+ net.ParseIP("127.0.0.1"),
+ net.ParseIP("12.34.56.0"),
+ net.ParseIP("255.255.255.255"),
+ net.ParseIP("2607:f8b0:4009:80b::200e"),
+ },
+ },
+ {
+ "select $1::cidr[]",
+ []net.IP{
+ net.ParseIP("0.0.0.0"),
+ net.ParseIP("127.0.0.1"),
+ net.ParseIP("12.34.56.0"),
+ net.ParseIP("255.255.255.255"),
+ net.ParseIP("2607:f8b0:4009:80b::200e"),
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ var actual []net.IP
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ if !reflect.DeepEqual(actual, tt.value) {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.value, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+
+ failTests := []struct {
+ sql string
+ value []net.IPNet
+ }{
+ {
+ "select $1::inet[]",
+ []net.IPNet{
+ mustParseCIDR(t, "12.34.56.0/32"),
+ mustParseCIDR(t, "192.168.1.0/24"),
+ },
+ },
+ {
+ "select $1::cidr[]",
+ []net.IPNet{
+ mustParseCIDR(t, "12.34.56.0/32"),
+ mustParseCIDR(t, "192.168.1.0/24"),
+ },
+ },
+ }
+
+ for i, tt := range failTests {
+ var actual []net.IP
+
+ err := conn.QueryRow(tt.sql, tt.value).Scan(&actual)
+ if err == nil || !strings.Contains(err.Error(), "Cannot decode netmask") {
+ t.Errorf("%d. Expected failure cannot decode netmask, but got: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestInetCidrTranscodeWithJustIP(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ value string
+ }{
+ {"select $1::inet", "0.0.0.0/32"},
+ {"select $1::inet", "127.0.0.1/32"},
+ {"select $1::inet", "12.34.56.0/32"},
+ {"select $1::inet", "255.255.255.255/32"},
+ {"select $1::inet", "::/128"},
+ {"select $1::inet", "2607:f8b0:4009:80b::200e/128"},
+ {"select $1::cidr", "0.0.0.0/32"},
+ {"select $1::cidr", "127.0.0.1/32"},
+ {"select $1::cidr", "12.34.56.0/32"},
+ {"select $1::cidr", "255.255.255.255/32"},
+ {"select $1::cidr", "::/128"},
+ {"select $1::cidr", "2607:f8b0:4009:80b::200e/128"},
+ }
+
+ for i, tt := range tests {
+ expected := mustParseCIDR(t, tt.value)
+ var actual net.IPNet
+
+ err := conn.QueryRow(tt.sql, expected.IP).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, value -> %v)", i, err, tt.sql, tt.value)
+ continue
+ }
+
+ if actual.String() != expected.String() {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.value, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestNullX(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ type allTypes struct {
+ s pgx.NullString
+ i16 pgx.NullInt16
+ i32 pgx.NullInt32
+ c pgx.NullChar
+ a pgx.NullAclItem
+ n pgx.NullName
+ oid pgx.NullOid
+ xid pgx.NullXid
+ cid pgx.NullCid
+ tid pgx.NullTid
+ i64 pgx.NullInt64
+ f32 pgx.NullFloat32
+ f64 pgx.NullFloat64
+ b pgx.NullBool
+ t pgx.NullTime
+ }
+
+ var actual, zero allTypes
+
+ tests := []struct {
+ sql string
+ queryArgs []interface{}
+ scanArgs []interface{}
+ expected allTypes
+ }{
+ {"select $1::text", []interface{}{pgx.NullString{String: "foo", Valid: true}}, []interface{}{&actual.s}, allTypes{s: pgx.NullString{String: "foo", Valid: true}}},
+ {"select $1::text", []interface{}{pgx.NullString{String: "foo", Valid: false}}, []interface{}{&actual.s}, allTypes{s: pgx.NullString{String: "", Valid: false}}},
+ {"select $1::int2", []interface{}{pgx.NullInt16{Int16: 1, Valid: true}}, []interface{}{&actual.i16}, allTypes{i16: pgx.NullInt16{Int16: 1, Valid: true}}},
+ {"select $1::int2", []interface{}{pgx.NullInt16{Int16: 1, Valid: false}}, []interface{}{&actual.i16}, allTypes{i16: pgx.NullInt16{Int16: 0, Valid: false}}},
+ {"select $1::int4", []interface{}{pgx.NullInt32{Int32: 1, Valid: true}}, []interface{}{&actual.i32}, allTypes{i32: pgx.NullInt32{Int32: 1, Valid: true}}},
+ {"select $1::int4", []interface{}{pgx.NullInt32{Int32: 1, Valid: false}}, []interface{}{&actual.i32}, allTypes{i32: pgx.NullInt32{Int32: 0, Valid: false}}},
+ {"select $1::oid", []interface{}{pgx.NullOid{Oid: 1, Valid: true}}, []interface{}{&actual.oid}, allTypes{oid: pgx.NullOid{Oid: 1, Valid: true}}},
+ {"select $1::oid", []interface{}{pgx.NullOid{Oid: 1, Valid: false}}, []interface{}{&actual.oid}, allTypes{oid: pgx.NullOid{Oid: 0, Valid: false}}},
+ {"select $1::oid", []interface{}{pgx.NullOid{Oid: 4294967295, Valid: true}}, []interface{}{&actual.oid}, allTypes{oid: pgx.NullOid{Oid: 4294967295, Valid: true}}},
+ {"select $1::xid", []interface{}{pgx.NullXid{Xid: 1, Valid: true}}, []interface{}{&actual.xid}, allTypes{xid: pgx.NullXid{Xid: 1, Valid: true}}},
+ {"select $1::xid", []interface{}{pgx.NullXid{Xid: 1, Valid: false}}, []interface{}{&actual.xid}, allTypes{xid: pgx.NullXid{Xid: 0, Valid: false}}},
+ {"select $1::xid", []interface{}{pgx.NullXid{Xid: 4294967295, Valid: true}}, []interface{}{&actual.xid}, allTypes{xid: pgx.NullXid{Xid: 4294967295, Valid: true}}},
+ {"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 1, Valid: true}}},
+ {"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 1, Valid: false}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 0, Valid: false}}},
+ {"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 255, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 255, Valid: true}}},
+ {"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: true}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "foo", Valid: true}}},
+ {"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: false}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "", Valid: false}}},
+ {"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}},
+ {"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: false}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "", Valid: false}}},
+ // A tricky (and valid) aclitem can still be used, especially with Go's useful backticks
+ {"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}}},
+ {"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 1, Valid: true}}},
+ {"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: false}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 0, Valid: false}}},
+ {"select $1::cid", []interface{}{pgx.NullCid{Cid: 4294967295, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 4294967295, Valid: true}}},
+ {"select $1::tid", []interface{}{pgx.NullTid{Tid: pgx.Tid{BlockNumber: 1, OffsetNumber: 1}, Valid: true}}, []interface{}{&actual.tid}, allTypes{tid: pgx.NullTid{Tid: pgx.Tid{BlockNumber: 1, OffsetNumber: 1}, Valid: true}}},
+ {"select $1::tid", []interface{}{pgx.NullTid{Tid: pgx.Tid{BlockNumber: 1, OffsetNumber: 1}, Valid: false}}, []interface{}{&actual.tid}, allTypes{tid: pgx.NullTid{Tid: pgx.Tid{BlockNumber: 0, OffsetNumber: 0}, Valid: false}}},
+ {"select $1::tid", []interface{}{pgx.NullTid{Tid: pgx.Tid{BlockNumber: 4294967295, OffsetNumber: 65535}, Valid: true}}, []interface{}{&actual.tid}, allTypes{tid: pgx.NullTid{Tid: pgx.Tid{BlockNumber: 4294967295, OffsetNumber: 65535}, Valid: true}}},
+ {"select $1::int8", []interface{}{pgx.NullInt64{Int64: 1, Valid: true}}, []interface{}{&actual.i64}, allTypes{i64: pgx.NullInt64{Int64: 1, Valid: true}}},
+ {"select $1::int8", []interface{}{pgx.NullInt64{Int64: 1, Valid: false}}, []interface{}{&actual.i64}, allTypes{i64: pgx.NullInt64{Int64: 0, Valid: false}}},
+ {"select $1::float4", []interface{}{pgx.NullFloat32{Float32: 1.23, Valid: true}}, []interface{}{&actual.f32}, allTypes{f32: pgx.NullFloat32{Float32: 1.23, Valid: true}}},
+ {"select $1::float4", []interface{}{pgx.NullFloat32{Float32: 1.23, Valid: false}}, []interface{}{&actual.f32}, allTypes{f32: pgx.NullFloat32{Float32: 0, Valid: false}}},
+ {"select $1::float8", []interface{}{pgx.NullFloat64{Float64: 1.23, Valid: true}}, []interface{}{&actual.f64}, allTypes{f64: pgx.NullFloat64{Float64: 1.23, Valid: true}}},
+ {"select $1::float8", []interface{}{pgx.NullFloat64{Float64: 1.23, Valid: false}}, []interface{}{&actual.f64}, allTypes{f64: pgx.NullFloat64{Float64: 0, Valid: false}}},
+ {"select $1::bool", []interface{}{pgx.NullBool{Bool: true, Valid: true}}, []interface{}{&actual.b}, allTypes{b: pgx.NullBool{Bool: true, Valid: true}}},
+ {"select $1::bool", []interface{}{pgx.NullBool{Bool: true, Valid: false}}, []interface{}{&actual.b}, allTypes{b: pgx.NullBool{Bool: false, Valid: false}}},
+ {"select $1::timestamptz", []interface{}{pgx.NullTime{Time: time.Unix(123, 5000), Valid: true}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Unix(123, 5000), Valid: true}}},
+ {"select $1::timestamptz", []interface{}{pgx.NullTime{Time: time.Unix(123, 5000), Valid: false}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Time{}, Valid: false}}},
+ {"select $1::timestamp", []interface{}{pgx.NullTime{Time: time.Unix(123, 5000), Valid: true}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Unix(123, 5000), Valid: true}}},
+ {"select $1::timestamp", []interface{}{pgx.NullTime{Time: time.Unix(123, 5000), Valid: false}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Time{}, Valid: false}}},
+ {"select $1::date", []interface{}{pgx.NullTime{Time: time.Date(1990, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Date(1990, 1, 1, 0, 0, 0, 0, time.Local), Valid: true}}},
+ {"select $1::date", []interface{}{pgx.NullTime{Time: time.Date(1990, 1, 1, 0, 0, 0, 0, time.Local), Valid: false}}, []interface{}{&actual.t}, allTypes{t: pgx.NullTime{Time: time.Time{}, Valid: false}}},
+ {"select 42::int4, $1::float8", []interface{}{pgx.NullFloat64{Float64: 1.23, Valid: true}}, []interface{}{&actual.i32, &actual.f64}, allTypes{i32: pgx.NullInt32{Int32: 42, Valid: true}, f64: pgx.NullFloat64{Float64: 1.23, Valid: true}}},
+ }
+
+ for i, tt := range tests {
+ actual = zero
+
+ err := conn.QueryRow(tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, queryArgs -> %v)", i, err, tt.sql, tt.queryArgs)
+ }
+
+ if actual != tt.expected {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v, queryArgs -> %v)", i, tt.expected, actual, tt.sql, tt.queryArgs)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func assertAclItemSlicesEqual(t *testing.T, query, scan []pgx.AclItem) {
+ if !reflect.DeepEqual(query, scan) {
+ t.Errorf("failed to encode aclitem[]\n EXPECTED: %d %v\n ACTUAL: %d %v", len(query), query, len(scan), scan)
+ }
+}
+
+func TestAclArrayDecoding(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ sql := "select $1::aclitem[]"
+ var scan []pgx.AclItem
+
+ tests := []struct {
+ query []pgx.AclItem
+ }{
+ {
+ []pgx.AclItem{},
+ },
+ {
+ []pgx.AclItem{"=r/postgres"},
+ },
+ {
+ []pgx.AclItem{"=r/postgres", "postgres=arwdDxt/postgres"},
+ },
+ {
+ []pgx.AclItem{"=r/postgres", "postgres=arwdDxt/postgres", `postgres=arwdDxt/" tricky, ' } "" \ test user "`},
+ },
+ }
+ for i, tt := range tests {
+ err := conn.QueryRow(sql, tt.query).Scan(&scan)
+ if err != nil {
+ // t.Errorf(`%d. error reading array: %v`, i, err)
+ t.Errorf(`%d. error reading array: %v query: %s`, i, err, tt.query)
+ if pgerr, ok := err.(pgx.PgError); ok {
+ t.Errorf(`%d. error reading array (detail): %s`, i, pgerr.Detail)
+ }
+ continue
+ }
+ assertAclItemSlicesEqual(t, tt.query, scan)
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestArrayDecoding(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ query interface{}
+ scan interface{}
+ assert func(*testing.T, interface{}, interface{})
+ }{
+ {
+ "select $1::bool[]", []bool{true, false, true}, &[]bool{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]bool))) {
+ t.Errorf("failed to encode bool[]")
+ }
+ },
+ },
+ {
+ "select $1::smallint[]", []int16{2, 4, 484, 32767}, &[]int16{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]int16))) {
+ t.Errorf("failed to encode smallint[]")
+ }
+ },
+ },
+ {
+ "select $1::smallint[]", []uint16{2, 4, 484, 32767}, &[]uint16{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]uint16))) {
+ t.Errorf("failed to encode smallint[]")
+ }
+ },
+ },
+ {
+ "select $1::int[]", []int32{2, 4, 484}, &[]int32{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]int32))) {
+ t.Errorf("failed to encode int[]")
+ }
+ },
+ },
+ {
+ "select $1::int[]", []uint32{2, 4, 484, 2147483647}, &[]uint32{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]uint32))) {
+ t.Errorf("failed to encode int[]")
+ }
+ },
+ },
+ {
+ "select $1::bigint[]", []int64{2, 4, 484, 9223372036854775807}, &[]int64{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]int64))) {
+ t.Errorf("failed to encode bigint[]")
+ }
+ },
+ },
+ {
+ "select $1::bigint[]", []uint64{2, 4, 484, 9223372036854775807}, &[]uint64{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]uint64))) {
+ t.Errorf("failed to encode bigint[]")
+ }
+ },
+ },
+ {
+ "select $1::text[]", []string{"it's", "over", "9000!"}, &[]string{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]string))) {
+ t.Errorf("failed to encode text[]")
+ }
+ },
+ },
+ {
+ "select $1::timestamp[]", []time.Time{time.Unix(323232, 0), time.Unix(3239949334, 00)}, &[]time.Time{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]time.Time))) {
+ t.Errorf("failed to encode time.Time[] to timestamp[]")
+ }
+ },
+ },
+ {
+ "select $1::timestamptz[]", []time.Time{time.Unix(323232, 0), time.Unix(3239949334, 00)}, &[]time.Time{},
+ func(t *testing.T, query, scan interface{}) {
+ if !reflect.DeepEqual(query, *(scan.(*[]time.Time))) {
+ t.Errorf("failed to encode time.Time[] to timestamptz[]")
+ }
+ },
+ },
+ {
+ "select $1::bytea[]", [][]byte{{0, 1, 2, 3}, {4, 5, 6, 7}}, &[][]byte{},
+ func(t *testing.T, query, scan interface{}) {
+ queryBytesSliceSlice := query.([][]byte)
+ scanBytesSliceSlice := *(scan.(*[][]byte))
+ if len(queryBytesSliceSlice) != len(scanBytesSliceSlice) {
+ t.Errorf("failed to encode byte[][] to bytea[]: expected %d to equal %d", len(queryBytesSliceSlice), len(scanBytesSliceSlice))
+ }
+ for i := range queryBytesSliceSlice {
+ qb := queryBytesSliceSlice[i]
+ sb := scanBytesSliceSlice[i]
+ if !bytes.Equal(qb, sb) {
+ t.Errorf("failed to encode byte[][] to bytea[]: expected %v to equal %v", qb, sb)
+ }
+ }
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ err := conn.QueryRow(tt.sql, tt.query).Scan(tt.scan)
+ if err != nil {
+ t.Errorf(`%d. error reading array: %v`, i, err)
+ continue
+ }
+ tt.assert(t, tt.query, tt.scan)
+ ensureConnValid(t, conn)
+ }
+}
+
+type shortScanner struct{}
+
+func (*shortScanner) Scan(r *pgx.ValueReader) error {
+ r.ReadByte()
+ return nil
+}
+
+func TestShortScanner(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ rows, err := conn.Query("select 'ab', 'cd' union select 'cd', 'ef'")
+ if err != nil {
+ t.Error(err)
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var s1, s2 shortScanner
+ err = rows.Scan(&s1, &s2)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ ensureConnValid(t, conn)
+}
+
+func TestEmptyArrayDecoding(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ var val []string
+
+ err := conn.QueryRow("select array[]::text[]").Scan(&val)
+ if err != nil {
+ t.Errorf(`error reading array: %v`, err)
+ }
+ if len(val) != 0 {
+ t.Errorf("Expected 0 values, got %d", len(val))
+ }
+
+ var n, m int32
+
+ err = conn.QueryRow("select 1::integer, array[]::text[], 42::integer").Scan(&n, &val, &m)
+ if err != nil {
+ t.Errorf(`error reading array: %v`, err)
+ }
+ if len(val) != 0 {
+ t.Errorf("Expected 0 values, got %d", len(val))
+ }
+ if n != 1 {
+ t.Errorf("Expected n to be 1, but it was %d", n)
+ }
+ if m != 42 {
+ t.Errorf("Expected n to be 42, but it was %d", n)
+ }
+
+ rows, err := conn.Query("select 1::integer, array['test']::text[] union select 2::integer, array[]::text[] union select 3::integer, array['test']::text[]")
+ if err != nil {
+ t.Errorf(`error retrieving rows with array: %v`, err)
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ err = rows.Scan(&n, &val)
+ if err != nil {
+ t.Errorf(`error reading array: %v`, err)
+ }
+ }
+
+ ensureConnValid(t, conn)
+}
+
+func TestNullXMismatch(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ type allTypes struct {
+ s pgx.NullString
+ i16 pgx.NullInt16
+ i32 pgx.NullInt32
+ i64 pgx.NullInt64
+ f32 pgx.NullFloat32
+ f64 pgx.NullFloat64
+ b pgx.NullBool
+ t pgx.NullTime
+ }
+
+ var actual, zero allTypes
+
+ tests := []struct {
+ sql string
+ queryArgs []interface{}
+ scanArgs []interface{}
+ err string
+ }{
+ {"select $1::date", []interface{}{pgx.NullString{String: "foo", Valid: true}}, []interface{}{&actual.s}, "invalid input syntax for type date"},
+ {"select $1::date", []interface{}{pgx.NullInt16{Int16: 1, Valid: true}}, []interface{}{&actual.i16}, "cannot encode into OID 1082"},
+ {"select $1::date", []interface{}{pgx.NullInt32{Int32: 1, Valid: true}}, []interface{}{&actual.i32}, "cannot encode into OID 1082"},
+ {"select $1::date", []interface{}{pgx.NullInt64{Int64: 1, Valid: true}}, []interface{}{&actual.i64}, "cannot encode into OID 1082"},
+ {"select $1::date", []interface{}{pgx.NullFloat32{Float32: 1.23, Valid: true}}, []interface{}{&actual.f32}, "cannot encode into OID 1082"},
+ {"select $1::date", []interface{}{pgx.NullFloat64{Float64: 1.23, Valid: true}}, []interface{}{&actual.f64}, "cannot encode into OID 1082"},
+ {"select $1::date", []interface{}{pgx.NullBool{Bool: true, Valid: true}}, []interface{}{&actual.b}, "cannot encode into OID 1082"},
+ {"select $1::int4", []interface{}{pgx.NullTime{Time: time.Unix(123, 5000), Valid: true}}, []interface{}{&actual.t}, "cannot encode into OID 23"},
+ }
+
+ for i, tt := range tests {
+ actual = zero
+
+ err := conn.QueryRow(tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)
+ if err == nil || !strings.Contains(err.Error(), tt.err) {
+ t.Errorf(`%d. Expected error to contain "%s", but it didn't: %v`, i, tt.err, err)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestPointerPointer(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ type allTypes struct {
+ s *string
+ i16 *int16
+ i32 *int32
+ i64 *int64
+ f32 *float32
+ f64 *float64
+ b *bool
+ t *time.Time
+ }
+
+ var actual, zero, expected allTypes
+
+ {
+ s := "foo"
+ expected.s = &s
+ i16 := int16(1)
+ expected.i16 = &i16
+ i32 := int32(1)
+ expected.i32 = &i32
+ i64 := int64(1)
+ expected.i64 = &i64
+ f32 := float32(1.23)
+ expected.f32 = &f32
+ f64 := float64(1.23)
+ expected.f64 = &f64
+ b := true
+ expected.b = &b
+ t := time.Unix(123, 5000)
+ expected.t = &t
+ }
+
+ tests := []struct {
+ sql string
+ queryArgs []interface{}
+ scanArgs []interface{}
+ expected allTypes
+ }{
+ {"select $1::text", []interface{}{expected.s}, []interface{}{&actual.s}, allTypes{s: expected.s}},
+ {"select $1::text", []interface{}{zero.s}, []interface{}{&actual.s}, allTypes{}},
+ {"select $1::int2", []interface{}{expected.i16}, []interface{}{&actual.i16}, allTypes{i16: expected.i16}},
+ {"select $1::int2", []interface{}{zero.i16}, []interface{}{&actual.i16}, allTypes{}},
+ {"select $1::int4", []interface{}{expected.i32}, []interface{}{&actual.i32}, allTypes{i32: expected.i32}},
+ {"select $1::int4", []interface{}{zero.i32}, []interface{}{&actual.i32}, allTypes{}},
+ {"select $1::int8", []interface{}{expected.i64}, []interface{}{&actual.i64}, allTypes{i64: expected.i64}},
+ {"select $1::int8", []interface{}{zero.i64}, []interface{}{&actual.i64}, allTypes{}},
+ {"select $1::float4", []interface{}{expected.f32}, []interface{}{&actual.f32}, allTypes{f32: expected.f32}},
+ {"select $1::float4", []interface{}{zero.f32}, []interface{}{&actual.f32}, allTypes{}},
+ {"select $1::float8", []interface{}{expected.f64}, []interface{}{&actual.f64}, allTypes{f64: expected.f64}},
+ {"select $1::float8", []interface{}{zero.f64}, []interface{}{&actual.f64}, allTypes{}},
+ {"select $1::bool", []interface{}{expected.b}, []interface{}{&actual.b}, allTypes{b: expected.b}},
+ {"select $1::bool", []interface{}{zero.b}, []interface{}{&actual.b}, allTypes{}},
+ {"select $1::timestamptz", []interface{}{expected.t}, []interface{}{&actual.t}, allTypes{t: expected.t}},
+ {"select $1::timestamptz", []interface{}{zero.t}, []interface{}{&actual.t}, allTypes{}},
+ {"select $1::timestamp", []interface{}{expected.t}, []interface{}{&actual.t}, allTypes{t: expected.t}},
+ {"select $1::timestamp", []interface{}{zero.t}, []interface{}{&actual.t}, allTypes{}},
+ }
+
+ for i, tt := range tests {
+ actual = zero
+
+ err := conn.QueryRow(tt.sql, tt.queryArgs...).Scan(tt.scanArgs...)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v, queryArgs -> %v)", i, err, tt.sql, tt.queryArgs)
+ }
+
+ if !reflect.DeepEqual(actual, tt.expected) {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v, queryArgs -> %v)", i, tt.expected, actual, tt.sql, tt.queryArgs)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}
+
+func TestPointerPointerNonZero(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ f := "foo"
+ dest := &f
+
+ err := conn.QueryRow("select $1::text", nil).Scan(&dest)
+ if err != nil {
+ t.Errorf("Unexpected failure scanning: %v", err)
+ }
+ if dest != nil {
+ t.Errorf("Expected dest to be nil, got %#v", dest)
+ }
+}
+
+func TestEncodeTypeRename(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ type _int int
+ inInt := _int(3)
+ var outInt _int
+
+ type _int8 int8
+ inInt8 := _int8(3)
+ var outInt8 _int8
+
+ type _int16 int16
+ inInt16 := _int16(3)
+ var outInt16 _int16
+
+ type _int32 int32
+ inInt32 := _int32(4)
+ var outInt32 _int32
+
+ type _int64 int64
+ inInt64 := _int64(5)
+ var outInt64 _int64
+
+ type _uint uint
+ inUint := _uint(6)
+ var outUint _uint
+
+ type _uint8 uint8
+ inUint8 := _uint8(7)
+ var outUint8 _uint8
+
+ type _uint16 uint16
+ inUint16 := _uint16(8)
+ var outUint16 _uint16
+
+ type _uint32 uint32
+ inUint32 := _uint32(9)
+ var outUint32 _uint32
+
+ type _uint64 uint64
+ inUint64 := _uint64(10)
+ var outUint64 _uint64
+
+ type _string string
+ inString := _string("foo")
+ var outString _string
+
+ err := conn.QueryRow("select $1::int, $2::int, $3::int2, $4::int4, $5::int8, $6::int, $7::int, $8::int, $9::int, $10::int, $11::text",
+ inInt, inInt8, inInt16, inInt32, inInt64, inUint, inUint8, inUint16, inUint32, inUint64, inString,
+ ).Scan(&outInt, &outInt8, &outInt16, &outInt32, &outInt64, &outUint, &outUint8, &outUint16, &outUint32, &outUint64, &outString)
+ if err != nil {
+ t.Fatalf("Failed with type rename: %v", err)
+ }
+
+ if inInt != outInt {
+ t.Errorf("int rename: expected %v, got %v", inInt, outInt)
+ }
+
+ if inInt8 != outInt8 {
+ t.Errorf("int8 rename: expected %v, got %v", inInt8, outInt8)
+ }
+
+ if inInt16 != outInt16 {
+ t.Errorf("int16 rename: expected %v, got %v", inInt16, outInt16)
+ }
+
+ if inInt32 != outInt32 {
+ t.Errorf("int32 rename: expected %v, got %v", inInt32, outInt32)
+ }
+
+ if inInt64 != outInt64 {
+ t.Errorf("int64 rename: expected %v, got %v", inInt64, outInt64)
+ }
+
+ if inUint != outUint {
+ t.Errorf("uint rename: expected %v, got %v", inUint, outUint)
+ }
+
+ if inUint8 != outUint8 {
+ t.Errorf("uint8 rename: expected %v, got %v", inUint8, outUint8)
+ }
+
+ if inUint16 != outUint16 {
+ t.Errorf("uint16 rename: expected %v, got %v", inUint16, outUint16)
+ }
+
+ if inUint32 != outUint32 {
+ t.Errorf("uint32 rename: expected %v, got %v", inUint32, outUint32)
+ }
+
+ if inUint64 != outUint64 {
+ t.Errorf("uint64 rename: expected %v, got %v", inUint64, outUint64)
+ }
+
+ if inString != outString {
+ t.Errorf("string rename: expected %v, got %v", inString, outString)
+ }
+
+ ensureConnValid(t, conn)
+}
+
+func TestRowDecode(t *testing.T) {
+ t.Parallel()
+
+ conn := mustConnect(t, *defaultConnConfig)
+ defer closeConn(t, conn)
+
+ tests := []struct {
+ sql string
+ expected []interface{}
+ }{
+ {
+ "select row(1, 'cat', '2015-01-01 08:12:42-00'::timestamptz)",
+ []interface{}{
+ int32(1),
+ "cat",
+ time.Date(2015, 1, 1, 8, 12, 42, 0, time.UTC).Local(),
+ },
+ },
+ }
+
+ for i, tt := range tests {
+ var actual []interface{}
+
+ err := conn.QueryRow(tt.sql).Scan(&actual)
+ if err != nil {
+ t.Errorf("%d. Unexpected failure: %v (sql -> %v)", i, err, tt.sql)
+ continue
+ }
+
+ if !reflect.DeepEqual(actual, tt.expected) {
+ t.Errorf("%d. Expected %v, got %v (sql -> %v)", i, tt.expected, actual, tt.sql)
+ }
+
+ ensureConnValid(t, conn)
+ }
+}