From 18c64dceeee5aa96300d71c5260ba08bbdef9643 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 2 May 2020 20:18:51 -0500 Subject: [PATCH] ConnInfo Scan optimizes common native types This comes at a small expense to scanning into a type that implements TextDecoder or BinaryDecoder but I think it is a good trade. Before: BenchmarkConnInfoScanInt4IntoBinaryDecoder-16 88181061 12.4 ns/op 0 B/op 0 allocs/op BenchmarkConnInfoScanInt4IntoGoInt32-16 30402768 36.8 ns/op 0 B/op 0 allocs/op After: BenchmarkConnInfoScanInt4IntoBinaryDecoder-16 79859755 14.6 ns/op 0 B/op 0 allocs/op BenchmarkConnInfoScanInt4IntoGoInt32-16 38969991 30.0 ns/op 0 B/op 0 allocs/op --- pgtype.go | 61 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/pgtype.go b/pgtype.go index f7dc1379..f6c354ef 100644 --- a/pgtype.go +++ b/pgtype.go @@ -3,6 +3,7 @@ package pgtype import ( "database/sql" "reflect" + "time" errors "golang.org/x/xerrors" ) @@ -337,17 +338,39 @@ func (ci *ConnInfo) DeepCopy() *ConnInfo { } func (ci *ConnInfo) Scan(oid uint32, formatCode int16, buf []byte, dest interface{}) error { - switch formatCode { - case BinaryFormatCode: - if dest, ok := dest.(BinaryDecoder); ok { - return dest.DecodeBinary(ci, buf) + isFastType := false + switch dest.(type) { + case *int16: + isFastType = true + case *int32: + isFastType = true + case *int64: + isFastType = true + case *float32: + isFastType = true + case *float64: + isFastType = true + case *string: + isFastType = true + case *time.Time: + isFastType = true + case *[]byte: + isFastType = true + } + + if !isFastType { + switch formatCode { + case BinaryFormatCode: + if dest, ok := dest.(BinaryDecoder); ok { + return dest.DecodeBinary(ci, buf) + } + case TextFormatCode: + if dest, ok := dest.(TextDecoder); ok { + return dest.DecodeText(ci, buf) + } + default: + return errors.Errorf("unknown format code: %v", formatCode) } - case TextFormatCode: - if dest, ok := dest.(TextDecoder); ok { - return dest.DecodeText(ci, buf) - } - default: - return errors.Errorf("unknown format code: %v", formatCode) } if dt, ok := ci.DataTypeForOID(oid); ok { @@ -371,17 +394,21 @@ func (ci *ConnInfo) Scan(oid uint32, formatCode int16, buf []byte, dest interfac } else { return errors.Errorf("%T is not a pgtype.BinaryDecoder", value) } + default: + return errors.Errorf("unknown format code: %v", formatCode) } - if scanner, ok := dest.(sql.Scanner); ok { - sqlSrc, err := DatabaseSQLValue(ci, value) - if err != nil { - return err + if !isFastType { + if scanner, ok := dest.(sql.Scanner); ok { + sqlSrc, err := DatabaseSQLValue(ci, value) + if err != nil { + return err + } + return scanner.Scan(sqlSrc) } - return scanner.Scan(sqlSrc) - } else { - return value.AssignTo(dest) } + + return value.AssignTo(dest) } // We might be given a pointer to something that implements the decoder interface(s),