2

I'm trying to store a Point variable in an SQL database using GORM on a go server, I tried looking everywhere but I haven't seen an answer that satisfies me yet

Eddi3
  • 59
  • 1
  • 8
  • What is a Point value? A coordinate set, like `type Point struct{ X, Y int }`? Also: why? What have you tried? Why was the other solutions not satisfying? You need to add more detail to your problem description. – Brian Stengaard - Podio Jun 17 '16 at 20:18
  • In postgres SQL I can make a column that holds a Type Point, it should be an X and Y yes but it will store lat and lng so it might not be an int type unless that's the only type that can be stored in a point in postgres sql. I looked at similar posts about this on stack overflow: http://stackoverflow.com/questions/1727137/sql-query-for-performing-radius-search-based-on-latitude-longitude I took a look at the 2nd answer that names making a spatial column using a point – Eddi3 Jun 17 '16 at 20:22
  • Basically I want to do what was mentioned in the 2nd post using a GORM struct as mentioned in this site: http://jinzhu.me/gorm/models.html – Eddi3 Jun 17 '16 at 20:24

1 Answers1

3

For any type that's not supported by the standard library's database/sql package, nor by a 3rd party package that depends on database/sql, like gorm does, you can implement the Valuer and Scanner interfaces to add custom support for the type.

To be able to correctly implement those two interfaces you need to first find out what the target database expects as input and what it returns as output for that type. In the case of PostgreSQL and the Point type the syntax is (x,y).

So what you can do is the following:

  1. Declare the type.
type Point struct {
    X, Y float64
}
  1. Implement the Valuer interface.
func (p Point) Value() (driver.Value, error) {
    out := []byte{'('}
    out = strconv.AppendFloat(out, p.X, 'f', -1, 64)
    out = append(out, ',')
    out = strconv.AppendFloat(out, p.Y, 'f', -1, 64)
    out = append(out, ')')
    return out, nil
}
  1. Implement the Scanner interface.
func (p *Point) Scan(src interface{}) (err error) {
    var data []byte
    switch src := src.(type) {
    case []byte:
        data = src
    case string:
        data = []byte(src)
    case nil:
        return nil
    default:
        return errors.New("(*Point).Scan: unsupported data type")
    }

    if len(data) == 0 {
        return nil
    }

    data = data[1 : len(data)-1] // drop the surrounding parentheses
    for i := 0; i < len(data); i++ {
        if data[i] == ',' {
            if p.X, err = strconv.ParseFloat(string(data[:i]), 64); err != nil {
                return err
            }
            if p.Y, err = strconv.ParseFloat(string(data[i+1:]), 64); err != nil {
                return err
            }
            break
        }
    }
    return nil
}
mkopriva
  • 35,176
  • 4
  • 57
  • 71