Reputation: 4351
I am trying to perform a postgres query that contains a custom geoPoint type but get a Unexpected EOF error. Any ideas to what I am doing wrong?
type Account struct {
Id uint `json:"id" db:"id"`
RegistrationId string `json:"registration_id" db:"registration_id"`
PhoneNumber string `json:"phone_number" db:"phone_number"`
AuthToken string `json:"auth_token" db:"auth_token"`
// Role string `json:"-" db:"role"`
CreatedAt time.Time `json:"-" db:"created_at"`
ActivatedAt time.Time `json:"-" db:"activated_at"`
Location GeoPoint `json:"location" db:"location"`
}
// THE FAILING FUNCTION
func FindAccountByToken(db *sqlx.DB, token string) (Account, error) {
var account Account
log.Println("FindAcountByToken", token)
err := db.Get(&account, "select * from accounts where auth_token = $1", token)
return account, err
}
type GeoPoint struct {
Latitude float64 `json:"latitude" db:"latitude"`
Longitude float64 `json:"longitude" db:"longitude"`
}
// String value
func (g *GeoPoint) String() string {
return fmt.Sprintf("(%v, %v)", g.Latitude, g.Longitude)
}
// Value of the geoPoint to be stored in the db based on the .String() method
func (g GeoPoint) Value() (driver.Value, error) {
return g.String(), nil
}
// Scan converts the db []byte array value to the geoPoint value
func (g *GeoPoint) Scan(src interface{}) error {
var source []byte
var gp GeoPoint
switch src.(type) {
case []byte:
source = src.([]byte)
default:
return errors.New("Unable to perform geopoint conversion")
}
log.Println("bytes -> ", source)
reader := bytes.NewReader(source)
if err := binary.Read(reader, binary.BigEndian, &gp); err != nil {
log.Println("BinaryRead Error", err)
return err
}
*g = gp
return nil
}
Upvotes: 1
Views: 643
Reputation: 12003
The GeoPoint implementation of Scanner
and Valuer
interfaces looks questionable. They should be symmetric, but in one case, it represents the GeoPoint as a arbitrary-variable-length string of the form:
"(<latitude>, <longitude>)"
but in the other direction, expects a representation of exactly 16 bytes (two 64-bit floats in big-endian byte order). This does not seem compatible.
Have you tested that the output value of Value()
can be fed back into Scan()
to get the same value? Essentially, you should be able to do:
p1 := GeoPoint{3.1415926, 2.71828}
bs, _ := p1.Value()
p2 := GeoPoint{}
p2.Scan(bs)
fmt.Printf("%#v\n", p2)
and see the same value in p2
as in p1
.
For example, something like:
// Value of the geoPoint to be stored in the db
func (g *GeoPoint) Value() (driver.Value, error) {
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, g)
return buf.Bytes(), nil
}
// Scan converts the db []byte array value to the geoPoint value
func (g *GeoPoint) Scan(src interface{}) error {
var source []byte
switch src.(type) {
case []byte:
source = src.([]byte)
default:
return errors.New("Unable to perform geopoint conversion")
}
reader := bytes.NewReader(source)
return binary.Read(reader, binary.BigEndian, g)
}
should satisfy this property. Scan()
and Value()
have to be conceptually consistent: otherwise, they wouldn't be useful.
Upvotes: 1