From a8691a70660f601d69cdfb609694cb46c35ea7c2 Mon Sep 17 00:00:00 2001
From: Jack Christensen <jack@jackchristensen.com>
Date: Tue, 10 Sep 2019 17:58:24 -0500
Subject: [PATCH] Add RawValues to Rows

---
 pgxpool/rows.go |  5 +++++
 query_test.go   | 34 ++++++++++++++++++++++++++++++++++
 rows.go         | 10 +++++++++-
 3 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/pgxpool/rows.go b/pgxpool/rows.go
index 59279bc2..a9765d08 100644
--- a/pgxpool/rows.go
+++ b/pgxpool/rows.go
@@ -17,6 +17,7 @@ func (errRows) FieldDescriptions() []pgproto3.FieldDescription { return nil }
 func (errRows) Next() bool                                     { return false }
 func (e errRows) Scan(dest ...interface{}) error               { return e.err }
 func (e errRows) Values() ([]interface{}, error)               { return nil, e.err }
+func (e errRows) RawValues() [][]byte                          { return nil }
 
 type errRow struct {
 	err error
@@ -81,6 +82,10 @@ func (rows *poolRows) Values() ([]interface{}, error) {
 	return values, err
 }
 
+func (rows *poolRows) RawValues() [][]byte {
+	return rows.r.RawValues()
+}
+
 type poolRow struct {
 	r   pgx.Row
 	c   *Conn
diff --git a/query_test.go b/query_test.go
index 8093d017..431135b5 100644
--- a/query_test.go
+++ b/query_test.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"os"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
@@ -300,6 +301,39 @@ func TestRowsScanDoesNotAllowScanningBinaryFormatValuesIntoString(t *testing.T)
 	ensureConnValid(t, conn)
 }
 
+func TestConnQueryRawValues(t *testing.T) {
+	t.Parallel()
+
+	conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
+	defer closeConn(t, conn)
+
+	var rowCount int32
+
+	rows, err := conn.Query(
+		context.Background(),
+		"select 'foo'::text, 'bar'::varchar, n, null, n from generate_series(1,$1) n",
+		pgx.QuerySimpleProtocol(true),
+		10,
+	)
+	require.NoError(t, err)
+	defer rows.Close()
+
+	for rows.Next() {
+		rowCount++
+
+		rawValues := rows.RawValues()
+		assert.Len(t, rawValues, 5)
+		assert.Equal(t, "foo", string(rawValues[0]))
+		assert.Equal(t, "bar", string(rawValues[1]))
+		assert.Equal(t, strconv.FormatInt(int64(rowCount), 10), string(rawValues[2]))
+		assert.Nil(t, rawValues[3])
+		assert.Equal(t, strconv.FormatInt(int64(rowCount), 10), string(rawValues[4]))
+	}
+
+	require.NoError(t, rows.Err())
+	assert.EqualValues(t, 10, rowCount)
+}
+
 // Test that a connection stays valid when query results are closed early
 func TestConnQueryCloseEarly(t *testing.T) {
 	t.Parallel()
diff --git a/rows.go b/rows.go
index 7b3a5f17..379bdc7a 100644
--- a/rows.go
+++ b/rows.go
@@ -40,8 +40,12 @@ type Rows interface {
 	// copy the raw bytes received from PostgreSQL. nil will skip the value entirely.
 	Scan(dest ...interface{}) error
 
-	// Values returns an array of the row values
+	// Values returns the decoded row values.
 	Values() ([]interface{}, error)
+
+	// RawValues returns the unparsed bytes of the row values. The returned [][]byte is only valid until the next Next
+	// call or the Rows is closed. However, the underlying byte data is safe to retain a reference to and mutate.
+	RawValues() [][]byte
 }
 
 // Row is a convenience wrapper over Rows that is returned by QueryRow.
@@ -249,6 +253,10 @@ func (rows *connRows) Values() ([]interface{}, error) {
 	return values, rows.Err()
 }
 
+func (rows *connRows) RawValues() [][]byte {
+	return rows.resultReader.Values()
+}
+
 type scanArgError struct {
 	col int
 	err error