From 2fc89c69e9d1f205021721d63806edde5988918a Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Tue, 4 Apr 2017 08:04:40 -0500 Subject: [PATCH] Add pgtype.Line --- line.go | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ line_test.go | 21 ++++++++ pgtype.go | 1 + 3 files changed, 170 insertions(+) create mode 100644 line.go create mode 100644 line_test.go diff --git a/line.go b/line.go new file mode 100644 index 00000000..08a74e84 --- /dev/null +++ b/line.go @@ -0,0 +1,148 @@ +package pgtype + +import ( + "database/sql/driver" + "encoding/binary" + "fmt" + "io" + "math" + "strconv" + "strings" + + "github.com/jackc/pgx/pgio" +) + +type Line struct { + A, B, C float64 + Status Status +} + +func (dst *Line) Set(src interface{}) error { + return fmt.Errorf("cannot convert %v to Line", src) +} + +func (dst *Line) Get() interface{} { + switch dst.Status { + case Present: + return dst + case Null: + return nil + default: + return dst.Status + } +} + +func (src *Line) AssignTo(dst interface{}) error { + return fmt.Errorf("cannot assign %v to %T", src, dst) +} + +func (dst *Line) DecodeText(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Line{Status: Null} + return nil + } + + if len(src) < 7 { + return fmt.Errorf("invalid length for Line: %v", len(src)) + } + + parts := strings.SplitN(string(src[1:len(src)-1]), ",", 3) + if len(parts) < 3 { + return fmt.Errorf("invalid format for line") + } + + a, err := strconv.ParseFloat(parts[0], 64) + if err != nil { + return err + } + + b, err := strconv.ParseFloat(parts[1], 64) + if err != nil { + return err + } + + c, err := strconv.ParseFloat(parts[2], 64) + if err != nil { + return err + } + + *dst = Line{A: a, B: b, C: c, Status: Present} + return nil +} + +func (dst *Line) DecodeBinary(ci *ConnInfo, src []byte) error { + if src == nil { + *dst = Line{Status: Null} + return nil + } + + if len(src) != 24 { + return fmt.Errorf("invalid length for Line: %v", len(src)) + } + + a := binary.BigEndian.Uint64(src) + b := binary.BigEndian.Uint64(src[8:]) + c := binary.BigEndian.Uint64(src[16:]) + + *dst = Line{ + A: math.Float64frombits(a), + B: math.Float64frombits(b), + C: math.Float64frombits(c), + Status: Present, + } + return nil +} + +func (src *Line) EncodeText(ci *ConnInfo, w io.Writer) (bool, error) { + switch src.Status { + case Null: + return true, nil + case Undefined: + return false, errUndefined + } + + _, err := io.WriteString(w, fmt.Sprintf(`{%f,%f,%f}`, src.A, src.B, src.C)) + return false, err +} + +func (src *Line) EncodeBinary(ci *ConnInfo, w io.Writer) (bool, error) { + switch src.Status { + case Null: + return true, nil + case Undefined: + return false, errUndefined + } + + if _, err := pgio.WriteUint64(w, math.Float64bits(src.A)); err != nil { + return false, err + } + + if _, err := pgio.WriteUint64(w, math.Float64bits(src.B)); err != nil { + return false, err + } + + _, err := pgio.WriteUint64(w, math.Float64bits(src.C)) + return false, err +} + +// Scan implements the database/sql Scanner interface. +func (dst *Line) Scan(src interface{}) error { + if src == nil { + *dst = Line{Status: Null} + return nil + } + + switch src := src.(type) { + case string: + return dst.DecodeText(nil, []byte(src)) + case []byte: + return dst.DecodeText(nil, src) + } + + return fmt.Errorf("cannot scan %T", src) +} + +// Value implements the database/sql/driver Valuer interface. +func (src *Line) Value() (driver.Value, error) { + return encodeValueText(src) +} diff --git a/line_test.go b/line_test.go new file mode 100644 index 00000000..6d3b02e1 --- /dev/null +++ b/line_test.go @@ -0,0 +1,21 @@ +package pgtype_test + +import ( + "testing" + + "github.com/jackc/pgx/pgtype" +) + +func TestLineTranscode(t *testing.T) { + testSuccessfulTranscode(t, "line", []interface{}{ + &pgtype.Line{ + A: 1.23, B: 4.56, C: 7.89, + Status: pgtype.Present, + }, + &pgtype.Line{ + A: -1.23, B: -4.56, C: -7.89, + Status: pgtype.Present, + }, + &pgtype.Line{Status: pgtype.Null}, + }) +} diff --git a/pgtype.go b/pgtype.go index b29bc90c..c92dfccf 100644 --- a/pgtype.go +++ b/pgtype.go @@ -242,6 +242,7 @@ func init() { "int8range": &Int8range{}, "json": &Json{}, "jsonb": &Jsonb{}, + "line": &Line{}, "name": &Name{}, "numeric": &Numeric{}, "numrange": &Numrange{},