From 368295d3ee4d8f08f30d5f8cb1841461cd4f14a6 Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Sun, 12 Apr 2020 18:40:52 +0100 Subject: [PATCH] Create ROW helper for adhoc decoding of records --- composite_test.go | 13 +++++++++++++ convert.go | 28 ++++++++++++++++++++++++++++ pgtype.go | 9 +++++++++ 3 files changed, 50 insertions(+) diff --git a/composite_test.go b/composite_test.go index d51cb579..ffa7d479 100644 --- a/composite_test.go +++ b/composite_test.go @@ -71,7 +71,20 @@ create type mytype as ( } fmt.Printf("Second row: %v\n", result) + + // Adhoc rows can be decoded inplace without boilerplate (works with composite types too) + var isNull bool + var a int + var b *string + + if err = conn.QueryRow(context.Background(), "select (2, 'bar')::mytype", pgx.QueryResultFormats{pgx.BinaryFormatCode}).Scan(pgtype.ROW(&isNull, &a, &b)); err != nil { + panic(err) + } + + fmt.Printf("Adhoc: isNull=%v a=%d b=%s", isNull, a, *b) + // Output: // First row: a=1 b=foo // Second row: + // Adhoc: isNull=false a=2 b=bar } diff --git a/convert.go b/convert.go index a0c38c5b..8157358b 100644 --- a/convert.go +++ b/convert.go @@ -471,6 +471,34 @@ func ScanRowValue(ci *ConnInfo, src []byte, dst ...Value) error { return nil } +// ROW allows deconstructing row values (records and composite types) into +// fields directly without creating your own type and implementing decoder interfaces +func ROW(isNull *bool, fields ...interface{}) BinaryDecoderFunc { + return func(ci *ConnInfo, src []byte) error { + var record Record + if err := record.DecodeBinary(ci, src); err != nil { + return err + } + + if record.Status == Null { + *isNull = true + return nil + } + + if len(record.Fields) != len(fields) { + return errors.Errorf("can't scan row value, number of fields don't match: row fields count=%d desired fields count=%d", len(record.Fields), len(fields)) + } + + for i, f := range record.Fields { + if err := f.AssignTo(fields[i]); err != nil { + return err + } + } + + return nil + } +} + func init() { kindTypes = map[reflect.Kind]reflect.Type{ reflect.Bool: reflect.TypeOf(false), diff --git a/pgtype.go b/pgtype.go index 914e02d2..1749c8c2 100644 --- a/pgtype.go +++ b/pgtype.go @@ -158,6 +158,15 @@ type TextEncoder interface { EncodeText(ci *ConnInfo, buf []byte) (newBuf []byte, err error) } +//The BinaryDecoderFunc type is an adapter to allow the use of ordinary functions as BinaryDecoder types. +// If f is a function with the appropriate signature, BinaryDecoderFunc(f) is a BinaryDecoder that calls f. +type BinaryDecoderFunc func(ci *ConnInfo, src []byte) error + +// DecodeBinary calls f(ci, src) +func (f BinaryDecoderFunc) DecodeBinary(ci *ConnInfo, src []byte) error { + return f(ci, src) +} + var errUndefined = errors.New("cannot encode status undefined") var errBadStatus = errors.New("invalid status")