Add pgtype.Circle

Also rename Point.Vec2 to Point.P to conform to rest of geometric types.
wip-varbit
Jack Christensen 2017-04-04 20:39:48 -05:00
parent dc71bedebf
commit 5be6819a8c
6 changed files with 174 additions and 9 deletions

150
pgtype/circle.go Normal file
View File

@ -0,0 +1,150 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"io"
"math"
"strconv"
"strings"
"github.com/jackc/pgx/pgio"
)
type Circle struct {
P Vec2
R float64
Status Status
}
func (dst *Circle) Set(src interface{}) error {
return fmt.Errorf("cannot convert %v to Circle", src)
}
func (dst *Circle) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *Circle) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *Circle) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Circle{Status: Null}
return nil
}
if len(src) < 9 {
return fmt.Errorf("invalid length for Circle: %v", len(src))
}
str := string(src[2:])
end := strings.IndexByte(str, ',')
x, err := strconv.ParseFloat(str[:end], 64)
if err != nil {
return err
}
str = str[end+1:]
end = strings.IndexByte(str, ')')
y, err := strconv.ParseFloat(str[:end], 64)
if err != nil {
return err
}
str = str[end+2 : len(str)-1]
r, err := strconv.ParseFloat(str, 64)
if err != nil {
return err
}
*dst = Circle{P: Vec2{x, y}, R: r, Status: Present}
return nil
}
func (dst *Circle) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Circle{Status: Null}
return nil
}
if len(src) != 24 {
return fmt.Errorf("invalid length for Circle: %v", len(src))
}
x := binary.BigEndian.Uint64(src)
y := binary.BigEndian.Uint64(src[8:])
r := binary.BigEndian.Uint64(src[16:])
*dst = Circle{
P: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
R: math.Float64frombits(r),
Status: Present,
}
return nil
}
func (src *Circle) 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.P.X, src.P.Y, src.R))
return false, err
}
func (src *Circle) 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.P.X)); err != nil {
return false, err
}
if _, err := pgio.WriteUint64(w, math.Float64bits(src.P.Y)); err != nil {
return false, err
}
_, err := pgio.WriteUint64(w, math.Float64bits(src.R))
return false, err
}
// Scan implements the database/sql Scanner interface.
func (dst *Circle) Scan(src interface{}) error {
if src == nil {
*dst = Circle{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 *Circle) Value() (driver.Value, error) {
return encodeValueText(src)
}

15
pgtype/circle_test.go Normal file
View File

@ -0,0 +1,15 @@
package pgtype_test
import (
"testing"
"github.com/jackc/pgx/pgtype"
)
func TestCircleTranscode(t *testing.T) {
testSuccessfulTranscode(t, "circle", []interface{}{
&pgtype.Circle{P: pgtype.Vec2{1.234, 5.6789}, R: 3.5, Status: pgtype.Present},
&pgtype.Circle{P: pgtype.Vec2{-1.234, -5.6789}, R: 12.9, Status: pgtype.Present},
&pgtype.Circle{Status: pgtype.Null},
})
}

View File

@ -228,6 +228,7 @@ func init() {
"char": &QChar{},
"cid": &Cid{},
"cidr": &Cidr{},
"circle": &Circle{},
"date": &Date{},
"daterange": &Daterange{},
"decimal": &Decimal{},

View File

@ -18,7 +18,7 @@ type Vec2 struct {
}
type Point struct {
Vec2
P Vec2
Status Status
}
@ -66,7 +66,7 @@ func (dst *Point) DecodeText(ci *ConnInfo, src []byte) error {
return err
}
*dst = Point{Vec2: Vec2{x, y}, Status: Present}
*dst = Point{P: Vec2{x, y}, Status: Present}
return nil
}
@ -84,7 +84,7 @@ func (dst *Point) DecodeBinary(ci *ConnInfo, src []byte) error {
y := binary.BigEndian.Uint64(src[8:])
*dst = Point{
Vec2: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
P: Vec2{math.Float64frombits(x), math.Float64frombits(y)},
Status: Present,
}
return nil
@ -98,7 +98,7 @@ func (src *Point) EncodeText(ci *ConnInfo, w io.Writer) (bool, error) {
return false, errUndefined
}
_, err := io.WriteString(w, fmt.Sprintf(`(%f,%f)`, src.X, src.Y))
_, err := io.WriteString(w, fmt.Sprintf(`(%f,%f)`, src.P.X, src.P.Y))
return false, err
}
@ -110,12 +110,12 @@ func (src *Point) EncodeBinary(ci *ConnInfo, w io.Writer) (bool, error) {
return false, errUndefined
}
_, err := pgio.WriteUint64(w, math.Float64bits(src.X))
_, err := pgio.WriteUint64(w, math.Float64bits(src.P.X))
if err != nil {
return false, err
}
_, err = pgio.WriteUint64(w, math.Float64bits(src.Y))
_, err = pgio.WriteUint64(w, math.Float64bits(src.P.Y))
return false, err
}

View File

@ -8,8 +8,8 @@ import (
func TestPointTranscode(t *testing.T) {
testSuccessfulTranscode(t, "point", []interface{}{
&pgtype.Point{Vec2: pgtype.Vec2{1.234, 5.6789}, Status: pgtype.Present},
&pgtype.Point{Vec2: pgtype.Vec2{-1.234, -5.6789}, Status: pgtype.Present},
&pgtype.Point{P: pgtype.Vec2{1.234, 5.6789}, Status: pgtype.Present},
&pgtype.Point{P: pgtype.Vec2{-1.234, -5.6789}, Status: pgtype.Present},
&pgtype.Point{Status: pgtype.Null},
})
}

1
v3.md
View File

@ -68,6 +68,5 @@ something like:
select array[1,2,3], array[4,5,6,7]
pgtype TODO:
circle
macaddr
varbit