英文:
sqlx postgres scan method fail
问题
我正在尝试执行一个包含自定义geoPoint类型的Postgres查询,但是遇到了意外的EOF错误。你有什么想法,我做错了什么吗?
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"`
}
// 失败的函数
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方法返回字符串表示的geoPoint值
func (g *GeoPoint) String() string {
return fmt.Sprintf("(%v, %v)", g.Latitude, g.Longitude)
}
// Value方法根据.String()方法返回的值将geoPoint值存储到数据库中
func (g GeoPoint) Value() (driver.Value, error) {
return g.String(), nil
}
// Scan方法将数据库中的[]byte数组值转换为geoPoint值
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("无法执行geopoint转换")
}
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
}
以上是你提供的代码的翻译。
英文:
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
}
答案1
得分: 1
Scanner
和Valuer
接口的GeoPoint实现看起来有问题。它们应该是对称的,但在一个情况下,它将GeoPoint表示为一个任意长度的字符串,格式如下:
"<纬度, 经度>"
但在另一个方向上,它期望一个精确为16字节的表示(两个64位浮点数,按大端字节顺序)。这似乎不兼容。
你测试过Value()
的输出值是否可以被反馈到Scan()
中以获得相同的值吗?基本上,你应该能够执行以下操作:
p1 := GeoPoint{3.1415926, 2.71828}
bs, _ := p1.Value()
p2 := GeoPoint{}
p2.Scan(bs)
fmt.Printf("%#v\n", p2)
并且在p2
中看到与p1
相同的值。
例如,像这样的代码:
// 将geoPoint的值存储在数据库中
func (g *GeoPoint) Value() (driver.Value, error) {
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, g)
return buf.Bytes(), nil
}
// 将数据库中的[]byte数组值转换为geoPoint值
func (g *GeoPoint) Scan(src interface{}) error {
var source []byte
switch src.(type) {
case []byte:
source = src.([]byte)
default:
return errors.New("无法执行geopoint转换")
}
reader := bytes.NewReader(source)
return binary.Read(reader, binary.BigEndian, g)
}
应该满足这个属性。Scan()
和Value()
在概念上必须一致,否则它们将没有用处。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论