Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use sqlx.types in modl models for json postgres type? #32

Open
rmrio opened this issue Jan 12, 2015 · 3 comments
Open

How to use sqlx.types in modl models for json postgres type? #32

rmrio opened this issue Jan 12, 2015 · 3 comments

Comments

@rmrio
Copy link

rmrio commented Jan 12, 2015

I want to use postgresql json type for keep golang maps.

type Card struct {
    Number string
    Name   string
    CVV    int
}

type Person struct {
    Id    int
    Name  string
    Cards map[int]Card    // want to save this filed in postgres json
}

I know that i need to implement some sql.Scanner and driver.Valuer interfaces, but they already implemented for type JsonText in sqlx.types package. The JsonText type looks like what i need.

So, give me a right way please.

@rmrio
Copy link
Author

rmrio commented Jan 13, 2015

jmoiron/sqlx#117 PR were helpful.
Also map[int]Card is not supported by json.Marshal, need to use map[string]Card instead.

The code, may be helpful for someone:

type Card struct {
    Number string
    Name   string
    CVV    int
}

type JsonArray map[string]Card

type Person struct {
    Id    int
    Name  string
    Cards JsonArray
}

func (p JsonArray) Value() (driver.Value, error) {
    if len(p) == 0 {
        return nil, nil
    }
    return json.Marshal(p)
}

func (p JsonArray) Scan(src interface{}) error {
    v := reflect.ValueOf(src)
    if !v.IsValid() || v.IsNil() {
        return nil
    }
    if data, ok := src.([]byte); ok {
        return json.Unmarshal(data, &p)
    }
    return fmt.Errorf("Could not not decode type %T -> %T", src, p)
}

func main() {

    db, err := sql.Open("postgres", "user=modl_user password=qwerty dbname=modl_test sslmode=disable")
    if err != nil {
        fmt.Println(err)
    }

    dbmap := modl.NewDbMap(db, modl.PostgresDialect{})
    dbmap.TraceOn("", log.New(os.Stdout, "modltest: ", log.Lmicroseconds))
    table := dbmap.AddTableWithName(Person{}, "persons").SetKeys(true, "Id")
    column := table.ColMap("cards")
    column.SetSqlType("json")

    err = dbmap.CreateTablesIfNotExists()
    //defer dbmap.DropTables()
    if err != nil {
        fmt.Println(err)
    }
    person := &Person{}
    person.Name = "Alex Smith"
    person.Cards = make(JsonArray)
    visa := Card{"4432-3433-2311-2343", "Alex Smith", 235}
    mc := Card{"5535-5443-2320-0009", "Jina Smith", 431}
    person.Cards["0"] = visa
    person.Cards["1"] = mc
    err = dbmap.Insert(person)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("Person id", person.Id)
}

@rmrio rmrio closed this as completed Jan 13, 2015
@rmrio
Copy link
Author

rmrio commented Jan 15, 2015

Ok, saving to db working good, selecting one field working good too.
But i get an error when I select all rows from a table.
The error is non-struct dest type struct with >1 columns (3)
How i can fix that? Should I implement another interface?
The full example code is here

type File struct {
    Stat
    Id   int64
    Name string
}

type Stat struct {
    Bytes int64 `json:"b"`
}

func (p Stat) Value() (driver.Value, error) {
    return json.Marshal(p)
}

func (p Stat) Scan(src interface{}) error {
    v := reflect.ValueOf(src)
    if !v.IsValid() || v.IsNil() {
        return nil
    }
    if data, ok := src.([]byte); ok {
        return json.Unmarshal(data, &p)
    }
    return fmt.Errorf("Could not not decode type %T -> %T", src, p)
}

func main() {
    logger := log.New(os.Stdout, "", log.Lshortfile)
    db, err := sql.Open("postgres", "user=modl_user password=qwerty dbname=modl_test sslmode=disable")
    if err != nil {
        logger.Fatalln(err)
    }
    dbmap := modl.NewDbMap(db, modl.PostgresDialect{})
    dbmap.TraceOn("", log.New(os.Stdout, "modltest: ", log.Lmicroseconds))

    dbmap.AddTableWithName(File{}, "files").SetKeys(true, "Id")

    err = dbmap.CreateTablesIfNotExists()
    if err != nil {
        logger.Fatalln(err)
    }
    defer dbmap.DropTables()

    // populate
    for i := 0; i < 10; i++ {
        f := File{}
        f.Name = fmt.Sprintf("abcfile%v", i)
        err = dbmap.Insert(&f)
        if err != nil {
            logger.Println(err)
        }
    }

    results := []File{}
    err = dbmap.Select(&results, "SELECT * FROM files")
    if err != nil {
        // err: non-struct dest type struct with >1 columns (3)
        logger.Fatalln(err)
    }
}

@rmrio rmrio reopened this Jan 15, 2015
@alexflint
Copy link

It may be because your Scan function takes a non-pointer receiver. Your call to Unmarshal then unmarshals into a copy of Stat that is local to the Scan function. Try func (p *Stat) Scan(src interface{}) error { ... }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants