mirror of https://github.com/jackc/pgx.git
Simplify []byte scanning
parent
015108be9a
commit
92cff1d961
5
doc.go
5
doc.go
|
@ -169,10 +169,7 @@ and database/sql/driver.Valuer interfaces.
|
|||
Raw Bytes Mapping
|
||||
|
||||
[]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
|
||||
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.
|
||||
to PostgreSQL.
|
||||
|
||||
Transactions
|
||||
|
||||
|
|
|
@ -49,6 +49,16 @@ func (src *Text) AssignTo(dst interface{}) error {
|
|||
return fmt.Errorf("cannot assign %v to %T", src, dst)
|
||||
}
|
||||
*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:
|
||||
if v := reflect.ValueOf(dst); v.Kind() == reflect.Ptr {
|
||||
el := v.Elem()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package pgtype_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -44,7 +45,7 @@ func TestTextAssignTo(t *testing.T) {
|
|||
var s string
|
||||
var ps *string
|
||||
|
||||
simpleTests := []struct {
|
||||
stringTests := []struct {
|
||||
src pgtype.Text
|
||||
dst interface{}
|
||||
expected interface{}
|
||||
|
@ -53,7 +54,7 @@ func TestTextAssignTo(t *testing.T) {
|
|||
{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)
|
||||
if err != nil {
|
||||
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 {
|
||||
src pgtype.Text
|
||||
dst interface{}
|
||||
|
|
15
query.go
15
query.go
|
@ -198,20 +198,7 @@ func (rows *Rows) Scan(dest ...interface{}) (err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
// Check for []byte first as we allow sidestepping the decoding process and retrieving the raw bytes
|
||||
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 {
|
||||
if s, ok := d.(pgtype.BinaryDecoder); ok && vr.Type().FormatCode == BinaryFormatCode {
|
||||
err = s.DecodeBinary(rows.conn.ConnInfo, vr.bytes())
|
||||
if err != nil {
|
||||
rows.Fatal(scanArgError{col: i, err: err})
|
||||
|
|
|
@ -684,7 +684,6 @@ func TestQueryRowCoreByteSlice(t *testing.T) {
|
|||
}{
|
||||
{"select $1::text", "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::bytea", []byte{0, 15, 255, 17}, []byte{0, 15, 255, 17}},
|
||||
}
|
||||
|
|
2
v3.md
2
v3.md
|
@ -28,6 +28,8 @@ Reject scanning binary format values into a string (e.g. binary encoded timestam
|
|||
|
||||
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
|
||||
|
||||
Organize errors better
|
||||
|
|
18
values.go
18
values.go
|
@ -217,21 +217,3 @@ func stripNamedType(val *reflect.Value) (interface{}, bool) {
|
|||
|
||||
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())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue