来自数据库/ SQL JSON列的json.RawMessage被覆盖

来自数据库/ SQL JSON列的json.RawMessage被覆盖

问题描述:

Getting strange behaviour with a struct with embedded json.

package main

import (
    "database/sql"
    "encoding/json"
    "fmt"

    _ "github.com/lib/pq"
)

type Article struct {
    Id  int
    Doc *json.RawMessage
}

func main() {
    db, err := sql.Open("postgres", "postgres://localhost/json_test?sslmode=disable")
    if err != nil {
        panic(err)
    }

    _, err = db.Query(`create table if not exists articles (id serial primary key, doc json)`)
    if err != nil {
        panic(err)
    }
    _, err = db.Query(`truncate articles`)
    if err != nil {
        panic(err)
    }
    docs := []string{
        `{"type":"event1"}`,
        `{"type":"event2"}`,
    }
    for _, doc := range docs {
        _, err = db.Query(`insert into articles ("doc") values ($1)`, doc)
        if err != nil {
            panic(err)
        }
    }

    rows, err := db.Query(`select id, doc from articles`)
    if err != nil {
        panic(err)
    }

    articles := make([]Article, 0)

    for rows.Next() {
        var a Article
        err := rows.Scan(
            &a.Id,
            &a.Doc,
        )
        if err != nil {
            panic(err)
        }
        articles = append(articles, a)
        fmt.Println("scan", string(*a.Doc), len(*a.Doc))
    }

    fmt.Println()

    for _, a := range articles {
        fmt.Println("loop", string(*a.Doc), len(*a.Doc))
    }
}

Output:

scan {"type":"event1"} 17
scan {"type":"event2"} 17

loop {"type":"event2"} 17
loop {"type":"event2"} 17

So the articles end up pointing to the same json.

Am I doing something wrong?

UPDATE

Edited to a runnable example. I'm using Postgres and lib/pq.

I ran into this same issue and after looking at if for a long time I read the doc on Scan and it says

If an argument has type *[]byte, Scan saves in that argument a copy of the corresponding data. The copy is owned by the caller and can be modified and held indefinitely. The copy can be avoided by using an argument of type *RawBytes instead; see the documentation for RawBytes for restrictions on its use.

What I think is happening if you use *json.RawMessage then Scan does not see it as a *[]byte and does not copy into it. So you get in internal slice on the next loop Scan overwrites.

Change your Scan to cast the *json.RawMessage to a *[]byte so Scan will copy the values to it.

    err := rows.Scan(
        &a.Id,
        (*[]byte)(a.Doc),
    )

In case that helps anyone :

I used masebase anwser to INSERT a json.RawMessage property of my struct in a postgresql db column having jsonb column type.

All you need to do is cast : ([]byte)(a.Doc) in the insert binding method (without the * in my case).