sqlx postgres扫描方法失败

时间:2015-04-26 18:55:57

标签: postgresql go

我正在尝试执行包含自定义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"`
}

// 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 个答案:

答案 0 :(得分:1)

ScannerValuer接口的GeoPoint实现看起来有问题。它们应该是对称的,但在一种情况下,它将GeoPoint表示为形式的任意变量长度字符串:

"(<latitude>, <longitude>)"

但是在另一个方向上,需要一个恰好16个字节的表示(以big-endian字节顺序的两个64位浮点数)。这似乎不兼容。

您是否测试过Value()的输出值可以反馈到Scan()以获得相同的值?基本上,你应该能够做到:

p1 := GeoPoint{3.1415926, 2.71828}
bs, _ := p1.Value()
p2 := GeoPoint{}
p2.Scan(bs)
fmt.Printf("%#v\n", p2) 

并在p2中看到与p1中相同的值。

例如:

// 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)
}

应该满足这个属性。 Scan()Value()必须在概念上保持一致:否则,它们将无用。