From 5394aa9a2b7a0a1a4f6e340f68592096924425d9 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Tue, 4 Apr 2017 08:04:40 -0500 Subject: [PATCH] Add pgtype.Line --- pgtype/line.go | 148 ++++++++++++++++++++++++++++++++++++++++++++ pgtype/line_test.go | 21 +++++++ pgtype/pgtype.go | 1 + v3.md | 1 - 4 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 pgtype/line.go create mode 100644 pgtype/line_test.go diff --git a/pgtype/line.go b/pgtype/line.go new file mode 100644 index 00000000..08a74e84 --- /dev/null +++ b/pgtype/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/pgtype/line_test.go b/pgtype/line_test.go new file mode 100644 index 00000000..6d3b02e1 --- /dev/null +++ b/pgtype/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/pgtype.go b/pgtype/pgtype.go index b29bc90c..c92dfccf 100644 --- a/pgtype/pgtype.go +++ b/pgtype/pgtype.go @@ -242,6 +242,7 @@ func init() { "int8range": &Int8range{}, "json": &Json{}, "jsonb": &Jsonb{}, + "line": &Line{}, "name": &Name{}, "numeric": &Numeric{}, "numrange": &Numrange{}, diff --git a/v3.md b/v3.md index 1930508f..38cf18cf 100644 --- a/v3.md +++ b/v3.md @@ -68,7 +68,6 @@ something like: select array[1,2,3], array[4,5,6,7] pgtype TODO: -line lseg path path