Simplify []byte scanning

v3-numeric-wip
Jack Christensen 2017-03-18 14:42:36 -05:00
parent 015108be9a
commit 92cff1d961
7 changed files with 39 additions and 39 deletions

5
doc.go
View File

@ -169,10 +169,7 @@ and database/sql/driver.Valuer interfaces.
Raw Bytes Mapping Raw Bytes Mapping
[]byte passed as arguments to Query, QueryRow, and Exec are passed unmodified []byte passed as arguments to Query, QueryRow, and Exec are passed unmodified
to PostgreSQL. In like manner, a *[]byte passed to Scan will be filled with to PostgreSQL.
the raw bytes returned by PostgreSQL. This can be especially useful for reading
varchar, text, json, and jsonb values directly into a []byte and avoiding the
type conversion from string.
Transactions Transactions

View File

@ -49,6 +49,16 @@ func (src *Text) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst) return fmt.Errorf("cannot assign %v to %T", src, dst)
} }
*v = src.String *v = src.String
case *[]byte:
switch src.Status {
case Present:
*v = make([]byte, len(src.String))
copy(*v, src.String)
case Null:
*v = nil
default:
return fmt.Errorf("unknown status")
}
default: default:
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr { if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
el := v.Elem() el := v.Elem()

View File

@ -1,6 +1,7 @@
package pgtype_test package pgtype_test
import ( import (
"bytes"
"reflect" "reflect"
"testing" "testing"
@ -44,7 +45,7 @@ func TestTextAssignTo(t *testing.T) {
var s string var s string
var ps *string var ps *string
simpleTests := []struct { stringTests := []struct {
src pgtype.Text src pgtype.Text
dst interface{} dst interface{}
expected interface{} expected interface{}
@ -53,7 +54,7 @@ func TestTextAssignTo(t *testing.T) {
{src: pgtype.Text{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))}, {src: pgtype.Text{Status: pgtype.Null}, dst: &ps, expected: ((*string)(nil))},
} }
for i, tt := range simpleTests { for i, tt := range stringTests {
err := tt.src.AssignTo(tt.dst) err := tt.src.AssignTo(tt.dst)
if err != nil { if err != nil {
t.Errorf("%d: %v", i, err) t.Errorf("%d: %v", i, err)
@ -64,6 +65,28 @@ func TestTextAssignTo(t *testing.T) {
} }
} }
var buf []byte
bytesTests := []struct {
src pgtype.Text
dst *[]byte
expected []byte
}{
{src: pgtype.Text{String: "foo", Status: pgtype.Present}, dst: &buf, expected: []byte("foo")},
{src: pgtype.Text{Status: pgtype.Null}, dst: &buf, expected: nil},
}
for i, tt := range bytesTests {
err := tt.src.AssignTo(tt.dst)
if err != nil {
t.Errorf("%d: %v", i, err)
}
if bytes.Compare(*tt.dst, tt.expected) != 0 {
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, tt.dst)
}
}
pointerAllocTests := []struct { pointerAllocTests := []struct {
src pgtype.Text src pgtype.Text
dst interface{} dst interface{}

View File

@ -198,20 +198,7 @@ func (rows *Rows) Scan(dest ...interface{}) (err error) {
continue continue
} }
// Check for []byte first as we allow sidestepping the decoding process and retrieving the raw bytes if s, ok := d.(pgtype.BinaryDecoder); ok && vr.Type().FormatCode == BinaryFormatCode {
if b, ok := d.(*[]byte); ok {
// If it actually is a bytea then pass it through decodeBytea (so it can be decoded if it is in text format)
// Otherwise read the bytes directly regardless of what the actual type is.
if vr.Type().DataType == ByteaOid {
*b = decodeBytea(vr)
} else {
if vr.Len() != -1 {
*b = vr.ReadBytes(vr.Len())
} else {
*b = nil
}
}
} else if s, ok := d.(pgtype.BinaryDecoder); ok && vr.Type().FormatCode == BinaryFormatCode {
err = s.DecodeBinary(rows.conn.ConnInfo, vr.bytes()) err = s.DecodeBinary(rows.conn.ConnInfo, vr.bytes())
if err != nil { if err != nil {
rows.Fatal(scanArgError{col: i, err: err}) rows.Fatal(scanArgError{col: i, err: err})

View File

@ -684,7 +684,6 @@ func TestQueryRowCoreByteSlice(t *testing.T) {
}{ }{
{"select $1::text", "Jack", []byte("Jack")}, {"select $1::text", "Jack", []byte("Jack")},
{"select $1::text", []byte("Jack"), []byte("Jack")}, {"select $1::text", []byte("Jack"), []byte("Jack")},
{"select $1::int4", int32(239023409), []byte{14, 63, 53, 49}},
{"select $1::varchar", []byte("Jack"), []byte("Jack")}, {"select $1::varchar", []byte("Jack"), []byte("Jack")},
{"select $1::bytea", []byte{0, 15, 255, 17}, []byte{0, 15, 255, 17}}, {"select $1::bytea", []byte{0, 15, 255, 17}, []byte{0, 15, 255, 17}},
} }

2
v3.md
View File

@ -28,6 +28,8 @@ Reject scanning binary format values into a string (e.g. binary encoded timestam
Remove CopyTo Remove CopyTo
No longer can read raw bytes of any value into a []byte. Use pgtype.GenericBinary if this functionality is needed.
## TODO / Possible / Investigate ## TODO / Possible / Investigate
Organize errors better Organize errors better

View File

@ -217,21 +217,3 @@ func stripNamedType(val *reflect.Value) (interface{}, bool) {
return nil, false return nil, false
} }
func decodeBytea(vr *ValueReader) []byte {
if vr.Len() == -1 {
return nil
}
if vr.Type().DataType != ByteaOid {
vr.Fatal(ProtocolError(fmt.Sprintf("Cannot decode oid %v into []byte", vr.Type().DataType)))
return nil
}
if vr.Type().FormatCode != BinaryFormatCode {
vr.Fatal(ProtocolError(fmt.Sprintf("Unknown field description format code: %v", vr.Type().FormatCode)))
return nil
}
return vr.ReadBytes(vr.Len())
}