数据库/SQL中的json.RawMessage被覆盖了。

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

json.RawMessage from database/sql json column getting overwritten

问题

使用嵌入的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))
	}
}

输出:

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

所以文章最终指向相同的JSON。

我做错了什么吗?

更新

编辑为可运行的示例。我正在使用Postgres和lib/pq

英文:

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.

答案1

得分: 5

我遇到了同样的问题,经过长时间的研究后,我阅读了关于Scan的文档,文档中写道:

> 如果一个参数的类型是*[]byte,Scan会将相应的数据复制到该参数中。复制的数据由调用者拥有,可以进行修改并持有时间不确定。如果想避免复制,可以使用类型为*RawBytes的参数;请参阅RawBytes的文档以了解其使用限制。

我认为的问题是,如果你使用json.RawMessage,那么Scan不会将其视为[]byte,并且不会将值复制到其中。因此,在下一次循环中,你会得到一个内部切片,Scan会覆盖它。

将你的Scan更改为将json.RawMessage强制转换为[]byte,这样Scan就会将值复制到其中。

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

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),
)

答案2

得分: 0

如果有帮助的话:

我使用了masebase的答案,将我的结构体中的json.RawMessage属性插入到一个具有jsonb列类型的PostgreSQL数据库列中。

你只需要在插入绑定方法中进行类型转换:([]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).

huangapple
  • 本文由 发表于 2014年6月6日 04:15:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/24069459.html
匿名

发表评论

匿名网友

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

确定