sqlx postgres扫描方法失败

huangapple go评论91阅读模式
英文:

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

ScannerValuer接口的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:

&quot;(&lt;latitude&gt;, &lt;longitude&gt;)&quot;

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(&quot;%#v\n&quot;, 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(&amp;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(&quot;Unable to perform geopoint conversion&quot;)
}
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.

huangapple
  • 本文由 发表于 2015年4月27日 02:55:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/29882044.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定